diff --git a/cmd/godex/gc.go b/cmd/godex/gc.go index e252900985..66b0a0e617 100644 --- a/cmd/godex/gc.go +++ b/cmd/godex/gc.go @@ -8,10 +8,8 @@ package main -import ( - "golang.org/x/tools/go/gcimporter" -) +import "go/importer" func init() { - register("gc", gcimporter.Import) + register("gc", importer.For("gc", nil)) } diff --git a/cmd/godex/gc14.go b/cmd/godex/gc14.go deleted file mode 100644 index 40c433d2ff..0000000000 --- a/cmd/godex/gc14.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// This file implements access to gc-generated export data. - -package main - -import ( - "golang.org/x/tools/go/gcimporter" -) - -func init() { - register("gc", gcimporter.Import) -} diff --git a/cmd/godex/gccgo.go b/cmd/godex/gccgo.go index 2f2168dab9..785441cd32 100644 --- a/cmd/godex/gccgo.go +++ b/cmd/godex/gccgo.go @@ -9,34 +9,28 @@ package main import ( - "golang.org/x/tools/go/gccgoimporter" - "golang.org/x/tools/go/types" -) - -var ( - initmap = make(map[*types.Package]gccgoimporter.InitData) + "go/importer" + "go/types" ) func init() { - incpaths := []string{"/"} - - // importer for default gccgo - var inst gccgoimporter.GccgoInstallation - inst.InitFromDriver("gccgo") - register("gccgo", inst.GetImporter(incpaths, initmap)) + register("gccgo", importer.For("gccgo", nil)) } // Print the extra gccgo compiler data for this package, if it exists. func (p *printer) printGccgoExtra(pkg *types.Package) { - if initdata, ok := initmap[pkg]; ok { - p.printf("/*\npriority %d\n", initdata.Priority) + // Disabled for now. + // TODO(gri) address this at some point. - p.printDecl("init", len(initdata.Inits), func() { - for _, init := range initdata.Inits { - p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority) - } - }) + // if initdata, ok := initmap[pkg]; ok { + // p.printf("/*\npriority %d\n", initdata.Priority) - p.print("*/\n") - } + // p.printDecl("init", len(initdata.Inits), func() { + // for _, init := range initdata.Inits { + // p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority) + // } + // }) + + // p.print("*/\n") + // } } diff --git a/cmd/godex/gccgo14.go b/cmd/godex/gccgo14.go deleted file mode 100644 index 131752783f..0000000000 --- a/cmd/godex/gccgo14.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// This file implements access to gccgo-generated export data. - -package main - -import ( - "golang.org/x/tools/go/gccgoimporter" - "golang.org/x/tools/go/types" -) - -var ( - initmap = make(map[*types.Package]gccgoimporter.InitData) -) - -func init() { - incpaths := []string{"/"} - - // importer for default gccgo - var inst gccgoimporter.GccgoInstallation - inst.InitFromDriver("gccgo") - register("gccgo", inst.GetImporter(incpaths, initmap)) -} - -// Print the extra gccgo compiler data for this package, if it exists. -func (p *printer) printGccgoExtra(pkg *types.Package) { - if initdata, ok := initmap[pkg]; ok { - p.printf("/*\npriority %d\n", initdata.Priority) - - p.printDecl("init", len(initdata.Inits), func() { - for _, init := range initdata.Inits { - p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority) - } - }) - - p.print("*/\n") - } -} diff --git a/cmd/godex/godex.go b/cmd/godex/godex.go index 3cdca2ec49..5d40d878f3 100644 --- a/cmd/godex/godex.go +++ b/cmd/godex/godex.go @@ -11,12 +11,11 @@ import ( "flag" "fmt" "go/build" + "go/types" "io/ioutil" "os" "path/filepath" "strings" - - "golang.org/x/tools/go/types" ) var ( @@ -31,9 +30,6 @@ var ( importFailed = errors.New("import failed") ) -// map of imported packages -var packages = make(map[string]*types.Package) - func usage() { fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}") flag.PrintDefaults() @@ -53,7 +49,7 @@ func main() { report("no package name, path, or file provided") } - imp := tryImports + var imp types.Importer = new(tryImporters) if *source != "" { imp = lookup(*source) if imp == nil { @@ -71,7 +67,7 @@ func main() { go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path)) // import package - pkg, err := tryPrefixes(packages, prefixes, path, imp) + pkg, err := tryPrefixes(prefixes, path, imp) if err != nil { logf("\t=> ignoring %q: %s\n", path, err) continue @@ -115,7 +111,7 @@ func splitPathIdent(arg string) (path, name string) { // tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp // by prepending all possible prefixes to path. It returns with the first package that it could import, or // with an error. -func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) { +func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) { for prefix := range prefixes { actual := path if prefix == "" { @@ -127,7 +123,7 @@ func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path actual = filepath.Join(prefix, path) logf("\ttrying prefix %q\n", prefix) } - pkg, err = imp(packages, actual) + pkg, err = imp.Import(actual) if err == nil { break } @@ -136,12 +132,14 @@ func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path return } -// tryImports is an importer that tries all registered importers +// tryImporters is an importer that tries all registered importers // successively until one of them succeeds or all of them failed. -func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { +type tryImporters struct{} + +func (t *tryImporters) Import(path string) (pkg *types.Package, err error) { for i, imp := range importers { logf("\t\ttrying %s import\n", sources[i]) - pkg, err = imp(packages, path) + pkg, err = imp.Import(path) if err == nil { break } @@ -150,17 +148,23 @@ func tryImports(packages map[string]*types.Package, path string) (pkg *types.Pac return } +type protector struct { + imp types.Importer +} + +func (p *protector) Import(path string) (pkg *types.Package, err error) { + defer func() { + if recover() != nil { + pkg = nil + err = importFailed + } + }() + return p.imp.Import(path) +} + // protect protects an importer imp from panics and returns the protected importer. func protect(imp types.Importer) types.Importer { - return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { - defer func() { - if recover() != nil { - pkg = nil - err = importFailed - } - }() - return imp(packages, path) - } + return &protector{imp} } // register registers an importer imp for a given source src. diff --git a/cmd/godex/godex14.go b/cmd/godex/godex14.go deleted file mode 100644 index c193bad3bd..0000000000 --- a/cmd/godex/godex14.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package main - -import ( - "errors" - "flag" - "fmt" - "go/build" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "golang.org/x/tools/go/types" -) - -var ( - source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers") - verbose = flag.Bool("v", false, "verbose mode") -) - -// lists of registered sources and corresponding importers -var ( - sources []string - importers []types.Importer - importFailed = errors.New("import failed") -) - -// map of imported packages -var packages = make(map[string]*types.Package) - -func usage() { - fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}") - flag.PrintDefaults() - os.Exit(2) -} - -func report(msg string) { - fmt.Fprintln(os.Stderr, "error: "+msg) - os.Exit(2) -} - -func main() { - flag.Usage = usage - flag.Parse() - - if flag.NArg() == 0 { - report("no package name, path, or file provided") - } - - imp := tryImports - if *source != "" { - imp = lookup(*source) - if imp == nil { - report("source (-s argument) must be one of: " + strings.Join(sources, ", ")) - } - } - - for _, arg := range flag.Args() { - path, name := splitPathIdent(arg) - logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name) - - // generate possible package path prefixes - // (at the moment we do this for each argument - should probably cache the generated prefixes) - prefixes := make(chan string) - go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path)) - - // import package - pkg, err := tryPrefixes(packages, prefixes, path, imp) - if err != nil { - logf("\t=> ignoring %q: %s\n", path, err) - continue - } - - // filter objects if needed - var filter func(types.Object) bool - if name != "" { - filter = func(obj types.Object) bool { - // TODO(gri) perhaps use regular expression matching here? - return obj.Name() == name - } - } - - // print contents - print(os.Stdout, pkg, filter) - } -} - -func logf(format string, args ...interface{}) { - if *verbose { - fmt.Fprintf(os.Stderr, format, args...) - } -} - -// splitPathIdent splits a path.name argument into its components. -// All but the last path element may contain dots. -func splitPathIdent(arg string) (path, name string) { - if i := strings.LastIndex(arg, "."); i >= 0 { - if j := strings.LastIndex(arg, "/"); j < i { - // '.' is not part of path - path = arg[:i] - name = arg[i+1:] - return - } - } - path = arg - return -} - -// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp -// by prepending all possible prefixes to path. It returns with the first package that it could import, or -// with an error. -func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) { - for prefix := range prefixes { - actual := path - if prefix == "" { - // don't use filepath.Join as it will sanitize the path and remove - // a leading dot and then the path is not recognized as a relative - // package path by the importers anymore - logf("\ttrying no prefix\n") - } else { - actual = filepath.Join(prefix, path) - logf("\ttrying prefix %q\n", prefix) - } - pkg, err = imp(packages, actual) - if err == nil { - break - } - logf("\t=> importing %q failed: %s\n", actual, err) - } - return -} - -// tryImports is an importer that tries all registered importers -// successively until one of them succeeds or all of them failed. -func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { - for i, imp := range importers { - logf("\t\ttrying %s import\n", sources[i]) - pkg, err = imp(packages, path) - if err == nil { - break - } - logf("\t\t=> %s import failed: %s\n", sources[i], err) - } - return -} - -// protect protects an importer imp from panics and returns the protected importer. -func protect(imp types.Importer) types.Importer { - return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { - defer func() { - if recover() != nil { - pkg = nil - err = importFailed - } - }() - return imp(packages, path) - } -} - -// register registers an importer imp for a given source src. -func register(src string, imp types.Importer) { - if lookup(src) != nil { - panic(src + " importer already registered") - } - sources = append(sources, src) - importers = append(importers, protect(imp)) -} - -// lookup returns the importer imp for a given source src. -func lookup(src string) types.Importer { - for i, s := range sources { - if s == src { - return importers[i] - } - } - return nil -} - -func genPrefixes(out chan string, all bool) { - out <- "" - if all { - platform := build.Default.GOOS + "_" + build.Default.GOARCH - dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...) - for _, dirname := range dirnames { - walkDir(filepath.Join(dirname, "pkg", platform), "", out) - } - } - close(out) -} - -func walkDir(dirname, prefix string, out chan string) { - fiList, err := ioutil.ReadDir(dirname) - if err != nil { - return - } - for _, fi := range fiList { - if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") { - prefix := filepath.Join(prefix, fi.Name()) - out <- prefix - walkDir(filepath.Join(dirname, fi.Name()), prefix, out) - } - } -} diff --git a/cmd/godex/print.go b/cmd/godex/print.go index d411afdd50..02b46060d5 100644 --- a/cmd/godex/print.go +++ b/cmd/godex/print.go @@ -9,12 +9,11 @@ package main import ( "bytes" "fmt" + exact "go/constant" "go/token" + "go/types" "io" "math/big" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) // TODO(gri) use tabwriter for alignment? diff --git a/cmd/godex/print14.go b/cmd/godex/print14.go deleted file mode 100644 index 55d7c5b631..0000000000 --- a/cmd/godex/print14.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package main - -import ( - "bytes" - "fmt" - "go/token" - "io" - "math/big" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// TODO(gri) use tabwriter for alignment? - -func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) { - var p printer - p.pkg = pkg - p.printPackage(pkg, filter) - p.printGccgoExtra(pkg) - io.Copy(w, &p.buf) -} - -type printer struct { - pkg *types.Package - buf bytes.Buffer - indent int // current indentation level - last byte // last byte written -} - -func (p *printer) print(s string) { - // Write the string one byte at a time. We care about the presence of - // newlines for indentation which we will see even in the presence of - // (non-corrupted) Unicode; no need to read one rune at a time. - for i := 0; i < len(s); i++ { - ch := s[i] - if ch != '\n' && p.last == '\n' { - // Note: This could lead to a range overflow for very large - // indentations, but it's extremely unlikely to happen for - // non-pathological code. - p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent]) - } - p.buf.WriteByte(ch) - p.last = ch - } -} - -func (p *printer) printf(format string, args ...interface{}) { - p.print(fmt.Sprintf(format, args...)) -} - -// methodsFor returns the named type and corresponding methods if the type -// denoted by obj is not an interface and has methods. Otherwise it returns -// the zero value. -func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) { - named, _ := obj.Type().(*types.Named) - if named == nil { - // A type name's type can also be the - // exported basic type unsafe.Pointer. - return nil, nil - } - if _, ok := named.Underlying().(*types.Interface); ok { - // ignore interfaces - return nil, nil - } - methods := combinedMethodSet(named) - if len(methods) == 0 { - return nil, nil - } - return named, methods -} - -func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { - // collect objects by kind - var ( - consts []*types.Const - typem []*types.Named // non-interface types with methods - typez []*types.TypeName // interfaces or types without methods - vars []*types.Var - funcs []*types.Func - builtins []*types.Builtin - methods = make(map[*types.Named][]*types.Selection) // method sets for named types - ) - scope := pkg.Scope() - for _, name := range scope.Names() { - obj := scope.Lookup(name) - if obj.Exported() { - // collect top-level exported and possibly filtered objects - if filter == nil || filter(obj) { - switch obj := obj.(type) { - case *types.Const: - consts = append(consts, obj) - case *types.TypeName: - // group into types with methods and types without - if named, m := methodsFor(obj); named != nil { - typem = append(typem, named) - methods[named] = m - } else { - typez = append(typez, obj) - } - case *types.Var: - vars = append(vars, obj) - case *types.Func: - funcs = append(funcs, obj) - case *types.Builtin: - // for unsafe.Sizeof, etc. - builtins = append(builtins, obj) - } - } - } else if filter == nil { - // no filtering: collect top-level unexported types with methods - if obj, _ := obj.(*types.TypeName); obj != nil { - // see case *types.TypeName above - if named, m := methodsFor(obj); named != nil { - typem = append(typem, named) - methods[named] = m - } - } - } - } - - p.printf("package %s // %q\n", pkg.Name(), pkg.Path()) - - p.printDecl("const", len(consts), func() { - for _, obj := range consts { - p.printObj(obj) - p.print("\n") - } - }) - - p.printDecl("var", len(vars), func() { - for _, obj := range vars { - p.printObj(obj) - p.print("\n") - } - }) - - p.printDecl("type", len(typez), func() { - for _, obj := range typez { - p.printf("%s ", obj.Name()) - p.writeType(p.pkg, obj.Type().Underlying()) - p.print("\n") - } - }) - - // non-interface types with methods - for _, named := range typem { - first := true - if obj := named.Obj(); obj.Exported() { - if first { - p.print("\n") - first = false - } - p.printf("type %s ", obj.Name()) - p.writeType(p.pkg, named.Underlying()) - p.print("\n") - } - for _, m := range methods[named] { - if obj := m.Obj(); obj.Exported() { - if first { - p.print("\n") - first = false - } - p.printFunc(m.Recv(), obj.(*types.Func)) - p.print("\n") - } - } - } - - if len(funcs) > 0 { - p.print("\n") - for _, obj := range funcs { - p.printFunc(nil, obj) - p.print("\n") - } - } - - // TODO(gri) better handling of builtins (package unsafe only) - if len(builtins) > 0 { - p.print("\n") - for _, obj := range builtins { - p.printf("func %s() // builtin\n", obj.Name()) - } - } - - p.print("\n") -} - -func (p *printer) printDecl(keyword string, n int, printGroup func()) { - switch n { - case 0: - // nothing to do - case 1: - p.printf("\n%s ", keyword) - printGroup() - default: - p.printf("\n%s (\n", keyword) - p.indent++ - printGroup() - p.indent-- - p.print(")\n") - } -} - -// absInt returns the absolute value of v as a *big.Int. -// v must be a numeric value. -func absInt(v exact.Value) *big.Int { - // compute big-endian representation of v - b := exact.Bytes(v) // little-endian - for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { - b[i], b[j] = b[j], b[i] - } - return new(big.Int).SetBytes(b) -} - -var ( - one = big.NewRat(1, 1) - ten = big.NewRat(10, 1) -) - -// floatString returns the string representation for a -// numeric value v in normalized floating-point format. -func floatString(v exact.Value) string { - if exact.Sign(v) == 0 { - return "0.0" - } - // x != 0 - - // convert |v| into a big.Rat x - x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.Denom(v))) - - // normalize x and determine exponent e - // (This is not very efficient, but also not speed-critical.) - var e int - for x.Cmp(ten) >= 0 { - x.Quo(x, ten) - e++ - } - for x.Cmp(one) < 0 { - x.Mul(x, ten) - e-- - } - - // TODO(gri) Values such as 1/2 are easier to read in form 0.5 - // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as - // 10.0. Fine-tune best exponent range for readability. - - s := x.FloatString(100) // good-enough precision - - // trim trailing 0's - i := len(s) - for i > 0 && s[i-1] == '0' { - i-- - } - s = s[:i] - - // add a 0 if the number ends in decimal point - if len(s) > 0 && s[len(s)-1] == '.' { - s += "0" - } - - // add exponent and sign - if e != 0 { - s += fmt.Sprintf("e%+d", e) - } - if exact.Sign(v) < 0 { - s = "-" + s - } - - // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator - // are just a small number of decimal digits), add the exact fraction as - // a comment. For instance: 3.3333...e-1 /* = 1/3 */ - - return s -} - -// valString returns the string representation for the value v. -// Setting floatFmt forces an integer value to be formatted in -// normalized floating-point format. -// TODO(gri) Move this code into package exact. -func valString(v exact.Value, floatFmt bool) string { - switch v.Kind() { - case exact.Int: - if floatFmt { - return floatString(v) - } - case exact.Float: - return floatString(v) - case exact.Complex: - re := exact.Real(v) - im := exact.Imag(v) - var s string - if exact.Sign(re) != 0 { - s = floatString(re) - if exact.Sign(im) >= 0 { - s += " + " - } else { - s += " - " - im = exact.UnaryOp(token.SUB, im, 0) // negate im - } - } - // im != 0, otherwise v would be exact.Int or exact.Float - return s + floatString(im) + "i" - } - return v.String() -} - -func (p *printer) printObj(obj types.Object) { - p.print(obj.Name()) - - typ, basic := obj.Type().Underlying().(*types.Basic) - if basic && typ.Info()&types.IsUntyped != 0 { - // don't write untyped types - } else { - p.print(" ") - p.writeType(p.pkg, obj.Type()) - } - - if obj, ok := obj.(*types.Const); ok { - floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 - p.print(" = ") - p.print(valString(obj.Val(), floatFmt)) - } -} - -func (p *printer) printFunc(recvType types.Type, obj *types.Func) { - p.print("func ") - sig := obj.Type().(*types.Signature) - if recvType != nil { - p.print("(") - p.writeType(p.pkg, recvType) - p.print(") ") - } - p.print(obj.Name()) - p.writeSignature(p.pkg, sig) -} - -// combinedMethodSet returns the method set for a named type T -// merged with all the methods of *T that have different names than -// the methods of T. -// -// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet -// but doesn't require a MethodSetCache. -// TODO(gri) If this functionality doesn't change over time, consider -// just calling IntuitiveMethodSet eventually. -func combinedMethodSet(T *types.Named) []*types.Selection { - // method set for T - mset := types.NewMethodSet(T) - var res []*types.Selection - for i, n := 0, mset.Len(); i < n; i++ { - res = append(res, mset.At(i)) - } - - // add all *T methods with names different from T methods - pmset := types.NewMethodSet(types.NewPointer(T)) - for i, n := 0, pmset.Len(); i < n; i++ { - pm := pmset.At(i) - if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil { - res = append(res, pm) - } - } - - return res -} diff --git a/cmd/godex/source.go b/cmd/godex/source.go index a86031a060..0d3c006859 100644 --- a/cmd/godex/source.go +++ b/cmd/godex/source.go @@ -8,14 +8,14 @@ package main -import ( - "golang.org/x/tools/go/types" -) +import "go/types" func init() { - register("source", sourceImporter) + register("source", sourceImporter{}) } -func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) { +type sourceImporter struct{} + +func (sourceImporter) Import(path string) (*types.Package, error) { panic("unimplemented") } diff --git a/cmd/godex/source14.go b/cmd/godex/source14.go deleted file mode 100644 index 620a9b35db..0000000000 --- a/cmd/godex/source14.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// This file implements access to export data from source. - -package main - -import ( - "golang.org/x/tools/go/types" -) - -func init() { - register("source", sourceImporter) -} - -func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) { - panic("unimplemented") -} diff --git a/cmd/godex/writetype.go b/cmd/godex/writetype.go index c079855011..ed0bd9f2ff 100644 --- a/cmd/godex/writetype.go +++ b/cmd/godex/writetype.go @@ -14,7 +14,7 @@ package main -import "golang.org/x/tools/go/types" +import "go/types" func (p *printer) writeType(this *types.Package, typ types.Type) { p.writeTypeInternal(this, typ, make([]types.Type, 8)) diff --git a/cmd/godex/writetype14.go b/cmd/godex/writetype14.go deleted file mode 100644 index ea009b4b1c..0000000000 --- a/cmd/godex/writetype14.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// This file implements writing of types. The functionality is lifted -// directly from go/types, but now contains various modifications for -// nicer output. -// -// TODO(gri) back-port once we have a fixed interface and once the -// go/types API is not frozen anymore for the 1.3 release; and remove -// this implementation if possible. - -package main - -import "golang.org/x/tools/go/types" - -func (p *printer) writeType(this *types.Package, typ types.Type) { - p.writeTypeInternal(this, typ, make([]types.Type, 8)) -} - -// From go/types - leave for now to ease back-porting this code. -const GcCompatibilityMode = false - -func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) { - // Theoretically, this is a quadratic lookup algorithm, but in - // practice deeply nested composite types with unnamed component - // types are uncommon. This code is likely more efficient than - // using a map. - for _, t := range visited { - if t == typ { - p.printf("○%T", typ) // cycle to typ - return - } - } - visited = append(visited, typ) - - switch t := typ.(type) { - case nil: - p.print("") - - case *types.Basic: - if t.Kind() == types.UnsafePointer { - p.print("unsafe.") - } - if GcCompatibilityMode { - // forget the alias names - switch t.Kind() { - case types.Byte: - t = types.Typ[types.Uint8] - case types.Rune: - t = types.Typ[types.Int32] - } - } - p.print(t.Name()) - - case *types.Array: - p.printf("[%d]", t.Len()) - p.writeTypeInternal(this, t.Elem(), visited) - - case *types.Slice: - p.print("[]") - p.writeTypeInternal(this, t.Elem(), visited) - - case *types.Struct: - n := t.NumFields() - if n == 0 { - p.print("struct{}") - return - } - - p.print("struct {\n") - p.indent++ - for i := 0; i < n; i++ { - f := t.Field(i) - if !f.Anonymous() { - p.printf("%s ", f.Name()) - } - p.writeTypeInternal(this, f.Type(), visited) - if tag := t.Tag(i); tag != "" { - p.printf(" %q", tag) - } - p.print("\n") - } - p.indent-- - p.print("}") - - case *types.Pointer: - p.print("*") - p.writeTypeInternal(this, t.Elem(), visited) - - case *types.Tuple: - p.writeTuple(this, t, false, visited) - - case *types.Signature: - p.print("func") - p.writeSignatureInternal(this, t, visited) - - case *types.Interface: - // We write the source-level methods and embedded types rather - // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have anonymous - // interface types that (directly or indirectly) embed the - // current interface. For instance, consider the result type - // of m: - // - // type T interface{ - // m() interface{ T } - // } - // - n := t.NumMethods() - if n == 0 { - p.print("interface{}") - return - } - - p.print("interface {\n") - p.indent++ - if GcCompatibilityMode { - // print flattened interface - // (useful to compare against gc-generated interfaces) - for i := 0; i < n; i++ { - m := t.Method(i) - p.print(m.Name()) - p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) - p.print("\n") - } - } else { - // print explicit interface methods and embedded types - for i, n := 0, t.NumExplicitMethods(); i < n; i++ { - m := t.ExplicitMethod(i) - p.print(m.Name()) - p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) - p.print("\n") - } - for i, n := 0, t.NumEmbeddeds(); i < n; i++ { - typ := t.Embedded(i) - p.writeTypeInternal(this, typ, visited) - p.print("\n") - } - } - p.indent-- - p.print("}") - - case *types.Map: - p.print("map[") - p.writeTypeInternal(this, t.Key(), visited) - p.print("]") - p.writeTypeInternal(this, t.Elem(), visited) - - case *types.Chan: - var s string - var parens bool - switch t.Dir() { - case types.SendRecv: - s = "chan " - // chan (<-chan T) requires parentheses - if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly { - parens = true - } - case types.SendOnly: - s = "chan<- " - case types.RecvOnly: - s = "<-chan " - default: - panic("unreachable") - } - p.print(s) - if parens { - p.print("(") - } - p.writeTypeInternal(this, t.Elem(), visited) - if parens { - p.print(")") - } - - case *types.Named: - s := "" - if obj := t.Obj(); obj != nil { - if pkg := obj.Pkg(); pkg != nil { - if pkg != this { - p.print(pkg.Path()) - p.print(".") - } - // TODO(gri): function-local named types should be displayed - // differently from named types at package level to avoid - // ambiguity. - } - s = obj.Name() - } - p.print(s) - - default: - // For externally defined implementations of Type. - p.print(t.String()) - } -} - -func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) { - p.print("(") - for i, n := 0, tup.Len(); i < n; i++ { - if i > 0 { - p.print(", ") - } - v := tup.At(i) - if name := v.Name(); name != "" { - p.print(name) - p.print(" ") - } - typ := v.Type() - if variadic && i == n-1 { - p.print("...") - typ = typ.(*types.Slice).Elem() - } - p.writeTypeInternal(this, typ, visited) - } - p.print(")") -} - -func (p *printer) writeSignature(this *types.Package, sig *types.Signature) { - p.writeSignatureInternal(this, sig, make([]types.Type, 8)) -} - -func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) { - p.writeTuple(this, sig.Params(), sig.Variadic(), visited) - - res := sig.Results() - n := res.Len() - if n == 0 { - // no result - return - } - - p.print(" ") - if n == 1 && res.At(0).Name() == "" { - // single unnamed result - p.writeTypeInternal(this, res.At(0).Type(), visited) - return - } - - // multiple or named result(s) - p.writeTuple(this, res, false, visited) -} diff --git a/cmd/gotype/gotype14.go b/cmd/gotype/gotype14.go deleted file mode 100644 index 92d089e9e5..0000000000 --- a/cmd/gotype/gotype14.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2011 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. - -// +build !go1.5 - -// This is a 1:1 copy of gotype.go but for the changes required to build -// against Go1.4 and before. -// TODO(gri) Decide long-term fate of gotype (issue #12303). - -package main - -import ( - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "time" - - "golang.org/x/tools/go/gccgoimporter" - _ "golang.org/x/tools/go/gcimporter" - "golang.org/x/tools/go/types" -) - -var ( - // main operation modes - allFiles = flag.Bool("a", false, "use all (incl. _test.go) files when processing a directory") - allErrors = flag.Bool("e", false, "report all errors (not just the first 10)") - verbose = flag.Bool("v", false, "verbose mode") - gccgo = flag.Bool("gccgo", false, "use gccgoimporter instead of gcimporter") - - // debugging support - sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel") - printAST = flag.Bool("ast", false, "print AST (forces -seq)") - printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)") - parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)") -) - -var ( - fset = token.NewFileSet() - errorCount = 0 - parserMode parser.Mode - sizes types.Sizes -) - -func initParserMode() { - if *allErrors { - parserMode |= parser.AllErrors - } - if *printTrace { - parserMode |= parser.Trace - } - if *parseComments && (*printAST || *printTrace) { - parserMode |= parser.ParseComments - } -} - -func initSizes() { - wordSize := 8 - maxAlign := 8 - switch build.Default.GOARCH { - case "386", "arm": - wordSize = 4 - maxAlign = 4 - // add more cases as needed - } - sizes = &types.StdSizes{WordSize: int64(wordSize), MaxAlign: int64(maxAlign)} -} - -func usage() { - fmt.Fprintln(os.Stderr, "usage: gotype [flags] [path ...]") - flag.PrintDefaults() - os.Exit(2) -} - -func report(err error) { - scanner.PrintError(os.Stderr, err) - if list, ok := err.(scanner.ErrorList); ok { - errorCount += len(list) - return - } - errorCount++ -} - -// parse may be called concurrently -func parse(filename string, src interface{}) (*ast.File, error) { - if *verbose { - fmt.Println(filename) - } - file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently - if *printAST { - ast.Print(fset, file) - } - return file, err -} - -func parseStdin() (*ast.File, error) { - src, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return nil, err - } - return parse("", src) -} - -func parseFiles(filenames []string) ([]*ast.File, error) { - files := make([]*ast.File, len(filenames)) - - if *sequential { - for i, filename := range filenames { - var err error - files[i], err = parse(filename, nil) - if err != nil { - return nil, err // leave unfinished goroutines hanging - } - } - } else { - type parseResult struct { - file *ast.File - err error - } - - out := make(chan parseResult) - for _, filename := range filenames { - go func(filename string) { - file, err := parse(filename, nil) - out <- parseResult{file, err} - }(filename) - } - - for i := range filenames { - res := <-out - if res.err != nil { - return nil, res.err // leave unfinished goroutines hanging - } - files[i] = res.file - } - } - - return files, nil -} - -func parseDir(dirname string) ([]*ast.File, error) { - ctxt := build.Default - pkginfo, err := ctxt.ImportDir(dirname, 0) - if _, nogo := err.(*build.NoGoError); err != nil && !nogo { - return nil, err - } - filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...) - if *allFiles { - filenames = append(filenames, pkginfo.TestGoFiles...) - } - - // complete file names - for i, filename := range filenames { - filenames[i] = filepath.Join(dirname, filename) - } - - return parseFiles(filenames) -} - -func getPkgFiles(args []string) ([]*ast.File, error) { - if len(args) == 0 { - // stdin - file, err := parseStdin() - if err != nil { - return nil, err - } - return []*ast.File{file}, nil - } - - if len(args) == 1 { - // possibly a directory - path := args[0] - info, err := os.Stat(path) - if err != nil { - return nil, err - } - if info.IsDir() { - return parseDir(path) - } - } - - // list of files - return parseFiles(args) -} - -func checkPkgFiles(files []*ast.File) { - type bailout struct{} - conf := types.Config{ - FakeImportC: true, - Error: func(err error) { - if !*allErrors && errorCount >= 10 { - panic(bailout{}) - } - report(err) - }, - Sizes: sizes, - } - if *gccgo { - var inst gccgoimporter.GccgoInstallation - inst.InitFromDriver("gccgo") - conf.Import = inst.GetImporter(nil, nil) - } - - defer func() { - switch p := recover().(type) { - case nil, bailout: - // normal return or early exit - default: - // re-panic - panic(p) - } - }() - - const path = "pkg" // any non-empty string will do for now - conf.Check(path, fset, files, nil) -} - -func printStats(d time.Duration) { - fileCount := 0 - lineCount := 0 - fset.Iterate(func(f *token.File) bool { - fileCount++ - lineCount += f.LineCount() - return true - }) - - fmt.Printf( - "%s (%d files, %d lines, %d lines/s)\n", - d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()), - ) -} - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) // not needed for go1.5 - - flag.Usage = usage - flag.Parse() - if *printAST || *printTrace { - *sequential = true - } - initParserMode() - initSizes() - - start := time.Now() - - files, err := getPkgFiles(flag.Args()) - if err != nil { - report(err) - os.Exit(2) - } - - checkPkgFiles(files) - if errorCount > 0 { - os.Exit(2) - } - - if *verbose { - printStats(time.Since(start)) - } -} diff --git a/cmd/ssadump/main14.go b/cmd/ssadump/main14.go deleted file mode 100644 index 201ea8165d..0000000000 --- a/cmd/ssadump/main14.go +++ /dev/null @@ -1,188 +0,0 @@ -// 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. - -// +build !go1.5 - -// ssadump: a tool for displaying and interpreting the SSA form of Go programs. -package main // import "golang.org/x/tools/cmd/ssadump" - -import ( - "flag" - "fmt" - "go/build" - "os" - "runtime" - "runtime/pprof" - - "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/interp" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -var ( - modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0) - - testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.") - - runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") - - interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. -The value is a sequence of zero or more more of these letters: -R disable [R]ecover() from panic; show interpreter crash instead. -T [T]race execution of the program. Best for single-threaded programs! -`) -) - -const usage = `SSA builder and interpreter. -Usage: ssadump [ ...] ... -Use -help flag to display options. - -Examples: -% ssadump -build=F hello.go # dump SSA form of a single package -% ssadump -run -interp=T hello.go # interpret a program, with tracing -% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely -` + loader.FromArgsUsage + - ` -When -run is specified, ssadump will run the program. -The entry point depends on the -test flag: -if clear, it runs the first package named main. -if set, it runs the tests of each package. -` - -var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") - -func init() { - flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) - - // If $GOMAXPROCS isn't set, use the full capacity of the machine. - // For small machines, use at least 4 threads. - if os.Getenv("GOMAXPROCS") == "" { - n := runtime.NumCPU() - if n < 4 { - n = 4 - } - runtime.GOMAXPROCS(n) - } -} - -func main() { - if err := doMain(); err != nil { - fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) - os.Exit(1) - } -} - -func doMain() error { - flag.Parse() - args := flag.Args() - - conf := loader.Config{Build: &build.Default} - - // Choose types.Sizes from conf.Build. - var wordSize int64 = 8 - switch conf.Build.GOARCH { - case "386", "arm": - wordSize = 4 - } - conf.TypeChecker.Sizes = &types.StdSizes{ - MaxAlign: 8, - WordSize: wordSize, - } - - var interpMode interp.Mode - for _, c := range *interpFlag { - switch c { - case 'T': - interpMode |= interp.EnableTracing - case 'R': - interpMode |= interp.DisableRecover - default: - return fmt.Errorf("unknown -interp option: '%c'", c) - } - } - - if len(args) == 0 { - fmt.Fprint(os.Stderr, usage) - os.Exit(1) - } - - // Profiling support. - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - // Use the initial packages from the command line. - args, err := conf.FromArgs(args, *testFlag) - if err != nil { - return err - } - - // The interpreter needs the runtime package. - if *runFlag { - conf.Import("runtime") - } - - // Load, parse and type-check the whole program. - iprog, err := conf.Load() - if err != nil { - return err - } - - // Create and build SSA-form program representation. - prog := ssautil.CreateProgram(iprog, *modeFlag) - - // Build and display only the initial packages - // (and synthetic wrappers), unless -run is specified. - for _, info := range iprog.InitialPackages() { - prog.Package(info.Pkg).Build() - } - - // Run the interpreter. - if *runFlag { - prog.Build() - - var main *ssa.Package - pkgs := prog.AllPackages() - if *testFlag { - // If -test, run all packages' tests. - if len(pkgs) > 0 { - main = prog.CreateTestMainPackage(pkgs...) - } - if main == nil { - return fmt.Errorf("no tests") - } - } else { - // Otherwise, run main.main. - for _, pkg := range pkgs { - if pkg.Pkg.Name() == "main" { - main = pkg - if main.Func("main") == nil { - return fmt.Errorf("no func main() in main package") - } - break - } - } - if main == nil { - return fmt.Errorf("no main package") - } - } - - if runtime.GOARCH != build.Default.GOARCH { - return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", - build.Default.GOARCH, runtime.GOARCH) - } - - interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) - } - return nil -} diff --git a/cmd/stringer/stringer14.go b/cmd/stringer/stringer14.go deleted file mode 100644 index e0962e3f04..0000000000 --- a/cmd/stringer/stringer14.go +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer -// interface. Given the name of a (signed or unsigned) integer type T that has constants -// defined, stringer will create a new self-contained Go source file implementing -// func (t T) String() string -// The file is created in the same package and directory as the package that defines T. -// It has helpful defaults designed for use with go generate. -// -// Stringer works best with constants that are consecutive values such as created using iota, -// but creates good code regardless. In the future it might also provide custom support for -// constant sets that are bit patterns. -// -// For example, given this snippet, -// -// package painkiller -// -// type Pill int -// -// const ( -// Placebo Pill = iota -// Aspirin -// Ibuprofen -// Paracetamol -// Acetaminophen = Paracetamol -// ) -// -// running this command -// -// stringer -type=Pill -// -// in the same directory will create the file pill_string.go, in package painkiller, -// containing a definition of -// -// func (Pill) String() string -// -// That method will translate the value of a Pill constant to the string representation -// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will -// print the string "Aspirin". -// -// Typically this process would be run using go generate, like this: -// -// //go:generate stringer -type=Pill -// -// If multiple constants have the same value, the lexically first matching name will -// be used (in the example, Acetaminophen will print as "Paracetamol"). -// -// With no arguments, it processes the package in the current directory. -// Otherwise, the arguments must name a single directory holding a Go package -// or a set of Go source files that represent a single Go package. -// -// The -type flag accepts a comma-separated list of types so a single run can -// generate methods for multiple types. The default output file is t_string.go, -// where t is the lower-cased name of the first type listed. It can be overridden -// with the -output flag. -// -package main // import "golang.org/x/tools/cmd/stringer" - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/build" - "go/format" - "go/parser" - "go/token" - "io/ioutil" - "log" - "os" - "path/filepath" - "sort" - "strings" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" - - _ "golang.org/x/tools/go/gcimporter" -) - -var ( - typeNames = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_string.go") -) - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n") - fmt.Fprintf(os.Stderr, "\tstringer [flags[ -type T files... # Must be a single package\n") - fmt.Fprintf(os.Stderr, "For more information, see:\n") - fmt.Fprintf(os.Stderr, "\thttp://godoc.org/golang.org/x/tools/cmd/stringer\n") - fmt.Fprintf(os.Stderr, "Flags:\n") - flag.PrintDefaults() -} - -func main() { - log.SetFlags(0) - log.SetPrefix("stringer: ") - flag.Usage = Usage - flag.Parse() - if len(*typeNames) == 0 { - flag.Usage() - os.Exit(2) - } - types := strings.Split(*typeNames, ",") - - // We accept either one directory or a list of files. Which do we have? - args := flag.Args() - if len(args) == 0 { - // Default: process whole package in current directory. - args = []string{"."} - } - - // Parse the package once. - var ( - dir string - g Generator - ) - if len(args) == 1 && isDirectory(args[0]) { - dir = args[0] - g.parsePackageDir(args[0]) - } else { - dir = filepath.Dir(args[0]) - g.parsePackageFiles(args) - } - - // Print the header and package clause. - g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT\n", strings.Join(os.Args[1:], " ")) - g.Printf("\n") - g.Printf("package %s", g.pkg.name) - g.Printf("\n") - g.Printf("import \"fmt\"\n") // Used by all methods. - - // Run generate for each type. - for _, typeName := range types { - g.generate(typeName) - } - - // Format the output. - src := g.format() - - // Write to file. - outputName := *output - if outputName == "" { - baseName := fmt.Sprintf("%s_string.go", types[0]) - outputName = filepath.Join(dir, strings.ToLower(baseName)) - } - err := ioutil.WriteFile(outputName, src, 0644) - if err != nil { - log.Fatalf("writing output: %s", err) - } -} - -// isDirectory reports whether the named file is a directory. -func isDirectory(name string) bool { - info, err := os.Stat(name) - if err != nil { - log.Fatal(err) - } - return info.IsDir() -} - -// Generator holds the state of the analysis. Primarily used to buffer -// the output for format.Source. -type Generator struct { - buf bytes.Buffer // Accumulated output. - pkg *Package // Package we are scanning. -} - -func (g *Generator) Printf(format string, args ...interface{}) { - fmt.Fprintf(&g.buf, format, args...) -} - -// File holds a single parsed file and associated data. -type File struct { - pkg *Package // Package to which this file belongs. - file *ast.File // Parsed AST. - // These fields are reset for each type being generated. - typeName string // Name of the constant type. - values []Value // Accumulator for constant values of that type. -} - -type Package struct { - dir string - name string - defs map[*ast.Ident]types.Object - files []*File - typesPkg *types.Package -} - -// parsePackageDir parses the package residing in the directory. -func (g *Generator) parsePackageDir(directory string) { - pkg, err := build.Default.ImportDir(directory, 0) - if err != nil { - log.Fatalf("cannot process directory %s: %s", directory, err) - } - var names []string - names = append(names, pkg.GoFiles...) - names = append(names, pkg.CgoFiles...) - // TODO: Need to think about constants in test files. Maybe write type_string_test.go - // in a separate pass? For later. - // names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. - names = append(names, pkg.SFiles...) - names = prefixDirectory(directory, names) - g.parsePackage(directory, names, nil) -} - -// parsePackageFiles parses the package occupying the named files. -func (g *Generator) parsePackageFiles(names []string) { - g.parsePackage(".", names, nil) -} - -// prefixDirectory places the directory name on the beginning of each name in the list. -func prefixDirectory(directory string, names []string) []string { - if directory == "." { - return names - } - ret := make([]string, len(names)) - for i, name := range names { - ret[i] = filepath.Join(directory, name) - } - return ret -} - -// parsePackage analyzes the single package constructed from the named files. -// If text is non-nil, it is a string to be used instead of the content of the file, -// to be used for testing. parsePackage exits if there is an error. -func (g *Generator) parsePackage(directory string, names []string, text interface{}) { - var files []*File - var astFiles []*ast.File - g.pkg = new(Package) - fs := token.NewFileSet() - for _, name := range names { - if !strings.HasSuffix(name, ".go") { - continue - } - parsedFile, err := parser.ParseFile(fs, name, text, 0) - if err != nil { - log.Fatalf("parsing package: %s: %s", name, err) - } - astFiles = append(astFiles, parsedFile) - files = append(files, &File{ - file: parsedFile, - pkg: g.pkg, - }) - } - if len(astFiles) == 0 { - log.Fatalf("%s: no buildable Go files", directory) - } - g.pkg.name = astFiles[0].Name.Name - g.pkg.files = files - g.pkg.dir = directory - // Type check the package. - g.pkg.check(fs, astFiles) -} - -// check type-checks the package. The package must be OK to proceed. -func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) { - pkg.defs = make(map[*ast.Ident]types.Object) - config := types.Config{FakeImportC: true} - info := &types.Info{ - Defs: pkg.defs, - } - typesPkg, err := config.Check(pkg.dir, fs, astFiles, info) - if err != nil { - log.Fatalf("checking package: %s", err) - } - pkg.typesPkg = typesPkg -} - -// generate produces the String method for the named type. -func (g *Generator) generate(typeName string) { - values := make([]Value, 0, 100) - for _, file := range g.pkg.files { - // Set the state for this run of the walker. - file.typeName = typeName - file.values = nil - if file.file != nil { - ast.Inspect(file.file, file.genDecl) - values = append(values, file.values...) - } - } - - if len(values) == 0 { - log.Fatalf("no values defined for type %s", typeName) - } - runs := splitIntoRuns(values) - // The decision of which pattern to use depends on the number of - // runs in the numbers. If there's only one, it's easy. For more than - // one, there's a tradeoff between complexity and size of the data - // and code vs. the simplicity of a map. A map takes more space, - // but so does the code. The decision here (crossover at 10) is - // arbitrary, but considers that for large numbers of runs the cost - // of the linear scan in the switch might become important, and - // rather than use yet another algorithm such as binary search, - // we punt and use a map. In any case, the likelihood of a map - // being necessary for any realistic example other than bitmasks - // is very low. And bitmasks probably deserve their own analysis, - // to be done some other day. - switch { - case len(runs) == 1: - g.buildOneRun(runs, typeName) - case len(runs) <= 10: - g.buildMultipleRuns(runs, typeName) - default: - g.buildMap(runs, typeName) - } -} - -// splitIntoRuns breaks the values into runs of contiguous sequences. -// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}. -// The input slice is known to be non-empty. -func splitIntoRuns(values []Value) [][]Value { - // We use stable sort so the lexically first name is chosen for equal elements. - sort.Stable(byValue(values)) - // Remove duplicates. Stable sort has put the one we want to print first, - // so use that one. The String method won't care about which named constant - // was the argument, so the first name for the given value is the only one to keep. - // We need to do this because identical values would cause the switch or map - // to fail to compile. - j := 1 - for i := 1; i < len(values); i++ { - if values[i].value != values[i-1].value { - values[j] = values[i] - j++ - } - } - values = values[:j] - runs := make([][]Value, 0, 10) - for len(values) > 0 { - // One contiguous sequence per outer loop. - i := 1 - for i < len(values) && values[i].value == values[i-1].value+1 { - i++ - } - runs = append(runs, values[:i]) - values = values[i:] - } - return runs -} - -// format returns the gofmt-ed contents of the Generator's buffer. -func (g *Generator) format() []byte { - src, err := format.Source(g.buf.Bytes()) - if err != nil { - // Should never happen, but can arise when developing this code. - // The user can compile the output to see the error. - log.Printf("warning: internal error: invalid Go generated: %s", err) - log.Printf("warning: compile the package to analyze the error") - return g.buf.Bytes() - } - return src -} - -// Value represents a declared constant. -type Value struct { - name string // The name of the constant. - // The value is stored as a bit pattern alone. The boolean tells us - // whether to interpret it as an int64 or a uint64; the only place - // this matters is when sorting. - // Much of the time the str field is all we need; it is printed - // by Value.String. - value uint64 // Will be converted to int64 when needed. - signed bool // Whether the constant is a signed type. - str string // The string representation given by the "go/exact" package. -} - -func (v *Value) String() string { - return v.str -} - -// byValue lets us sort the constants into increasing order. -// We take care in the Less method to sort in signed or unsigned order, -// as appropriate. -type byValue []Value - -func (b byValue) Len() int { return len(b) } -func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b byValue) Less(i, j int) bool { - if b[i].signed { - return int64(b[i].value) < int64(b[j].value) - } - return b[i].value < b[j].value -} - -// genDecl processes one declaration clause. -func (f *File) genDecl(node ast.Node) bool { - decl, ok := node.(*ast.GenDecl) - if !ok || decl.Tok != token.CONST { - // We only care about const declarations. - return true - } - // The name of the type of the constants we are declaring. - // Can change if this is a multi-element declaration. - typ := "" - // Loop over the elements of the declaration. Each element is a ValueSpec: - // a list of names possibly followed by a type, possibly followed by values. - // If the type and value are both missing, we carry down the type (and value, - // but the "go/types" package takes care of that). - for _, spec := range decl.Specs { - vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. - if vspec.Type == nil && len(vspec.Values) > 0 { - // "X = 1". With no type but a value, the constant is untyped. - // Skip this vspec and reset the remembered type. - typ = "" - continue - } - if vspec.Type != nil { - // "X T". We have a type. Remember it. - ident, ok := vspec.Type.(*ast.Ident) - if !ok { - continue - } - typ = ident.Name - } - if typ != f.typeName { - // This is not the type we're looking for. - continue - } - // We now have a list of names (from one line of source code) all being - // declared with the desired type. - // Grab their names and actual values and store them in f.values. - for _, name := range vspec.Names { - if name.Name == "_" { - continue - } - // This dance lets the type checker find the values for us. It's a - // bit tricky: look up the object declared by the name, find its - // types.Const, and extract its value. - obj, ok := f.pkg.defs[name] - if !ok { - log.Fatalf("no value for constant %s", name) - } - info := obj.Type().Underlying().(*types.Basic).Info() - if info&types.IsInteger == 0 { - log.Fatalf("can't handle non-integer constant type %s", typ) - } - value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. - if value.Kind() != exact.Int { - log.Fatalf("can't happen: constant is not an integer %s", name) - } - i64, isInt := exact.Int64Val(value) - u64, isUint := exact.Uint64Val(value) - if !isInt && !isUint { - log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) - } - if !isInt { - u64 = uint64(i64) - } - v := Value{ - name: name.Name, - value: u64, - signed: info&types.IsUnsigned == 0, - str: value.String(), - } - f.values = append(f.values, v) - } - } - return false -} - -// Helpers - -// usize returns the number of bits of the smallest unsigned integer -// type that will hold n. Used to create the smallest possible slice of -// integers to use as indexes into the concatenated strings. -func usize(n int) int { - switch { - case n < 1<<8: - return 8 - case n < 1<<16: - return 16 - default: - // 2^32 is enough constants for anyone. - return 32 - } -} - -// declareIndexAndNameVars declares the index slices and concatenated names -// strings representing the runs of values. -func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { - var indexes, names []string - for i, run := range runs { - index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) - indexes = append(indexes, index) - names = append(names, name) - } - g.Printf("const (\n") - for _, name := range names { - g.Printf("\t%s\n", name) - } - g.Printf(")\n\n") - g.Printf("var (") - for _, index := range indexes { - g.Printf("\t%s\n", index) - } - g.Printf(")\n\n") -} - -// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars -func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { - index, name := g.createIndexAndNameDecl(run, typeName, "") - g.Printf("const %s\n", name) - g.Printf("var %s\n", index) -} - -// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var". -func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { - b := new(bytes.Buffer) - indexes := make([]int, len(run)) - for i := range run { - b.WriteString(run[i].name) - indexes[i] = b.Len() - } - nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) - nameLen := b.Len() - b.Reset() - fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) - for i, v := range indexes { - if i > 0 { - fmt.Fprintf(b, ", ") - } - fmt.Fprintf(b, "%d", v) - } - fmt.Fprintf(b, "}") - return b.String(), nameConst -} - -// declareNameVars declares the concatenated names string representing all the values in the runs. -func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) { - g.Printf("const _%s_name%s = \"", typeName, suffix) - for _, run := range runs { - for i := range run { - g.Printf("%s", run[i].name) - } - } - g.Printf("\"\n") -} - -// buildOneRun generates the variables and String method for a single run of contiguous values. -func (g *Generator) buildOneRun(runs [][]Value, typeName string) { - values := runs[0] - g.Printf("\n") - g.declareIndexAndNameVar(values, typeName) - // The generated code is simple enough to write as a Printf format. - lessThanZero := "" - if values[0].signed { - lessThanZero = "i < 0 || " - } - if values[0].value == 0 { // Signed or unsigned, 0 is still 0. - g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) - } else { - g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) - } -} - -// Arguments to format are: -// [1]: type name -// [2]: size of index element (8 for uint8 etc.) -// [3]: less than zero check (for signed types) -const stringOneRun = `func (i %[1]s) String() string { - if %[3]si >= %[1]s(len(_%[1]s_index)-1) { - return fmt.Sprintf("%[1]s(%%d)", i) - } - return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] -} -` - -// Arguments to format are: -// [1]: type name -// [2]: lowest defined value for type, as a string -// [3]: size of index element (8 for uint8 etc.) -// [4]: less than zero check (for signed types) -/* - */ -const stringOneRunWithOffset = `func (i %[1]s) String() string { - i -= %[2]s - if %[4]si >= %[1]s(len(_%[1]s_index)-1) { - return fmt.Sprintf("%[1]s(%%d)", i + %[2]s) - } - return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]] -} -` - -// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. -// For this pattern, a single Printf format won't do. -func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { - g.Printf("\n") - g.declareIndexAndNameVars(runs, typeName) - g.Printf("func (i %s) String() string {\n", typeName) - g.Printf("\tswitch {\n") - for i, values := range runs { - if len(values) == 1 { - g.Printf("\tcase i == %s:\n", &values[0]) - g.Printf("\t\treturn _%s_name_%d\n", typeName, i) - continue - } - g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) - if values[0].value != 0 { - g.Printf("\t\ti -= %s\n", &values[0]) - } - g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n", - typeName, i, typeName, i, typeName, i) - } - g.Printf("\tdefault:\n") - g.Printf("\t\treturn fmt.Sprintf(\"%s(%%d)\", i)\n", typeName) - g.Printf("\t}\n") - g.Printf("}\n") -} - -// buildMap handles the case where the space is so sparse a map is a reasonable fallback. -// It's a rare situation but has simple code. -func (g *Generator) buildMap(runs [][]Value, typeName string) { - g.Printf("\n") - g.declareNameVars(runs, typeName, "") - g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) - n := 0 - for _, values := range runs { - for _, value := range values { - g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) - n += len(value.name) - } - } - g.Printf("}\n\n") - g.Printf(stringMap, typeName) -} - -// Argument to format is the type name. -const stringMap = `func (i %[1]s) String() string { - if str, ok := _%[1]s_map[i]; ok { - return str - } - return fmt.Sprintf("%[1]s(%%d)", i) -} -` diff --git a/cmd/vet/asmdecl.go b/cmd/vet/asmdecl.go deleted file mode 100644 index 6bdfdbf3be..0000000000 --- a/cmd/vet/asmdecl.go +++ /dev/null @@ -1,662 +0,0 @@ -// 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. - -// Identify mismatches between assembly files and Go func declarations. - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - "regexp" - "strconv" - "strings" -) - -// 'kind' is a kind of assembly variable. -// The kinds 1, 2, 4, 8 stand for values of that size. -type asmKind int - -// These special kinds are not valid sizes. -const ( - asmString asmKind = 100 + iota - asmSlice - asmInterface - asmEmptyInterface -) - -// An asmArch describes assembly parameters for an architecture -type asmArch struct { - name string - ptrSize int - intSize int - maxAlign int - bigEndian bool - stack string - lr bool -} - -// An asmFunc describes the expected variables for a function on a given architecture. -type asmFunc struct { - arch *asmArch - size int // size of all arguments - vars map[string]*asmVar - varByOffset map[int]*asmVar -} - -// An asmVar describes a single assembly variable. -type asmVar struct { - name string - kind asmKind - typ string - off int - size int - inner []*asmVar -} - -var ( - asmArch386 = asmArch{"386", 4, 4, 4, false, "SP", false} - asmArchArm = asmArch{"arm", 4, 4, 4, false, "R13", true} - asmArchArm64 = asmArch{"arm64", 8, 8, 8, false, "RSP", true} - asmArchAmd64 = asmArch{"amd64", 8, 8, 8, false, "SP", false} - asmArchAmd64p32 = asmArch{"amd64p32", 4, 4, 8, false, "SP", false} - asmArchPower64 = asmArch{"power64", 8, 8, 8, true, "R1", true} - asmArchPower64LE = asmArch{"power64le", 8, 8, 8, false, "R1", true} - - arches = []*asmArch{ - &asmArch386, - &asmArchArm, - &asmArchArm64, - &asmArchAmd64, - &asmArchAmd64p32, - &asmArchPower64, - &asmArchPower64LE, - } -) - -var ( - re = regexp.MustCompile - asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`) - asmTEXT = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`) - asmDATA = re(`\b(DATA|GLOBL)\b`) - asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) - asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`) - asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`) - asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`) - power64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`) -) - -func asmCheck(pkg *Package) { - if !vet("asmdecl") { - return - } - - // No work if no assembly files. - if !pkg.hasFileWithSuffix(".s") { - return - } - - // Gather declarations. knownFunc[name][arch] is func description. - knownFunc := make(map[string]map[string]*asmFunc) - - for _, f := range pkg.files { - if f.file != nil { - for _, decl := range f.file.Decls { - if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil { - knownFunc[decl.Name.Name] = f.asmParseDecl(decl) - } - } - } - } - -Files: - for _, f := range pkg.files { - if !strings.HasSuffix(f.name, ".s") { - continue - } - Println("Checking file", f.name) - - // Determine architecture from file name if possible. - var arch string - var archDef *asmArch - for _, a := range arches { - if strings.HasSuffix(f.name, "_"+a.name+".s") { - arch = a.name - archDef = a - break - } - } - - lines := strings.SplitAfter(string(f.content), "\n") - var ( - fn *asmFunc - fnName string - localSize, argSize int - wroteSP bool - haveRetArg bool - retLine []int - ) - - flushRet := func() { - if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 { - v := fn.vars["ret"] - for _, line := range retLine { - f.Badf(token.NoPos, "%s:%d: [%s] %s: RET without writing to %d-byte ret+%d(FP)", f.name, line, arch, fnName, v.size, v.off) - } - } - retLine = nil - } - for lineno, line := range lines { - lineno++ - - badf := func(format string, args ...interface{}) { - f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...)) - } - - if arch == "" { - // Determine architecture from +build line if possible. - if m := asmPlusBuild.FindStringSubmatch(line); m != nil { - Fields: - for _, fld := range strings.Fields(m[1]) { - for _, a := range arches { - if a.name == fld { - arch = a.name - archDef = a - break Fields - } - } - } - } - } - - if m := asmTEXT.FindStringSubmatch(line); m != nil { - flushRet() - if arch == "" { - f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name) - continue Files - } - fnName = m[1] - fn = knownFunc[m[1]][arch] - if fn != nil { - size, _ := strconv.Atoi(m[4]) - if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) { - badf("wrong argument size %d; expected $...-%d", size, fn.size) - } - } - localSize, _ = strconv.Atoi(m[3]) - localSize += archDef.intSize - if archDef.lr { - // Account for caller's saved LR - localSize += archDef.intSize - } - argSize, _ = strconv.Atoi(m[4]) - if fn == nil && !strings.Contains(fnName, "<>") { - badf("function %s missing Go declaration", fnName) - } - wroteSP = false - haveRetArg = false - continue - } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") { - // function, but not visible from Go (didn't match asmTEXT), so stop checking - flushRet() - fn = nil - fnName = "" - continue - } - - if strings.Contains(line, "RET") { - retLine = append(retLine, lineno) - } - - if fnName == "" { - continue - } - - if asmDATA.FindStringSubmatch(line) != nil { - fn = nil - } - - if archDef == nil { - continue - } - - if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) { - wroteSP = true - continue - } - - for _, m := range asmSP.FindAllStringSubmatch(line, -1) { - if m[3] != archDef.stack || wroteSP { - continue - } - off := 0 - if m[1] != "" { - off, _ = strconv.Atoi(m[2]) - } - if off >= localSize { - if fn != nil { - v := fn.varByOffset[off-localSize] - if v != nil { - badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize) - continue - } - } - if off >= localSize+argSize { - badf("use of %s points beyond argument frame", m[1]) - continue - } - badf("use of %s to access argument frame", m[1]) - } - } - - if fn == nil { - continue - } - - for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) { - off, _ := strconv.Atoi(m[2]) - v := fn.varByOffset[off] - if v != nil { - badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off) - } else { - badf("use of unnamed argument %s", m[1]) - } - } - - for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) { - name := m[1] - off := 0 - if m[2] != "" { - off, _ = strconv.Atoi(m[2]) - } - if name == "ret" || strings.HasPrefix(name, "ret_") { - haveRetArg = true - } - v := fn.vars[name] - if v == nil { - // Allow argframe+0(FP). - if name == "argframe" && off == 0 { - continue - } - v = fn.varByOffset[off] - if v != nil { - badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off) - } else { - badf("unknown variable %s", name) - } - continue - } - asmCheckVar(badf, fn, line, m[0], off, v) - } - } - flushRet() - } -} - -// asmParseDecl parses a function decl for expected assembly variables. -func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc { - var ( - arch *asmArch - fn *asmFunc - offset int - failed bool - ) - - addVar := func(outer string, v asmVar) { - if vo := fn.vars[outer]; vo != nil { - vo.inner = append(vo.inner, &v) - } - fn.vars[v.name] = &v - for i := 0; i < v.size; i++ { - fn.varByOffset[v.off+i] = &v - } - } - - addParams := func(list []*ast.Field) { - for i, fld := range list { - // Determine alignment, size, and kind of type in declaration. - var align, size int - var kind asmKind - names := fld.Names - typ := f.gofmt(fld.Type) - switch t := fld.Type.(type) { - default: - switch typ { - default: - f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ) - failed = true - return - case "int8", "uint8", "byte", "bool": - size = 1 - case "int16", "uint16": - size = 2 - case "int32", "uint32", "float32": - size = 4 - case "int64", "uint64", "float64": - align = arch.maxAlign - size = 8 - case "int", "uint": - size = arch.intSize - case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer": - size = arch.ptrSize - case "string", "ErrorString": - size = arch.ptrSize * 2 - align = arch.ptrSize - kind = asmString - } - case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr: - size = arch.ptrSize - case *ast.InterfaceType: - align = arch.ptrSize - size = 2 * arch.ptrSize - if len(t.Methods.List) > 0 { - kind = asmInterface - } else { - kind = asmEmptyInterface - } - case *ast.ArrayType: - if t.Len == nil { - size = arch.ptrSize + 2*arch.intSize - align = arch.ptrSize - kind = asmSlice - break - } - f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) - failed = true - case *ast.StructType: - f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) - failed = true - } - if align == 0 { - align = size - } - if kind == 0 { - kind = asmKind(size) - } - offset += -offset & (align - 1) - - // Create variable for each name being declared with this type. - if len(names) == 0 { - name := "unnamed" - if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 { - // Assume assembly will refer to single unnamed result as r. - name = "ret" - } - names = []*ast.Ident{{Name: name}} - } - for _, id := range names { - name := id.Name - addVar("", asmVar{ - name: name, - kind: kind, - typ: typ, - off: offset, - size: size, - }) - switch kind { - case 8: - if arch.ptrSize == 4 { - w1, w2 := "lo", "hi" - if arch.bigEndian { - w1, w2 = w2, w1 - } - addVar(name, asmVar{ - name: name + "_" + w1, - kind: 4, - typ: "half " + typ, - off: offset, - size: 4, - }) - addVar(name, asmVar{ - name: name + "_" + w2, - kind: 4, - typ: "half " + typ, - off: offset + 4, - size: 4, - }) - } - - case asmEmptyInterface: - addVar(name, asmVar{ - name: name + "_type", - kind: asmKind(arch.ptrSize), - typ: "interface type", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_data", - kind: asmKind(arch.ptrSize), - typ: "interface data", - off: offset + arch.ptrSize, - size: arch.ptrSize, - }) - - case asmInterface: - addVar(name, asmVar{ - name: name + "_itable", - kind: asmKind(arch.ptrSize), - typ: "interface itable", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_data", - kind: asmKind(arch.ptrSize), - typ: "interface data", - off: offset + arch.ptrSize, - size: arch.ptrSize, - }) - - case asmSlice: - addVar(name, asmVar{ - name: name + "_base", - kind: asmKind(arch.ptrSize), - typ: "slice base", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_len", - kind: asmKind(arch.intSize), - typ: "slice len", - off: offset + arch.ptrSize, - size: arch.intSize, - }) - addVar(name, asmVar{ - name: name + "_cap", - kind: asmKind(arch.intSize), - typ: "slice cap", - off: offset + arch.ptrSize + arch.intSize, - size: arch.intSize, - }) - - case asmString: - addVar(name, asmVar{ - name: name + "_base", - kind: asmKind(arch.ptrSize), - typ: "string base", - off: offset, - size: arch.ptrSize, - }) - addVar(name, asmVar{ - name: name + "_len", - kind: asmKind(arch.intSize), - typ: "string len", - off: offset + arch.ptrSize, - size: arch.intSize, - }) - } - offset += size - } - } - } - - m := make(map[string]*asmFunc) - for _, arch = range arches { - fn = &asmFunc{ - arch: arch, - vars: make(map[string]*asmVar), - varByOffset: make(map[int]*asmVar), - } - offset = 0 - addParams(decl.Type.Params.List) - if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { - offset += -offset & (arch.maxAlign - 1) - addParams(decl.Type.Results.List) - } - fn.size = offset - m[arch.name] = fn - } - - if failed { - return nil - } - return m -} - -// asmCheckVar checks a single variable reference. -func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) { - m := asmOpcode.FindStringSubmatch(line) - if m == nil { - if !strings.HasPrefix(strings.TrimSpace(line), "//") { - badf("cannot find assembly opcode") - } - return - } - - // Determine operand sizes from instruction. - // Typically the suffix suffices, but there are exceptions. - var src, dst, kind asmKind - op := m[1] - switch fn.arch.name + "." + op { - case "386.FMOVLP": - src, dst = 8, 4 - case "arm.MOVD": - src = 8 - case "arm.MOVW": - src = 4 - case "arm.MOVH", "arm.MOVHU": - src = 2 - case "arm.MOVB", "arm.MOVBU": - src = 1 - // LEA* opcodes don't really read the second arg. - // They just take the address of it. - case "386.LEAL": - dst = 4 - case "amd64.LEAQ": - dst = 8 - case "amd64p32.LEAL": - dst = 4 - default: - switch fn.arch.name { - case "386", "amd64": - if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) { - // FMOVDP, FXCHD, etc - src = 8 - break - } - if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) { - // FMOVFP, FXCHF, etc - src = 4 - break - } - if strings.HasSuffix(op, "SD") { - // MOVSD, SQRTSD, etc - src = 8 - break - } - if strings.HasSuffix(op, "SS") { - // MOVSS, SQRTSS, etc - src = 4 - break - } - if strings.HasPrefix(op, "SET") { - // SETEQ, etc - src = 1 - break - } - switch op[len(op)-1] { - case 'B': - src = 1 - case 'W': - src = 2 - case 'L': - src = 4 - case 'D', 'Q': - src = 8 - } - case "power64", "power64le": - // Strip standard suffixes to reveal size letter. - m := power64Suff.FindStringSubmatch(op) - if m != nil { - switch m[1][0] { - case 'B': - src = 1 - case 'H': - src = 2 - case 'W': - src = 4 - case 'D': - src = 8 - } - } - } - } - if dst == 0 { - dst = src - } - - // Determine whether the match we're holding - // is the first or second argument. - if strings.Index(line, expr) > strings.Index(line, ",") { - kind = dst - } else { - kind = src - } - - vk := v.kind - vt := v.typ - switch vk { - case asmInterface, asmEmptyInterface, asmString, asmSlice: - // allow reference to first word (pointer) - vk = v.inner[0].kind - vt = v.inner[0].typ - } - - if off != v.off { - var inner bytes.Buffer - for i, vi := range v.inner { - if len(v.inner) > 1 { - fmt.Fprintf(&inner, ",") - } - fmt.Fprintf(&inner, " ") - if i == len(v.inner)-1 { - fmt.Fprintf(&inner, "or ") - } - fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) - } - badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String()) - return - } - if kind != 0 && kind != vk { - var inner bytes.Buffer - if len(v.inner) > 0 { - fmt.Fprintf(&inner, " containing") - for i, vi := range v.inner { - if i > 0 && len(v.inner) > 2 { - fmt.Fprintf(&inner, ",") - } - fmt.Fprintf(&inner, " ") - if i > 0 && i == len(v.inner)-1 { - fmt.Fprintf(&inner, "and ") - } - fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) - } - } - badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String()) - } -} diff --git a/cmd/vet/assign.go b/cmd/vet/assign.go deleted file mode 100644 index 54c1ae1fdc..0000000000 --- a/cmd/vet/assign.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -/* -This file contains the code to check for useless assignments. -*/ - -package main - -import ( - "go/ast" - "go/token" - "reflect" -) - -func init() { - register("assign", - "check for useless assignments", - checkAssignStmt, - assignStmt) -} - -// TODO: should also check for assignments to struct fields inside methods -// that are on T instead of *T. - -// checkAssignStmt checks for assignments of the form " = ". -// These are almost always useless, and even when they aren't they are usually a mistake. -func checkAssignStmt(f *File, node ast.Node) { - stmt := node.(*ast.AssignStmt) - if stmt.Tok != token.ASSIGN { - return // ignore := - } - if len(stmt.Lhs) != len(stmt.Rhs) { - // If LHS and RHS have different cardinality, they can't be the same. - return - } - for i, lhs := range stmt.Lhs { - rhs := stmt.Rhs[i] - if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { - continue // short-circuit the heavy-weight gofmt check - } - le := f.gofmt(lhs) - re := f.gofmt(rhs) - if le == re { - f.Badf(stmt.Pos(), "self-assignment of %s to %s", re, le) - } - } -} diff --git a/cmd/vet/atomic.go b/cmd/vet/atomic.go deleted file mode 100644 index c084f13ab3..0000000000 --- a/cmd/vet/atomic.go +++ /dev/null @@ -1,66 +0,0 @@ -// 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 main - -import ( - "go/ast" - "go/token" -) - -func init() { - register("atomic", - "check for common mistaken usages of the sync/atomic package", - checkAtomicAssignment, - assignStmt) -} - -// checkAtomicAssignment walks the assignment statement checking for common -// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) -func checkAtomicAssignment(f *File, node ast.Node) { - n := node.(*ast.AssignStmt) - if len(n.Lhs) != len(n.Rhs) { - return - } - - for i, right := range n.Rhs { - call, ok := right.(*ast.CallExpr) - if !ok { - continue - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - continue - } - pkg, ok := sel.X.(*ast.Ident) - if !ok || pkg.Name != "atomic" { - continue - } - - switch sel.Sel.Name { - case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": - f.checkAtomicAddAssignment(n.Lhs[i], call) - } - } -} - -// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value -// to the same variable being used in the operation -func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { - if len(call.Args) != 2 { - return - } - arg := call.Args[0] - broken := false - - if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { - broken = f.gofmt(left) == f.gofmt(uarg.X) - } else if star, ok := left.(*ast.StarExpr); ok { - broken = f.gofmt(star.X) == f.gofmt(arg) - } - - if broken { - f.Bad(left.Pos(), "direct assignment to atomic value") - } -} diff --git a/cmd/vet/bool.go b/cmd/vet/bool.go deleted file mode 100644 index 07c2a93dff..0000000000 --- a/cmd/vet/bool.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2014 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. - -// This file contains boolean condition tests. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register("bool", - "check for mistakes involving boolean operators", - checkBool, - binaryExpr) -} - -func checkBool(f *File, n ast.Node) { - e := n.(*ast.BinaryExpr) - - var op boolOp - switch e.Op { - case token.LOR: - op = or - case token.LAND: - op = and - default: - return - } - - comm := op.commutativeSets(e) - for _, exprs := range comm { - op.checkRedundant(f, exprs) - op.checkSuspect(f, exprs) - } -} - -type boolOp struct { - name string - tok token.Token // token corresponding to this operator - badEq token.Token // token corresponding to the equality test that should not be used with this operator -} - -var ( - or = boolOp{"or", token.LOR, token.NEQ} - and = boolOp{"and", token.LAND, token.EQL} -) - -// commutativeSets returns all side effect free sets of -// expressions in e that are connected by op. -// For example, given 'a || b || f() || c || d' with the or op, -// commutativeSets returns {{b, a}, {d, c}}. -func (op boolOp) commutativeSets(e *ast.BinaryExpr) [][]ast.Expr { - exprs := op.split(e) - - // Partition the slice of expressions into commutative sets. - i := 0 - var sets [][]ast.Expr - for j := 0; j <= len(exprs); j++ { - if j == len(exprs) || hasSideEffects(exprs[j]) { - if i < j { - sets = append(sets, exprs[i:j]) - } - i = j + 1 - } - } - - return sets -} - -// checkRedundant checks for expressions of the form -// e && e -// e || e -// Exprs must contain only side effect free expressions. -func (op boolOp) checkRedundant(f *File, exprs []ast.Expr) { - seen := make(map[string]bool) - for _, e := range exprs { - efmt := f.gofmt(e) - if seen[efmt] { - f.Badf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt) - } else { - seen[efmt] = true - } - } -} - -// checkSuspect checks for expressions of the form -// x != c1 || x != c2 -// x == c1 && x == c2 -// where c1 and c2 are constant expressions. -// If c1 and c2 are the same then it's redundant; -// if c1 and c2 are different then it's always true or always false. -// Exprs must contain only side effect free expressions. -func (op boolOp) checkSuspect(f *File, exprs []ast.Expr) { - // seen maps from expressions 'x' to equality expressions 'x != c'. - seen := make(map[string]string) - - for _, e := range exprs { - bin, ok := e.(*ast.BinaryExpr) - if !ok || bin.Op != op.badEq { - continue - } - - // In order to avoid false positives, restrict to cases - // in which one of the operands is constant. We're then - // interested in the other operand. - // In the rare case in which both operands are constant - // (e.g. runtime.GOOS and "windows"), we'll only catch - // mistakes if the LHS is repeated, which is how most - // code is written. - var x ast.Expr - switch { - case f.pkg.types[bin.Y].Value != nil: - x = bin.X - case f.pkg.types[bin.X].Value != nil: - x = bin.Y - default: - continue - } - - // e is of the form 'x != c' or 'x == c'. - xfmt := f.gofmt(x) - efmt := f.gofmt(e) - if prev, found := seen[xfmt]; found { - // checkRedundant handles the case in which efmt == prev. - if efmt != prev { - f.Badf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev) - } - } else { - seen[xfmt] = efmt - } - } -} - -// hasSideEffects reports whether evaluation of e has side effects. -func hasSideEffects(e ast.Expr) bool { - safe := true - ast.Inspect(e, func(node ast.Node) bool { - switch n := node.(type) { - // Using CallExpr here will catch conversions - // as well as function and method invocations. - // We'll live with the false negatives for now. - case *ast.CallExpr: - safe = false - return false - case *ast.UnaryExpr: - if n.Op == token.ARROW { - safe = false - return false - } - } - return true - }) - return !safe -} - -// split returns a slice of all subexpressions in e that are connected by op. -// For example, given 'a || (b || c) || d' with the or op, -// split returns []{d, c, b, a}. -func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) { - for { - e = unparen(e) - if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok { - exprs = append(exprs, op.split(b.Y)...) - e = b.X - } else { - exprs = append(exprs, e) - break - } - } - return -} - -// unparen returns e with any enclosing parentheses stripped. -func unparen(e ast.Expr) ast.Expr { - for { - p, ok := e.(*ast.ParenExpr) - if !ok { - return e - } - e = p.X - } -} diff --git a/cmd/vet/buildtag.go b/cmd/vet/buildtag.go deleted file mode 100644 index 2d86edf734..0000000000 --- a/cmd/vet/buildtag.go +++ /dev/null @@ -1,91 +0,0 @@ -// 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 main - -import ( - "bytes" - "fmt" - "os" - "strings" - "unicode" -) - -var ( - nl = []byte("\n") - slashSlash = []byte("//") - plusBuild = []byte("+build") -) - -// checkBuildTag checks that build tags are in the correct location and well-formed. -func checkBuildTag(name string, data []byte) { - if !vet("buildtags") { - return - } - lines := bytes.SplitAfter(data, nl) - - // Determine cutpoint where +build comments are no longer valid. - // They are valid in leading // comments in the file followed by - // a blank line. - var cutoff int - for i, line := range lines { - line = bytes.TrimSpace(line) - if len(line) == 0 { - cutoff = i - continue - } - if bytes.HasPrefix(line, slashSlash) { - continue - } - break - } - - for i, line := range lines { - line = bytes.TrimSpace(line) - if !bytes.HasPrefix(line, slashSlash) { - continue - } - text := bytes.TrimSpace(line[2:]) - if bytes.HasPrefix(text, plusBuild) { - fields := bytes.Fields(text) - if !bytes.Equal(fields[0], plusBuild) { - // Comment is something like +buildasdf not +build. - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - continue - } - if i >= cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: +build comment must appear before package clause and be followed by a blank line\n", name, i+1) - setExit(1) - continue - } - // Check arguments. - Args: - for _, arg := range fields[1:] { - for _, elem := range strings.Split(string(arg), ",") { - if strings.HasPrefix(elem, "!!") { - fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) - setExit(1) - break Args - } - if strings.HasPrefix(elem, "!") { - elem = elem[1:] - } - for _, c := range elem { - if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { - fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) - setExit(1) - break Args - } - } - } - } - continue - } - // Comment with +build but not at beginning. - if bytes.Contains(line, plusBuild) && i < cutoff { - fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) - continue - } - } -} diff --git a/cmd/vet/composite.go b/cmd/vet/composite.go deleted file mode 100644 index 0c3f916558..0000000000 --- a/cmd/vet/composite.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2012 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. - -// This file contains the test for unkeyed struct literals. - -package main - -import ( - "flag" - "go/ast" - "strings" - - "golang.org/x/tools/cmd/vet/whitelist" -) - -var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") - -func init() { - register("composites", - "check that composite literals used field-keyed elements", - checkUnkeyedLiteral, - compositeLit) -} - -// checkUnkeyedLiteral checks if a composite literal is a struct literal with -// unkeyed fields. -func checkUnkeyedLiteral(f *File, node ast.Node) { - c := node.(*ast.CompositeLit) - typ := c.Type - for { - if typ1, ok := c.Type.(*ast.ParenExpr); ok { - typ = typ1 - continue - } - break - } - - switch typ.(type) { - case *ast.ArrayType: - return - case *ast.MapType: - return - case *ast.StructType: - return // a literal struct type does not need to use keys - case *ast.Ident: - // A simple type name like t or T does not need keys either, - // since it is almost certainly declared in the current package. - // (The exception is names being used via import . "pkg", but - // those are already breaking the Go 1 compatibility promise, - // so not reporting potential additional breakage seems okay.) - return - } - - // Otherwise the type is a selector like pkg.Name. - // We only care if pkg.Name is a struct, not if it's a map, array, or slice. - isStruct, typeString := f.pkg.isStruct(c) - if !isStruct { - return - } - - if typeString == "" { // isStruct doesn't know - typeString = f.gofmt(typ) - } - - // It's a struct, or we can't tell it's not a struct because we don't have types. - - // Check if the CompositeLit contains an unkeyed field. - allKeyValue := true - for _, e := range c.Elts { - if _, ok := e.(*ast.KeyValueExpr); !ok { - allKeyValue = false - break - } - } - if allKeyValue { - return - } - - // Check that the CompositeLit's type has the form pkg.Typ. - s, ok := c.Type.(*ast.SelectorExpr) - if !ok { - return - } - pkg, ok := s.X.(*ast.Ident) - if !ok { - return - } - - // Convert the package name to an import path, and compare to a whitelist. - path := pkgPath(f, pkg.Name) - if path == "" { - f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) - return - } - typeName := path + "." + s.Sel.Name - if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] { - return - } - - f.Bad(c.Pos(), typeString+" composite literal uses unkeyed fields") -} - -// pkgPath returns the import path "image/png" for the package name "png". -// -// This is based purely on syntax and convention, and not on the imported -// package's contents. It will be incorrect if a package name differs from the -// leaf element of the import path, or if the package was a dot import. -func pkgPath(f *File, pkgName string) (path string) { - for _, x := range f.file.Imports { - s := strings.Trim(x.Path.Value, `"`) - if x.Name != nil { - // Catch `import pkgName "foo/bar"`. - if x.Name.Name == pkgName { - return s - } - } else { - // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. - if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { - return s - } - } - } - return "" -} diff --git a/cmd/vet/copylock.go b/cmd/vet/copylock.go deleted file mode 100644 index e8a6820fce..0000000000 --- a/cmd/vet/copylock.go +++ /dev/null @@ -1,155 +0,0 @@ -// 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. - -// This file contains the code to check that locks are not passed by value. - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -func init() { - register("copylocks", - "check that locks are not passed by value", - checkCopyLocks, - funcDecl, rangeStmt) -} - -// checkCopyLocks checks whether node might -// inadvertently copy a lock. -func checkCopyLocks(f *File, node ast.Node) { - switch node := node.(type) { - case *ast.RangeStmt: - checkCopyLocksRange(f, node) - case *ast.FuncDecl: - checkCopyLocksFunc(f, node) - } -} - -// checkCopyLocksFunc checks whether a function might -// inadvertently copy a lock, by checking whether -// its receiver, parameters, or return values -// are locks. -func checkCopyLocksFunc(f *File, d *ast.FuncDecl) { - if d.Recv != nil && len(d.Recv.List) > 0 { - expr := d.Recv.List[0].Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { - f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path) - } - } - - if d.Type.Params != nil { - for _, field := range d.Type.Params.List { - expr := field.Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { - f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path) - } - } - } - - if d.Type.Results != nil { - for _, field := range d.Type.Results.List { - expr := field.Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { - f.Badf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path) - } - } - } -} - -// checkCopyLocksRange checks whether a range statement -// might inadvertently copy a lock by checking whether -// any of the range variables are locks. -func checkCopyLocksRange(f *File, r *ast.RangeStmt) { - checkCopyLocksRangeVar(f, r.Tok, r.Key) - checkCopyLocksRangeVar(f, r.Tok, r.Value) -} - -func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) { - if e == nil { - return - } - id, isId := e.(*ast.Ident) - if isId && id.Name == "_" { - return - } - - var typ types.Type - if rtok == token.DEFINE { - if !isId { - return - } - obj := f.pkg.defs[id] - if obj == nil { - return - } - typ = obj.Type() - } else { - typ = f.pkg.types[e].Type - } - - if typ == nil { - return - } - if path := lockPath(f.pkg.typesPkg, typ); path != nil { - f.Badf(e.Pos(), "range var %s copies Lock: %v", f.gofmt(e), path) - } -} - -type typePath []types.Type - -// String pretty-prints a typePath. -func (path typePath) String() string { - n := len(path) - var buf bytes.Buffer - for i := range path { - if i > 0 { - fmt.Fprint(&buf, " contains ") - } - // The human-readable path is in reverse order, outermost to innermost. - fmt.Fprint(&buf, path[n-i-1].String()) - } - return buf.String() -} - -// lockPath returns a typePath describing the location of a lock value -// contained in typ. If there is no contained lock, it returns nil. -func lockPath(tpkg *types.Package, typ types.Type) typePath { - if typ == nil { - return nil - } - - // We're only interested in the case in which the underlying - // type is a struct. (Interfaces and pointers are safe to copy.) - styp, ok := typ.Underlying().(*types.Struct) - if !ok { - return nil - } - - // We're looking for cases in which a reference to this type - // can be locked, but a value cannot. This differentiates - // embedded interfaces from embedded values. - if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil { - if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil { - return []types.Type{typ} - } - } - - nfields := styp.NumFields() - for i := 0; i < nfields; i++ { - ftyp := styp.Field(i).Type() - subpath := lockPath(tpkg, ftyp) - if subpath != nil { - return append(subpath, typ) - } - } - - return nil -} diff --git a/cmd/vet/deadcode.go b/cmd/vet/deadcode.go deleted file mode 100644 index 3b306c2104..0000000000 --- a/cmd/vet/deadcode.go +++ /dev/null @@ -1,296 +0,0 @@ -// 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. - -// Check for syntactically unreachable code. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register("unreachable", - "check for unreachable code", - checkUnreachable, - funcDecl, funcLit) -} - -type deadState struct { - f *File - hasBreak map[ast.Stmt]bool - hasGoto map[string]bool - labels map[string]ast.Stmt - breakTarget ast.Stmt - - reachable bool -} - -// checkUnreachable checks a function body for dead code. -func checkUnreachable(f *File, node ast.Node) { - var body *ast.BlockStmt - switch n := node.(type) { - case *ast.FuncDecl: - body = n.Body - case *ast.FuncLit: - body = n.Body - } - if body == nil { - return - } - - d := &deadState{ - f: f, - hasBreak: make(map[ast.Stmt]bool), - hasGoto: make(map[string]bool), - labels: make(map[string]ast.Stmt), - } - - d.findLabels(body) - - d.reachable = true - d.findDead(body) -} - -// findLabels gathers information about the labels defined and used by stmt -// and about which statements break, whether a label is involved or not. -func (d *deadState) findLabels(stmt ast.Stmt) { - switch x := stmt.(type) { - default: - d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x) - - case *ast.AssignStmt, - *ast.BadStmt, - *ast.DeclStmt, - *ast.DeferStmt, - *ast.EmptyStmt, - *ast.ExprStmt, - *ast.GoStmt, - *ast.IncDecStmt, - *ast.ReturnStmt, - *ast.SendStmt: - // no statements inside - - case *ast.BlockStmt: - for _, stmt := range x.List { - d.findLabels(stmt) - } - - case *ast.BranchStmt: - switch x.Tok { - case token.GOTO: - if x.Label != nil { - d.hasGoto[x.Label.Name] = true - } - - case token.BREAK: - stmt := d.breakTarget - if x.Label != nil { - stmt = d.labels[x.Label.Name] - } - if stmt != nil { - d.hasBreak[stmt] = true - } - } - - case *ast.IfStmt: - d.findLabels(x.Body) - if x.Else != nil { - d.findLabels(x.Else) - } - - case *ast.LabeledStmt: - d.labels[x.Label.Name] = x.Stmt - d.findLabels(x.Stmt) - - // These cases are all the same, but the x.Body only works - // when the specific type of x is known, so the cases cannot - // be merged. - case *ast.ForStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.RangeStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.SelectStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.SwitchStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.TypeSwitchStmt: - outer := d.breakTarget - d.breakTarget = x - d.findLabels(x.Body) - d.breakTarget = outer - - case *ast.CommClause: - for _, stmt := range x.Body { - d.findLabels(stmt) - } - - case *ast.CaseClause: - for _, stmt := range x.Body { - d.findLabels(stmt) - } - } -} - -// findDead walks the statement looking for dead code. -// If d.reachable is false on entry, stmt itself is dead. -// When findDead returns, d.reachable tells whether the -// statement following stmt is reachable. -func (d *deadState) findDead(stmt ast.Stmt) { - // Is this a labeled goto target? - // If so, assume it is reachable due to the goto. - // This is slightly conservative, in that we don't - // check that the goto is reachable, so - // L: goto L - // will not provoke a warning. - // But it's good enough. - if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] { - d.reachable = true - } - - if !d.reachable { - switch stmt.(type) { - case *ast.EmptyStmt: - // do not warn about unreachable empty statements - default: - d.f.Bad(stmt.Pos(), "unreachable code") - d.reachable = true // silence error about next statement - } - } - - switch x := stmt.(type) { - default: - d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x) - - case *ast.AssignStmt, - *ast.BadStmt, - *ast.DeclStmt, - *ast.DeferStmt, - *ast.EmptyStmt, - *ast.GoStmt, - *ast.IncDecStmt, - *ast.SendStmt: - // no control flow - - case *ast.BlockStmt: - for _, stmt := range x.List { - d.findDead(stmt) - } - - case *ast.BranchStmt: - switch x.Tok { - case token.BREAK, token.GOTO, token.FALLTHROUGH: - d.reachable = false - case token.CONTINUE: - // NOTE: We accept "continue" statements as terminating. - // They are not necessary in the spec definition of terminating, - // because a continue statement cannot be the final statement - // before a return. But for the more general problem of syntactically - // identifying dead code, continue redirects control flow just - // like the other terminating statements. - d.reachable = false - } - - case *ast.ExprStmt: - // Call to panic? - call, ok := x.X.(*ast.CallExpr) - if ok { - name, ok := call.Fun.(*ast.Ident) - if ok && name.Name == "panic" && name.Obj == nil { - d.reachable = false - } - } - - case *ast.ForStmt: - d.findDead(x.Body) - d.reachable = x.Cond != nil || d.hasBreak[x] - - case *ast.IfStmt: - d.findDead(x.Body) - if x.Else != nil { - r := d.reachable - d.reachable = true - d.findDead(x.Else) - d.reachable = d.reachable || r - } else { - // might not have executed if statement - d.reachable = true - } - - case *ast.LabeledStmt: - d.findDead(x.Stmt) - - case *ast.RangeStmt: - d.findDead(x.Body) - d.reachable = true - - case *ast.ReturnStmt: - d.reachable = false - - case *ast.SelectStmt: - // NOTE: Unlike switch and type switch below, we don't care - // whether a select has a default, because a select without a - // default blocks until one of the cases can run. That's different - // from a switch without a default, which behaves like it has - // a default with an empty body. - anyReachable := false - for _, comm := range x.Body.List { - d.reachable = true - for _, stmt := range comm.(*ast.CommClause).Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] - - case *ast.SwitchStmt: - anyReachable := false - hasDefault := false - for _, cas := range x.Body.List { - cc := cas.(*ast.CaseClause) - if cc.List == nil { - hasDefault = true - } - d.reachable = true - for _, stmt := range cc.Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] || !hasDefault - - case *ast.TypeSwitchStmt: - anyReachable := false - hasDefault := false - for _, cas := range x.Body.List { - cc := cas.(*ast.CaseClause) - if cc.List == nil { - hasDefault = true - } - d.reachable = true - for _, stmt := range cc.Body { - d.findDead(stmt) - } - anyReachable = anyReachable || d.reachable - } - d.reachable = anyReachable || d.hasBreak[x] || !hasDefault - } -} diff --git a/cmd/vet/doc.go b/cmd/vet/doc.go deleted file mode 100644 index 012f88b38c..0000000000 --- a/cmd/vet/doc.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2010 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. - -/* - -Vet examines Go source code and reports suspicious constructs, such as Printf -calls whose arguments do not align with the format string. Vet uses heuristics -that do not guarantee all reports are genuine problems, but it can find errors -not caught by the compilers. - -It can be invoked three ways: - -By package, from the go tool: - go vet package/path/name -vets the package whose path is provided. - -By files: - go tool vet source/directory/*.go -vets the files named, all of which must be in the same package. - -By directory: - go tool vet source/directory -recursively descends the directory, vetting each package it finds. - -Vet's exit code is 2 for erroneous invocation of the tool, 1 if a -problem was reported, and 0 otherwise. Note that the tool does not -check every possible problem and depends on unreliable heuristics -so it should be used as guidance only, not as a firm indicator of -program correctness. - -By default all checks are performed. If any flags are explicitly set -to true, only those tests are run. Conversely, if any flag is -explicitly set to false, only those tests are disabled. -Thus -printf=true runs the printf check, -printf=false runs all checks -except the printf check. - -Available checks: - -Assembly declarations - -Flag: -asmdecl - -Mismatches between assembly files and Go function declarations. - -Useless assignments - -Flag: -assign - -Check for useless assignments. - -Atomic mistakes - -Flag: -atomic - -Common mistaken usages of the sync/atomic package. - -Boolean conditions - -Flag: -bool - -Mistakes involving boolean operators. - -Build tags - -Flag: -buildtags - -Badly formed or misplaced +build tags. - -Unkeyed composite literals - -Flag: -composites - -Composite struct literals that do not use the field-keyed syntax. - -Copying locks - -Flag: -copylocks - -Locks that are erroneously passed by value. - -Documentation examples - -Flag: -example - -Mistakes involving example tests, including examples with incorrect names or -function signatures, or that document identifiers not in the package. - -Methods - -Flag: -methods - -Non-standard signatures for methods with familiar names, including: - Format GobEncode GobDecode MarshalJSON MarshalXML - Peek ReadByte ReadFrom ReadRune Scan Seek - UnmarshalJSON UnreadByte UnreadRune WriteByte - WriteTo - -Nil function comparison - -Flag: -nilfunc - -Comparisons between functions and nil. - -Printf family - -Flag: -printf - -Suspicious calls to functions in the Printf family, including any functions -with these names, disregarding case: - Print Printf Println - Fprint Fprintf Fprintln - Sprint Sprintf Sprintln - Error Errorf - Fatal Fatalf - Log Logf - Panic Panicf Panicln -If the function name ends with an 'f', the function is assumed to take -a format descriptor string in the manner of fmt.Printf. If not, vet -complains about arguments that look like format descriptor strings. - -It also checks for errors such as using a Writer as the first argument of -Printf. - -Range loop variables - -Flag: -rangeloops - -Incorrect uses of range loop variables in closures. - -Shadowed variables - -Flag: -shadow=false (experimental; must be set explicitly) - -Variables that may have been unintentionally shadowed. - -Shifts - -Flag: -shift - -Shifts equal to or longer than the variable's length. - -Struct tags - -Flag: -structtags - -Struct tags that do not follow the format understood by reflect.StructTag.Get. -Well-known encoding struct tags (json, xml) used with unexported fields. - -Unreachable code - -Flag: -unreachable - -Unreachable code. - -Misuse of unsafe Pointers - -Flag: -unsafeptr - -Likely incorrect uses of unsafe.Pointer to convert integers to pointers. -A conversion from uintptr to unsafe.Pointer is invalid if it implies that -there is a uintptr-typed word in memory that holds a pointer value, -because that word will be invisible to stack copying and to the garbage -collector. - -Unused result of certain function calls - -Flag: -unusedresult - -Calls to well-known functions and methods that return a value that is -discarded. By default, this includes functions like fmt.Errorf and -fmt.Sprintf and methods like String and Error. The flags -unusedfuncs -and -unusedstringmethods control the set. - -Other flags - -These flags configure the behavior of vet: - - -all (default true) - Check everything; disabled if any explicit check is requested. - -v - Verbose mode - -printfuncs - A comma-separated list of print-like functions to supplement - the standard list. Each entry is in the form Name:N where N - is the zero-based argument position of the first argument - involved in the print: either the format or the first print - argument for non-formatted prints. For example, - if you have Warn and Warnf functions that take an - io.Writer as their first argument, like Fprintf, - -printfuncs=Warn:1,Warnf:1 - -shadowstrict - Whether to be strict about shadowing; can be noisy. - -test - For testing only: sets -all and -shadow. -*/ -package main // import "golang.org/x/tools/cmd/vet" diff --git a/cmd/vet/example.go b/cmd/vet/example.go deleted file mode 100644 index 585d38844d..0000000000 --- a/cmd/vet/example.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2015 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 main - -import ( - "go/ast" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/tools/go/types" -) - -func init() { - register("example", - "check for common mistaken usages of documentation examples", - checkExample, - funcDecl) -} - -func isExampleSuffix(s string) bool { - r, size := utf8.DecodeRuneInString(s) - return size > 0 && unicode.IsLower(r) -} - -// checkExample walks the documentation example functions checking for common -// mistakes of misnamed functions, failure to map functions to existing -// identifiers, etc. -func checkExample(f *File, node ast.Node) { - if !strings.HasSuffix(f.name, "_test.go") { - return - } - var ( - pkg = f.pkg - pkgName = pkg.typesPkg.Name() - scopes = []*types.Scope{pkg.typesPkg.Scope()} - lookup = func(name string) types.Object { - for _, scope := range scopes { - if o := scope.Lookup(name); o != nil { - return o - } - } - return nil - } - ) - if strings.HasSuffix(pkgName, "_test") { - // Treat 'package foo_test' as an alias for 'package foo'. - var ( - basePkg = strings.TrimSuffix(pkgName, "_test") - pkg = f.pkg - ) - for _, p := range pkg.typesPkg.Imports() { - if p.Name() == basePkg { - scopes = append(scopes, p.Scope()) - break - } - } - } - fn, ok := node.(*ast.FuncDecl) - if !ok { - // Ignore non-functions. - return - } - var ( - fnName = fn.Name.Name - report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) } - ) - if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") { - // Ignore methods and types not named "Example". - return - } - if params := fn.Type.Params; len(params.List) != 0 { - report("%s should be niladic", fnName) - } - if results := fn.Type.Results; results != nil && len(results.List) != 0 { - report("%s should return nothing", fnName) - } - if fnName == "Example" { - // Nothing more to do. - return - } - if filesRun && !includesNonTest { - // The coherence checks between a test and the package it tests - // will report false positives if no non-test files have - // been provided. - return - } - var ( - exName = strings.TrimPrefix(fnName, "Example") - elems = strings.SplitN(exName, "_", 3) - ident = elems[0] - obj = lookup(ident) - ) - if ident != "" && obj == nil { - // Check ExampleFoo and ExampleBadFoo. - report("%s refers to unknown identifier: %s", fnName, ident) - // Abort since obj is absent and no subsequent checks can be performed. - return - } - if elemCnt := strings.Count(exName, "_"); elemCnt == 0 { - // Nothing more to do. - return - } - mmbr := elems[1] - if ident == "" { - // Check Example_suffix and Example_BadSuffix. - if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) { - report("%s has malformed example suffix: %s", fnName, residual) - } - return - } - if !isExampleSuffix(mmbr) { - // Check ExampleFoo_Method and ExampleFoo_BadMethod. - if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil { - report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr) - } - } - if len(elems) == 3 && !isExampleSuffix(elems[2]) { - // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix. - report("%s has malformed example suffix: %s", fnName, elems[2]) - } - return -} diff --git a/cmd/vet/main.go b/cmd/vet/main.go deleted file mode 100644 index c86e13c472..0000000000 --- a/cmd/vet/main.go +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2010 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. - -// NOTE: This version of vet is retired. Bug fixes only. -// Vet now lives in the core repository. - -// Vet is a simple checker for static errors in Go source code. -// See doc.go for more information. -package main - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/printer" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - - _ "golang.org/x/tools/go/gcimporter" - "golang.org/x/tools/go/types" -) - -var ( - verbose = flag.Bool("v", false, "verbose") - testFlag = flag.Bool("test", false, "for testing only: sets -all and -shadow") - tags = flag.String("tags", "", "comma-separated list of build tags to apply when parsing") - tagList = []string{} // exploded version of tags flag; set in main -) - -var exitCode = 0 - -// "all" is here only for the appearance of backwards compatibility. -// It has no effect; the triState flags do the work. -var all = flag.Bool("all", true, "check everything; disabled if any explicit check is requested") - -// Flags to control which individual checks to perform. -var report = map[string]*triState{ - // Only unusual checks are written here. - // Most checks that operate during the AST walk are added by register. - "asmdecl": triStateFlag("asmdecl", unset, "check assembly against Go declarations"), - "buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"), -} - -// experimental records the flags enabling experimental features. These must be -// requested explicitly; they are not enabled by -all. -var experimental = map[string]bool{} - -// setTrueCount record how many flags are explicitly set to true. -var setTrueCount int - -// dirsRun and filesRun indicate whether the vet is applied to directory or -// file targets. The distinction affects which checks are run. -var dirsRun, filesRun bool - -// includesNonTest indicates whether the vet is applied to non-test targets. -// Certain checks are relevant only if they touch both test and non-test files. -var includesNonTest bool - -// A triState is a boolean that knows whether it has been set to either true or false. -// It is used to identify if a flag appears; the standard boolean flag cannot -// distinguish missing from unset. It also satisfies flag.Value. -type triState int - -const ( - unset triState = iota - setTrue - setFalse -) - -func triStateFlag(name string, value triState, usage string) *triState { - flag.Var(&value, name, usage) - return &value -} - -// triState implements flag.Value, flag.Getter, and flag.boolFlag. -// They work like boolean flags: we can say vet -printf as well as vet -printf=true -func (ts *triState) Get() interface{} { - return *ts == setTrue -} - -func (ts triState) isTrue() bool { - return ts == setTrue -} - -func (ts *triState) Set(value string) error { - b, err := strconv.ParseBool(value) - if err != nil { - return err - } - if b { - *ts = setTrue - setTrueCount++ - } else { - *ts = setFalse - } - return nil -} - -func (ts *triState) String() string { - switch *ts { - case unset: - return "unset" - case setTrue: - return "true" - case setFalse: - return "false" - } - panic("not reached") -} - -func (ts triState) IsBoolFlag() bool { - return true -} - -// vet tells whether to report errors for the named check, a flag name. -func vet(name string) bool { - if *testFlag { - return true - } - return report[name].isTrue() -} - -// setExit sets the value for os.Exit when it is called, later. It -// remembers the highest value. -func setExit(err int) { - if err > exitCode { - exitCode = err - } -} - -var ( - // Each of these vars has a corresponding case in (*File).Visit. - assignStmt *ast.AssignStmt - binaryExpr *ast.BinaryExpr - callExpr *ast.CallExpr - compositeLit *ast.CompositeLit - exprStmt *ast.ExprStmt - field *ast.Field - funcDecl *ast.FuncDecl - funcLit *ast.FuncLit - genDecl *ast.GenDecl - interfaceType *ast.InterfaceType - rangeStmt *ast.RangeStmt - - // checkers is a two-level map. - // The outer level is keyed by a nil pointer, one of the AST vars above. - // The inner level is keyed by checker name. - checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) -) - -func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { - report[name] = triStateFlag(name, unset, usage) - for _, typ := range types { - m := checkers[typ] - if m == nil { - m = make(map[string]func(*File, ast.Node)) - checkers[typ] = m - } - m[name] = fn - } -} - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") - fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") - fmt.Fprintf(os.Stderr, "For more information run\n") - fmt.Fprintf(os.Stderr, "\tgodoc golang.org/x/tools/cmd/vet\n\n") - fmt.Fprintf(os.Stderr, "Flags:\n") - flag.PrintDefaults() - os.Exit(2) -} - -// File is a wrapper for the state of a file used in the parser. -// The parse tree walkers are all methods of this type. -type File struct { - pkg *Package - fset *token.FileSet - name string - content []byte - file *ast.File - b bytes.Buffer // for use by methods - - // The objects that are receivers of a "String() string" method. - // This is used by the recursiveStringer method in print.go. - stringers map[*ast.Object]bool - - // Registered checkers to run. - checkers map[ast.Node][]func(*File, ast.Node) -} - -func main() { - flag.Usage = Usage - flag.Parse() - - // If any flag is set, we run only those checks requested. - // If no flags are set true, set all the non-experimental ones not explicitly set (in effect, set the "-all" flag). - if setTrueCount == 0 { - for name, setting := range report { - if *setting == unset && !experimental[name] { - *setting = setTrue - } - } - } - - tagList = strings.Split(*tags, ",") - - initPrintFlags() - initUnusedFlags() - - if flag.NArg() == 0 { - Usage() - } - for _, name := range flag.Args() { - // Is it a directory? - fi, err := os.Stat(name) - if err != nil { - warnf("error walking tree: %s", err) - continue - } - if fi.IsDir() { - dirsRun = true - } else { - filesRun = true - if !strings.HasSuffix(name, "_test.go") { - includesNonTest = true - } - } - } - if dirsRun && filesRun { - Usage() - } - if dirsRun { - for _, name := range flag.Args() { - walkDir(name) - } - os.Exit(exitCode) - } - if !doPackage(".", flag.Args()) { - warnf("no files checked") - } - os.Exit(exitCode) -} - -// prefixDirectory places the directory name on the beginning of each name in the list. -func prefixDirectory(directory string, names []string) { - if directory != "." { - for i, name := range names { - names[i] = filepath.Join(directory, name) - } - } -} - -// doPackageDir analyzes the single package found in the directory, if there is one, -// plus a test package, if there is one. -func doPackageDir(directory string) { - context := build.Default - if len(context.BuildTags) != 0 { - warnf("build tags %s previously set", context.BuildTags) - } - context.BuildTags = append(tagList, context.BuildTags...) - - pkg, err := context.ImportDir(directory, 0) - if err != nil { - // If it's just that there are no go source files, that's fine. - if _, nogo := err.(*build.NoGoError); nogo { - return - } - // Non-fatal: we are doing a recursive walk and there may be other directories. - warnf("cannot process directory %s: %s", directory, err) - return - } - var names []string - names = append(names, pkg.GoFiles...) - names = append(names, pkg.CgoFiles...) - names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. - names = append(names, pkg.SFiles...) - prefixDirectory(directory, names) - doPackage(directory, names) - // Is there also a "foo_test" package? If so, do that one as well. - if len(pkg.XTestGoFiles) > 0 { - names = pkg.XTestGoFiles - prefixDirectory(directory, names) - doPackage(directory, names) - } -} - -type Package struct { - path string - defs map[*ast.Ident]types.Object - uses map[*ast.Ident]types.Object - selectors map[*ast.SelectorExpr]*types.Selection - types map[ast.Expr]types.TypeAndValue - spans map[types.Object]Span - files []*File - typesPkg *types.Package -} - -// doPackage analyzes the single package constructed from the named files. -// It returns whether any files were checked. -func doPackage(directory string, names []string) bool { - var files []*File - var astFiles []*ast.File - fs := token.NewFileSet() - for _, name := range names { - data, err := ioutil.ReadFile(name) - if err != nil { - // Warn but continue to next package. - warnf("%s: %s", name, err) - return false - } - checkBuildTag(name, data) - var parsedFile *ast.File - if strings.HasSuffix(name, ".go") { - parsedFile, err = parser.ParseFile(fs, name, data, 0) - if err != nil { - warnf("%s: %s", name, err) - return false - } - astFiles = append(astFiles, parsedFile) - } - files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile}) - } - if len(astFiles) == 0 { - return false - } - pkg := new(Package) - pkg.path = astFiles[0].Name.Name - pkg.files = files - // Type check the package. - err := pkg.check(fs, astFiles) - if err != nil && *verbose { - warnf("%s", err) - } - - // Check. - chk := make(map[ast.Node][]func(*File, ast.Node)) - for typ, set := range checkers { - for name, fn := range set { - if vet(name) { - chk[typ] = append(chk[typ], fn) - } - } - } - for _, file := range files { - file.pkg = pkg - file.checkers = chk - if file.file != nil { - file.walkFile(file.name, file.file) - } - } - asmCheck(pkg) - return true -} - -func visit(path string, f os.FileInfo, err error) error { - if err != nil { - warnf("walk error: %s", err) - return err - } - // One package per directory. Ignore the files themselves. - if !f.IsDir() { - return nil - } - doPackageDir(path) - return nil -} - -func (pkg *Package) hasFileWithSuffix(suffix string) bool { - for _, f := range pkg.files { - if strings.HasSuffix(f.name, suffix) { - return true - } - } - return false -} - -// walkDir recursively walks the tree looking for Go packages. -func walkDir(root string) { - filepath.Walk(root, visit) -} - -// errorf formats the error to standard error, adding program -// identification and a newline, and exits. -func errorf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - os.Exit(2) -} - -// warnf formats the error to standard error, adding program -// identification and a newline, but does not exit. -func warnf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - setExit(1) -} - -// Println is fmt.Println guarded by -v. -func Println(args ...interface{}) { - if !*verbose { - return - } - fmt.Println(args...) -} - -// Printf is fmt.Printf guarded by -v. -func Printf(format string, args ...interface{}) { - if !*verbose { - return - } - fmt.Printf(format+"\n", args...) -} - -// Bad reports an error and sets the exit code.. -func (f *File) Bad(pos token.Pos, args ...interface{}) { - f.Warn(pos, args...) - setExit(1) -} - -// Badf reports a formatted error and sets the exit code. -func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { - f.Warnf(pos, format, args...) - setExit(1) -} - -// loc returns a formatted representation of the position. -func (f *File) loc(pos token.Pos) string { - if pos == token.NoPos { - return "" - } - // Do not print columns. Because the pos often points to the start of an - // expression instead of the inner part with the actual error, the - // precision can mislead. - posn := f.fset.Position(pos) - return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line) -} - -// Warn reports an error but does not set the exit code. -func (f *File) Warn(pos token.Pos, args ...interface{}) { - fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...)) -} - -// Warnf reports a formatted error but does not set the exit code. -func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...) -} - -// walkFile walks the file's tree. -func (f *File) walkFile(name string, file *ast.File) { - Println("Checking file", name) - ast.Walk(f, file) -} - -// Visit implements the ast.Visitor interface. -func (f *File) Visit(node ast.Node) ast.Visitor { - var key ast.Node - switch node.(type) { - case *ast.AssignStmt: - key = assignStmt - case *ast.BinaryExpr: - key = binaryExpr - case *ast.CallExpr: - key = callExpr - case *ast.CompositeLit: - key = compositeLit - case *ast.ExprStmt: - key = exprStmt - case *ast.Field: - key = field - case *ast.FuncDecl: - key = funcDecl - case *ast.FuncLit: - key = funcLit - case *ast.GenDecl: - key = genDecl - case *ast.InterfaceType: - key = interfaceType - case *ast.RangeStmt: - key = rangeStmt - } - for _, fn := range f.checkers[key] { - fn(f, node) - } - return f -} - -// gofmt returns a string representation of the expression. -func (f *File) gofmt(x ast.Expr) string { - f.b.Reset() - printer.Fprint(&f.b, f.fset, x) - return f.b.String() -} diff --git a/cmd/vet/method.go b/cmd/vet/method.go deleted file mode 100644 index 00949df437..0000000000 --- a/cmd/vet/method.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2010 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. - -// This file contains the code to check canonical methods. - -package main - -import ( - "fmt" - "go/ast" - "go/printer" - "strings" -) - -func init() { - register("methods", - "check that canonically named methods are canonically defined", - checkCanonicalMethod, - funcDecl, interfaceType) -} - -type MethodSig struct { - args []string - results []string -} - -// canonicalMethods lists the input and output types for Go methods -// that are checked using dynamic interface checks. Because the -// checks are dynamic, such methods would not cause a compile error -// if they have the wrong signature: instead the dynamic check would -// fail, sometimes mysteriously. If a method is found with a name listed -// here but not the input/output types listed here, vet complains. -// -// A few of the canonical methods have very common names. -// For example, a type might implement a Scan method that -// has nothing to do with fmt.Scanner, but we still want to check -// the methods that are intended to implement fmt.Scanner. -// To do that, the arguments that have a = prefix are treated as -// signals that the canonical meaning is intended: if a Scan -// method doesn't have a fmt.ScanState as its first argument, -// we let it go. But if it does have a fmt.ScanState, then the -// rest has to match. -var canonicalMethods = map[string]MethodSig{ - // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict - "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter - "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder - "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder - "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler - "MarshalXML": {[]string{"*xml.Encoder", "xml.StartElement"}, []string{"error"}}, // xml.Marshaler - "Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader) - "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader - "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom - "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader - "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner - "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker - "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler - "UnmarshalXML": {[]string{"*xml.Decoder", "xml.StartElement"}, []string{"error"}}, // xml.Unmarshaler - "UnreadByte": {[]string{}, []string{"error"}}, - "UnreadRune": {[]string{}, []string{"error"}}, - "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer) - "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo -} - -func checkCanonicalMethod(f *File, node ast.Node) { - switch n := node.(type) { - case *ast.FuncDecl: - if n.Recv != nil { - canonicalMethod(f, n.Name, n.Type) - } - case *ast.InterfaceType: - for _, field := range n.Methods.List { - for _, id := range field.Names { - canonicalMethod(f, id, field.Type.(*ast.FuncType)) - } - } - } -} - -func canonicalMethod(f *File, id *ast.Ident, t *ast.FuncType) { - // Expected input/output. - expect, ok := canonicalMethods[id.Name] - if !ok { - return - } - - // Actual input/output - args := typeFlatten(t.Params.List) - var results []ast.Expr - if t.Results != nil { - results = typeFlatten(t.Results.List) - } - - // Do the =s (if any) all match? - if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") { - return - } - - // Everything must match. - if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") { - expectFmt := id.Name + "(" + argjoin(expect.args) + ")" - if len(expect.results) == 1 { - expectFmt += " " + argjoin(expect.results) - } else if len(expect.results) > 1 { - expectFmt += " (" + argjoin(expect.results) + ")" - } - - f.b.Reset() - if err := printer.Fprint(&f.b, f.fset, t); err != nil { - fmt.Fprintf(&f.b, "<%s>", err) - } - actual := f.b.String() - actual = strings.TrimPrefix(actual, "func") - actual = id.Name + actual - - f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt) - } -} - -func argjoin(x []string) string { - y := make([]string, len(x)) - for i, s := range x { - if s[0] == '=' { - s = s[1:] - } - y[i] = s - } - return strings.Join(y, ", ") -} - -// Turn parameter list into slice of types -// (in the ast, types are Exprs). -// Have to handle f(int, bool) and f(x, y, z int) -// so not a simple 1-to-1 conversion. -func typeFlatten(l []*ast.Field) []ast.Expr { - var t []ast.Expr - for _, f := range l { - if len(f.Names) == 0 { - t = append(t, f.Type) - continue - } - for _ = range f.Names { - t = append(t, f.Type) - } - } - return t -} - -// Does each type in expect with the given prefix match the corresponding type in actual? -func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool { - for i, x := range expect { - if !strings.HasPrefix(x, prefix) { - continue - } - if i >= len(actual) { - return false - } - if !f.matchParamType(x, actual[i]) { - return false - } - } - if prefix == "" && len(actual) > len(expect) { - return false - } - return true -} - -// Does this one type match? -func (f *File) matchParamType(expect string, actual ast.Expr) bool { - if strings.HasPrefix(expect, "=") { - expect = expect[1:] - } - // Strip package name if we're in that package. - if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' { - expect = expect[n+1:] - } - - // Overkill but easy. - f.b.Reset() - printer.Fprint(&f.b, f.fset, actual) - return f.b.String() == expect -} diff --git a/cmd/vet/nilfunc.go b/cmd/vet/nilfunc.go deleted file mode 100644 index fa1bac7e64..0000000000 --- a/cmd/vet/nilfunc.go +++ /dev/null @@ -1,68 +0,0 @@ -// 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. - -/* -This file contains the code to check for useless function comparisons. -A useless comparison is one like f == nil as opposed to f() == nil. -*/ - -package main - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -func init() { - register("nilfunc", - "check for comparisons between functions and nil", - checkNilFuncComparison, - binaryExpr) -} - -func checkNilFuncComparison(f *File, node ast.Node) { - e := node.(*ast.BinaryExpr) - - // Only want == or != comparisons. - if e.Op != token.EQL && e.Op != token.NEQ { - return - } - - // Only want comparisons with a nil identifier on one side. - var e2 ast.Expr - switch { - case f.isNil(e.X): - e2 = e.Y - case f.isNil(e.Y): - e2 = e.X - default: - return - } - - // Only want identifiers or selector expressions. - var obj types.Object - switch v := e2.(type) { - case *ast.Ident: - obj = f.pkg.uses[v] - case *ast.SelectorExpr: - obj = f.pkg.uses[v.Sel] - default: - return - } - - // Only want functions. - if _, ok := obj.(*types.Func); !ok { - return - } - - f.Badf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) -} - -// isNil reports whether the provided expression is the built-in nil -// identifier. -func (f *File) isNil(e ast.Expr) bool { - return f.pkg.types[e].Type == types.Typ[types.UntypedNil] -} diff --git a/cmd/vet/print.go b/cmd/vet/print.go deleted file mode 100644 index b20d935ef4..0000000000 --- a/cmd/vet/print.go +++ /dev/null @@ -1,587 +0,0 @@ -// Copyright 2010 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. - -// This file contains the printf-checker. - -package main - -import ( - "bytes" - "flag" - "go/ast" - "go/token" - "strconv" - "strings" - "unicode/utf8" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") - -func init() { - register("printf", - "check printf-like invocations", - checkFmtPrintfCall, - funcDecl, callExpr) -} - -func initPrintFlags() { - if *printfuncs == "" { - return - } - for _, name := range strings.Split(*printfuncs, ",") { - if len(name) == 0 { - flag.Usage() - } - skip := 0 - if colon := strings.LastIndex(name, ":"); colon > 0 { - var err error - skip, err = strconv.Atoi(name[colon+1:]) - if err != nil { - errorf(`illegal format for "Func:N" argument %q; %s`, name, err) - } - name = name[:colon] - } - name = strings.ToLower(name) - if name[len(name)-1] == 'f' { - printfList[name] = skip - } else { - printList[name] = skip - } - } -} - -// printfList records the formatted-print functions. The value is the location -// of the format parameter. Names are lower-cased so the lookup is -// case insensitive. -var printfList = map[string]int{ - "errorf": 0, - "fatalf": 0, - "fprintf": 1, - "logf": 0, - "panicf": 0, - "printf": 0, - "sprintf": 0, -} - -// printList records the unformatted-print functions. The value is the location -// of the first parameter to be printed. Names are lower-cased so the lookup is -// case insensitive. -var printList = map[string]int{ - "error": 0, - "fatal": 0, - "fprint": 1, "fprintln": 1, - "log": 0, - "panic": 0, "panicln": 0, - "print": 0, "println": 0, - "sprint": 0, "sprintln": 0, -} - -// checkCall triggers the print-specific checks if the call invokes a print function. -func checkFmtPrintfCall(f *File, node ast.Node) { - if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) { - // Remember we saw this. - if f.stringers == nil { - f.stringers = make(map[*ast.Object]bool) - } - if l := d.Recv.List; len(l) == 1 { - if n := l[0].Names; len(n) == 1 { - f.stringers[n[0].Obj] = true - } - } - return - } - - call, ok := node.(*ast.CallExpr) - if !ok { - return - } - var Name string - switch x := call.Fun.(type) { - case *ast.Ident: - Name = x.Name - case *ast.SelectorExpr: - Name = x.Sel.Name - default: - return - } - - name := strings.ToLower(Name) - if skip, ok := printfList[name]; ok { - f.checkPrintf(call, Name, skip) - return - } - if skip, ok := printList[name]; ok { - f.checkPrint(call, Name, skip) - return - } -} - -// isStringer returns true if the provided declaration is a "String() string" -// method, an implementation of fmt.Stringer. -func isStringer(f *File, d *ast.FuncDecl) bool { - return d.Recv != nil && d.Name.Name == "String" && d.Type.Results != nil && - len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 && - f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String] -} - -// formatState holds the parsed representation of a printf directive such as "%3.*[4]d". -// It is constructed by parsePrintfVerb. -type formatState struct { - verb rune // the format verb: 'd' for "%d" - format string // the full format directive from % through verb, "%.3d". - name string // Printf, Sprintf etc. - flags []byte // the list of # + etc. - argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call - indexed bool // whether an indexing expression appears: %[1]d. - firstArg int // Index of first argument after the format in the Printf call. - // Used only during parse. - file *File - call *ast.CallExpr - argNum int // Which argument we're expecting to format now. - indexPending bool // Whether we have an indexed argument that has not resolved. - nbytes int // number of bytes of the format string consumed. -} - -// checkPrintf checks a call to a formatted print routine such as Printf. -// call.Args[formatIndex] is (well, should be) the format argument. -func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { - if formatIndex >= len(call.Args) { - f.Bad(call.Pos(), "too few arguments in call to", name) - return - } - lit := f.pkg.types[call.Args[formatIndex]].Value - if lit == nil { - if *verbose { - f.Warn(call.Pos(), "can't check non-constant format in call to", name) - } - return - } - if lit.Kind() != exact.String { - f.Badf(call.Pos(), "constant %v not a string in call to %s", lit, name) - return - } - format := exact.StringVal(lit) - firstArg := formatIndex + 1 // Arguments are immediately after format string. - if !strings.Contains(format, "%") { - if len(call.Args) > firstArg { - f.Badf(call.Pos(), "no formatting directive in %s call", name) - } - return - } - // Hard part: check formats against args. - argNum := firstArg - indexed := false - for i, w := 0, 0; i < len(format); i += w { - w = 1 - if format[i] == '%' { - state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum) - if state == nil { - return - } - w = len(state.format) - if state.indexed { - indexed = true - } - if !f.okPrintfArg(call, state) { // One error per format is enough. - return - } - if len(state.argNums) > 0 { - // Continue with the next sequential argument. - argNum = state.argNums[len(state.argNums)-1] + 1 - } - } - } - // Dotdotdot is hard. - if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 { - return - } - // If the arguments were direct indexed, we assume the programmer knows what's up. - // Otherwise, there should be no leftover arguments. - if !indexed && argNum != len(call.Args) { - expect := argNum - firstArg - numArgs := len(call.Args) - firstArg - f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) - } -} - -// parseFlags accepts any printf flags. -func (s *formatState) parseFlags() { - for s.nbytes < len(s.format) { - switch c := s.format[s.nbytes]; c { - case '#', '0', '+', '-', ' ': - s.flags = append(s.flags, c) - s.nbytes++ - default: - return - } - } -} - -// scanNum advances through a decimal number if present. -func (s *formatState) scanNum() { - for ; s.nbytes < len(s.format); s.nbytes++ { - c := s.format[s.nbytes] - if c < '0' || '9' < c { - return - } - } -} - -// parseIndex scans an index expression. It returns false if there is a syntax error. -func (s *formatState) parseIndex() bool { - if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { - return true - } - // Argument index present. - s.indexed = true - s.nbytes++ // skip '[' - start := s.nbytes - s.scanNum() - if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { - s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index") - return false - } - arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) - if err != nil { - s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index: %s", err) - return false - } - s.nbytes++ // skip ']' - arg := int(arg32) - arg += s.firstArg - 1 // We want to zero-index the actual arguments. - s.argNum = arg - s.indexPending = true - return true -} - -// parseNum scans a width or precision (or *). It returns false if there's a bad index expression. -func (s *formatState) parseNum() bool { - if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { - if s.indexPending { // Absorb it. - s.indexPending = false - } - s.nbytes++ - s.argNums = append(s.argNums, s.argNum) - s.argNum++ - } else { - s.scanNum() - } - return true -} - -// parsePrecision scans for a precision. It returns false if there's a bad index expression. -func (s *formatState) parsePrecision() bool { - // If there's a period, there may be a precision. - if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { - s.flags = append(s.flags, '.') // Treat precision as a flag. - s.nbytes++ - if !s.parseIndex() { - return false - } - if !s.parseNum() { - return false - } - } - return true -} - -// parsePrintfVerb looks the formatting directive that begins the format string -// and returns a formatState that encodes what the directive wants, without looking -// at the actual arguments present in the call. The result is nil if there is an error. -func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { - state := &formatState{ - format: format, - name: name, - flags: make([]byte, 0, 5), - argNum: argNum, - argNums: make([]int, 0, 1), - nbytes: 1, // There's guaranteed to be a percent sign. - indexed: false, - firstArg: firstArg, - file: f, - call: call, - } - // There may be flags. - state.parseFlags() - indexPending := false - // There may be an index. - if !state.parseIndex() { - return nil - } - // There may be a width. - if !state.parseNum() { - return nil - } - // There may be a precision. - if !state.parsePrecision() { - return nil - } - // Now a verb, possibly prefixed by an index (which we may already have). - if !indexPending && !state.parseIndex() { - return nil - } - if state.nbytes == len(state.format) { - f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) - return nil - } - verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) - state.verb = verb - state.nbytes += w - if verb != '%' { - state.argNums = append(state.argNums, state.argNum) - } - state.format = state.format[:state.nbytes] - return state -} - -// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. -type printfArgType int - -const ( - argBool printfArgType = 1 << iota - argInt - argRune - argString - argFloat - argComplex - argPointer - anyType printfArgType = ^0 -) - -type printVerb struct { - verb rune // User may provide verb through Formatter; could be a rune. - flags string // known flags are all ASCII - typ printfArgType -} - -// Common flag sets for printf verbs. -const ( - noFlag = "" - numFlag = " -+.0" - sharpNumFlag = " -+.0#" - allFlags = " -+.0#" -) - -// printVerbs identifies which flags are known to printf for each verb. -// TODO: A type that implements Formatter may do what it wants, and vet -// will complain incorrectly. -var printVerbs = []printVerb{ - // '-' is a width modifier, always valid. - // '.' is a precision for float, max width for strings. - // '+' is required sign for numbers, Go format for %v. - // '#' is alternate format for several verbs. - // ' ' is spacer for numbers - {'%', noFlag, 0}, - {'b', numFlag, argInt | argFloat | argComplex}, - {'c', "-", argRune | argInt}, - {'d', numFlag, argInt}, - {'e', numFlag, argFloat | argComplex}, - {'E', numFlag, argFloat | argComplex}, - {'f', numFlag, argFloat | argComplex}, - {'F', numFlag, argFloat | argComplex}, - {'g', numFlag, argFloat | argComplex}, - {'G', numFlag, argFloat | argComplex}, - {'o', sharpNumFlag, argInt}, - {'p', "-#", argPointer}, - {'q', " -+.0#", argRune | argInt | argString}, - {'s', " -+.0", argString}, - {'t', "-", argBool}, - {'T', "-", anyType}, - {'U', "-#", argRune | argInt}, - {'v', allFlags, anyType}, - {'x', sharpNumFlag, argRune | argInt | argString}, - {'X', sharpNumFlag, argRune | argInt | argString}, -} - -// okPrintfArg compares the formatState to the arguments actually present, -// reporting any discrepancies it can discern. If the final argument is ellipsissed, -// there's little it can do for that. -func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { - var v printVerb - found := false - // Linear scan is fast enough for a small list. - for _, v = range printVerbs { - if v.verb == state.verb { - found = true - break - } - } - if !found { - f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) - return false - } - for _, flag := range state.flags { - if !strings.ContainsRune(v.flags, rune(flag)) { - f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) - return false - } - } - // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all - // but the final arg must be an integer. - trueArgs := 1 - if state.verb == '%' { - trueArgs = 0 - } - nargs := len(state.argNums) - for i := 0; i < nargs-trueArgs; i++ { - argNum := state.argNums[i] - if !f.argCanBeChecked(call, i, true, state) { - return - } - arg := call.Args[argNum] - if !f.matchArgType(argInt, nil, arg) { - f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) - return false - } - } - if state.verb == '%' { - return true - } - argNum := state.argNums[len(state.argNums)-1] - if !f.argCanBeChecked(call, len(state.argNums)-1, false, state) { - return false - } - arg := call.Args[argNum] - if !f.matchArgType(v.typ, nil, arg) { - typeString := "" - if typ := f.pkg.types[arg].Type; typ != nil { - typeString = typ.String() - } - f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) - return false - } - if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { - f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) - return false - } - return true -} - -// recursiveStringer reports whether the provided argument is r or &r for the -// fmt.Stringer receiver identifier r. -func (f *File) recursiveStringer(e ast.Expr) bool { - if len(f.stringers) == 0 { - return false - } - var obj *ast.Object - switch e := e.(type) { - case *ast.Ident: - obj = e.Obj - case *ast.UnaryExpr: - if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND { - obj = id.Obj - } - } - - // It's unlikely to be a recursive stringer if it has a Format method. - if typ := f.pkg.types[e].Type; typ != nil { - // Not a perfect match; see issue 6259. - if f.hasMethod(typ, "Format") { - return false - } - } - - // We compare the underlying Object, which checks that the identifier - // is the one we declared as the receiver for the String method in - // which this printf appears. - return f.stringers[obj] -} - -// argCanBeChecked reports whether the specified argument is statically present; -// it may be beyond the list of arguments or in a terminal slice... argument, which -// means we can't see it. -func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool { - argNum := state.argNums[formatArg] - if argNum < 0 { - // Shouldn't happen, so catch it with prejudice. - panic("negative arg num") - } - if argNum == 0 { - f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format) - return false - } - if argNum < len(call.Args)-1 { - return true // Always OK. - } - if call.Ellipsis.IsValid() { - return false // We just can't tell; there could be many more arguments. - } - if argNum < len(call.Args) { - return true - } - // There are bad indexes in the format or there are fewer arguments than the format needs. - // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". - arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. - f.Badf(call.Pos(), `missing argument for %s("%s"): format reads arg %d, have only %d args`, state.name, state.format, arg, len(call.Args)-state.firstArg) - return false -} - -// checkPrint checks a call to an unformatted print routine such as Println. -// call.Args[firstArg] is the first argument to be printed. -func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { - isLn := strings.HasSuffix(name, "ln") - isF := strings.HasPrefix(name, "F") - args := call.Args - if name == "Log" && len(args) > 0 { - // Special case: Don't complain about math.Log or cmplx.Log. - // Not strictly necessary because the only complaint likely is for Log("%d") - // but it feels wrong to check that math.Log is a good print function. - if sel, ok := args[0].(*ast.SelectorExpr); ok { - if x, ok := sel.X.(*ast.Ident); ok { - if x.Name == "math" || x.Name == "cmplx" { - return - } - } - } - } - // check for Println(os.Stderr, ...) - if firstArg == 0 && !isF && len(args) > 0 { - if sel, ok := args[0].(*ast.SelectorExpr); ok { - if x, ok := sel.X.(*ast.Ident); ok { - if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { - f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) - } - } - } - } - if len(args) <= firstArg { - // If we have a call to a method called Error that satisfies the Error interface, - // then it's ok. Otherwise it's something like (*T).Error from the testing package - // and we need to check it. - if name == "Error" && f.isErrorMethodCall(call) { - return - } - // If it's an Error call now, it's probably for printing errors. - if !isLn { - // Check the signature to be sure: there are niladic functions called "error". - if firstArg != 0 || f.numArgsInSignature(call) != firstArg { - f.Badf(call.Pos(), "no args in %s call", name) - } - } - return - } - arg := args[firstArg] - if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { - if strings.Contains(lit.Value, "%") { - f.Badf(call.Pos(), "possible formatting directive in %s call", name) - } - } - if isLn { - // The last item, if a string, should not have a newline. - arg = args[len(call.Args)-1] - if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { - if strings.HasSuffix(lit.Value, `\n"`) { - f.Badf(call.Pos(), "%s call ends with newline", name) - } - } - } - for _, arg := range args { - if f.recursiveStringer(arg) { - f.Badf(call.Pos(), "arg %s for print causes recursive call to String method", f.gofmt(arg)) - } - } -} diff --git a/cmd/vet/rangeloop.go b/cmd/vet/rangeloop.go deleted file mode 100644 index 96e2ca8062..0000000000 --- a/cmd/vet/rangeloop.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2012 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. - -/* -This file contains the code to check range loop variables bound inside function -literals that are deferred or launched in new goroutines. We only check -instances where the defer or go statement is the last statement in the loop -body, as otherwise we would need whole program analysis. - -For example: - - for i, v := range s { - go func() { - println(i, v) // not what you might expect - }() - } - -See: http://golang.org/doc/go_faq.html#closures_and_goroutines -*/ - -package main - -import "go/ast" - -func init() { - register("rangeloops", - "check that range loop variables are used correctly", - checkRangeLoop, - rangeStmt) -} - -// checkRangeLoop walks the body of the provided range statement, checking if -// its index or value variables are used unsafely inside goroutines or deferred -// function literals. -func checkRangeLoop(f *File, node ast.Node) { - n := node.(*ast.RangeStmt) - key, _ := n.Key.(*ast.Ident) - val, _ := n.Value.(*ast.Ident) - if key == nil && val == nil { - return - } - sl := n.Body.List - if len(sl) == 0 { - return - } - var last *ast.CallExpr - switch s := sl[len(sl)-1].(type) { - case *ast.GoStmt: - last = s.Call - case *ast.DeferStmt: - last = s.Call - default: - return - } - lit, ok := last.Fun.(*ast.FuncLit) - if !ok { - return - } - ast.Inspect(lit.Body, func(n ast.Node) bool { - id, ok := n.(*ast.Ident) - if !ok || id.Obj == nil { - return true - } - if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { - f.Bad(id.Pos(), "range variable", id.Name, "captured by func literal") - } - return true - }) -} diff --git a/cmd/vet/shadow.go b/cmd/vet/shadow.go deleted file mode 100644 index fa680a03e9..0000000000 --- a/cmd/vet/shadow.go +++ /dev/null @@ -1,245 +0,0 @@ -// 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. - -/* -This file contains the code to check for shadowed variables. -A shadowed variable is a variable declared in an inner scope -with the same name and type as a variable in an outer scope, -and where the outer variable is mentioned after the inner one -is declared. - -(This definition can be refined; the module generates too many -false positives and is not yet enabled by default.) - -For example: - - func BadRead(f *os.File, buf []byte) error { - var err error - for { - n, err := f.Read(buf) // shadows the function variable 'err' - if err != nil { - break // causes return of wrong value - } - foo(buf) - } - return err - } - -*/ - -package main - -import ( - "flag" - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy") - -func init() { - register("shadow", - "check for shadowed variables (experimental; must be set explicitly)", - checkShadow, - assignStmt, genDecl) - experimental["shadow"] = true -} - -func checkShadow(f *File, node ast.Node) { - switch n := node.(type) { - case *ast.AssignStmt: - checkShadowAssignment(f, n) - case *ast.GenDecl: - checkShadowDecl(f, n) - } -} - -// Span stores the minimum range of byte positions in the file in which a -// given variable (types.Object) is mentioned. It is lexically defined: it spans -// from the beginning of its first mention to the end of its last mention. -// A variable is considered shadowed (if *strictShadowing is off) only if the -// shadowing variable is declared within the span of the shadowed variable. -// In other words, if a variable is shadowed but not used after the shadowed -// variable is declared, it is inconsequential and not worth complaining about. -// This simple check dramatically reduces the nuisance rate for the shadowing -// check, at least until something cleverer comes along. -// -// One wrinkle: A "naked return" is a silent use of a variable that the Span -// will not capture, but the compilers catch naked returns of shadowed -// variables so we don't need to. -// -// Cases this gets wrong (TODO): -// - If a for loop's continuation statement mentions a variable redeclared in -// the block, we should complain about it but don't. -// - A variable declared inside a function literal can falsely be identified -// as shadowing a variable in the outer function. -// -type Span struct { - min token.Pos - max token.Pos -} - -// contains reports whether the position is inside the span. -func (s Span) contains(pos token.Pos) bool { - return s.min <= pos && pos < s.max -} - -// growSpan expands the span for the object to contain the instance represented -// by the identifier. -func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) { - if *strictShadowing { - return // No need - } - pos := ident.Pos() - end := ident.End() - span, ok := pkg.spans[obj] - if ok { - if span.min > pos { - span.min = pos - } - if span.max < end { - span.max = end - } - } else { - span = Span{pos, end} - } - pkg.spans[obj] = span -} - -// checkShadowAssignment checks for shadowing in a short variable declaration. -func checkShadowAssignment(f *File, a *ast.AssignStmt) { - if a.Tok != token.DEFINE { - return - } - if f.idiomaticShortRedecl(a) { - return - } - for _, expr := range a.Lhs { - ident, ok := expr.(*ast.Ident) - if !ok { - f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") - return - } - checkShadowing(f, ident) - } -} - -// idiomaticShortRedecl reports whether this short declaration can be ignored for -// the purposes of shadowing, that is, that any redeclarations it contains are deliberate. -func (f *File) idiomaticShortRedecl(a *ast.AssignStmt) bool { - // Don't complain about deliberate redeclarations of the form - // i := i - // Such constructs are idiomatic in range loops to create a new variable - // for each iteration. Another example is - // switch n := n.(type) - if len(a.Rhs) != len(a.Lhs) { - return false - } - // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) - for i, expr := range a.Lhs { - lhs, ok := expr.(*ast.Ident) - if !ok { - f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") - return true // Don't do any more processing. - } - switch rhs := a.Rhs[i].(type) { - case *ast.Ident: - if lhs.Name != rhs.Name { - return false - } - case *ast.TypeAssertExpr: - if id, ok := rhs.X.(*ast.Ident); ok { - if lhs.Name != id.Name { - return false - } - } - } - } - return true -} - -// idiomaticRedecl reports whether this declaration spec can be ignored for -// the purposes of shadowing, that is, that any redeclarations it contains are deliberate. -func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool { - // Don't complain about deliberate redeclarations of the form - // var i, j = i, j - if len(d.Names) != len(d.Values) { - return false - } - for i, lhs := range d.Names { - if rhs, ok := d.Values[i].(*ast.Ident); ok { - if lhs.Name != rhs.Name { - return false - } - } - } - return true -} - -// checkShadowDecl checks for shadowing in a general variable declaration. -func checkShadowDecl(f *File, d *ast.GenDecl) { - if d.Tok != token.VAR { - return - } - for _, spec := range d.Specs { - valueSpec, ok := spec.(*ast.ValueSpec) - if !ok { - f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec") - return - } - // Don't complain about deliberate redeclarations of the form - // var i = i - if f.idiomaticRedecl(valueSpec) { - return - } - for _, ident := range valueSpec.Names { - checkShadowing(f, ident) - } - } -} - -// checkShadowing checks whether the identifier shadows an identifier in an outer scope. -func checkShadowing(f *File, ident *ast.Ident) { - if ident.Name == "_" { - // Can't shadow the blank identifier. - return - } - obj := f.pkg.defs[ident] - if obj == nil { - return - } - // obj.Parent.Parent is the surrounding scope. If we can find another declaration - // starting from there, we have a shadowed identifier. - _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) - if shadowed == nil { - return - } - // Don't complain if it's shadowing a universe-declared identifier; that's fine. - if shadowed.Parent() == types.Universe { - return - } - if *strictShadowing { - // The shadowed identifier must appear before this one to be an instance of shadowing. - if shadowed.Pos() > ident.Pos() { - return - } - } else { - // Don't complain if the span of validity of the shadowed identifier doesn't include - // the shadowing identifier. - span, ok := f.pkg.spans[shadowed] - if !ok { - f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name) - return - } - if !span.contains(ident.Pos()) { - return - } - } - // Don't complain if the types differ: that implies the programmer really wants two different things. - if types.Identical(obj.Type(), shadowed.Type()) { - f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) - } -} diff --git a/cmd/vet/shift.go b/cmd/vet/shift.go deleted file mode 100644 index 2385c23fdb..0000000000 --- a/cmd/vet/shift.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014 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. - -/* -This file contains the code to check for suspicious shifts. -*/ - -package main - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -func init() { - register("shift", - "check for useless shifts", - checkShift, - binaryExpr, assignStmt) -} - -func checkShift(f *File, node ast.Node) { - switch node := node.(type) { - case *ast.BinaryExpr: - if node.Op == token.SHL || node.Op == token.SHR { - checkLongShift(f, node, node.X, node.Y) - } - case *ast.AssignStmt: - if len(node.Lhs) != 1 || len(node.Rhs) != 1 { - return - } - if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN { - checkLongShift(f, node, node.Lhs[0], node.Rhs[0]) - } - } -} - -// checkLongShift checks if shift or shift-assign operations shift by more than -// the length of the underlying variable. -func checkLongShift(f *File, node ast.Node, x, y ast.Expr) { - v := f.pkg.types[y].Value - if v == nil { - return - } - amt, ok := exact.Int64Val(v) - if !ok { - return - } - t := f.pkg.types[x].Type - if t == nil { - return - } - b, ok := t.Underlying().(*types.Basic) - if !ok { - return - } - var size int64 - var msg string - switch b.Kind() { - case types.Uint8, types.Int8: - size = 8 - case types.Uint16, types.Int16: - size = 16 - case types.Uint32, types.Int32: - size = 32 - case types.Uint64, types.Int64: - size = 64 - case types.Int, types.Uint, types.Uintptr: - // These types may be as small as 32 bits, but no smaller. - size = 32 - msg = "might be " - default: - return - } - if amt >= size { - ident := f.gofmt(x) - f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt) - } -} diff --git a/cmd/vet/structtag.go b/cmd/vet/structtag.go deleted file mode 100644 index e8164a46f9..0000000000 --- a/cmd/vet/structtag.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2010 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. - -// This file contains the test for canonical struct tags. - -package main - -import ( - "errors" - "go/ast" - "reflect" - "strconv" -) - -func init() { - register("structtags", - "check that struct field tags have canonical format and apply to exported fields as needed", - checkCanonicalFieldTag, - field) -} - -// checkCanonicalFieldTag checks a struct field tag. -func checkCanonicalFieldTag(f *File, node ast.Node) { - field := node.(*ast.Field) - if field.Tag == nil { - return - } - - tag, err := strconv.Unquote(field.Tag.Value) - if err != nil { - f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) - return - } - - if err := validateStructTag(tag); err != nil { - f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get: %s", field.Tag.Value, err) - } - - // Check for use of json or xml tags with unexported fields. - - // Embedded struct. Nothing to do for now, but that - // may change, depending on what happens with issue 7363. - if len(field.Names) == 0 { - return - } - - if field.Names[0].IsExported() { - return - } - - st := reflect.StructTag(tag) - for _, enc := range [...]string{"json", "xml"} { - if st.Get(enc) != "" { - f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Names[0].Name, enc) - return - } - } -} - -var ( - errTagSyntax = errors.New("bad syntax for struct tag pair") - errTagKeySyntax = errors.New("bad syntax for struct tag key") - errTagValueSyntax = errors.New("bad syntax for struct tag value") -) - -// validateStructTag parses the struct tag and returns an error if it is not -// in the canonical format, which is a space-separated list of key:"value" -// settings. The value may contain spaces. -func validateStructTag(tag string) error { - // This code is based on the StructTag.Get code in package reflect. - - for tag != "" { - // Skip leading space. - i := 0 - for i < len(tag) && tag[i] == ' ' { - i++ - } - tag = tag[i:] - if tag == "" { - break - } - - // Scan to colon. A space, a quote or a control character is a syntax error. - // Strictly speaking, control chars include the range [0x7f, 0x9f], not just - // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters - // as it is simpler to inspect the tag's bytes than the tag's runes. - i = 0 - for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { - i++ - } - if i == 0 { - return errTagKeySyntax - } - if i+1 >= len(tag) || tag[i] != ':' { - return errTagSyntax - } - if tag[i+1] != '"' { - return errTagValueSyntax - } - tag = tag[i+1:] - - // Scan quoted string to find value. - i = 1 - for i < len(tag) && tag[i] != '"' { - if tag[i] == '\\' { - i++ - } - i++ - } - if i >= len(tag) { - return errTagValueSyntax - } - qvalue := string(tag[:i+1]) - tag = tag[i+1:] - - if _, err := strconv.Unquote(qvalue); err != nil { - return errTagValueSyntax - } - } - return nil -} diff --git a/cmd/vet/testdata/asm.go b/cmd/vet/testdata/asm.go deleted file mode 100644 index 9a3d5315ad..0000000000 --- a/cmd/vet/testdata/asm.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010 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. - -// +build ignore - -// This file contains declarations to test the assembly in test_asm.s. - -package testdata - -func arg1(x int8, y uint8) -func arg2(x int16, y uint16) -func arg4(x int32, y uint32) -func arg8(x int64, y uint64) -func argint(x int, y uint) -func argptr(x *byte, y *byte, c chan int, m map[int]int, f func()) -func argstring(x, y string) -func argslice(x, y []string) -func argiface(x interface{}, y interface { - m() -}) -func returnint() int -func returnbyte(x int) byte -func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte) -func returnintmissing() int -func leaf(x, y int) int - -func noprof(x int) -func dupok(x int) -func nosplit(x int) -func rodata(x int) -func noptr(x int) -func wrapper(x int) diff --git a/cmd/vet/testdata/asm1.s b/cmd/vet/testdata/asm1.s deleted file mode 100644 index 62f423cd8b..0000000000 --- a/cmd/vet/testdata/asm1.s +++ /dev/null @@ -1,254 +0,0 @@ -// 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. - -// +build amd64 -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - // MOVB x+0(FP), AX // commented out instructions used to panic - MOVB y+1(FP), BX - MOVW x+0(FP), AX // ERROR "\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" - MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" - MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - TESTB x+0(FP), AX - TESTB y+1(FP), BX - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" - TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" - TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" - TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" - TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - MOVB 8(SP), AX // ERROR "8\(SP\) should be x\+0\(FP\)" - MOVB 9(SP), AX // ERROR "9\(SP\) should be y\+1\(FP\)" - MOVB 10(SP), AX // ERROR "use of 10\(SP\) points beyond argument frame" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+0(FP), AX - MOVW y+2(FP), BX - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" - MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" - MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" - TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+0(FP), AX - TESTW y+2(FP), BX - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" - TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" - TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value" - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value" - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - MOVL c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value" - MOVL m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value" - MOVL f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value" - RET - -TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value" - MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value" - MOVQ x_base+0(FP), AX - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value" - MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value" - MOVQ x_len+8(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)" - MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)" - RET - -TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value" - MOVL x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value" - MOVQ x_base+0(FP), AX - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)" - MOVW x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value" - MOVL x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value" - MOVQ x_len+8(FP), AX - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)" - MOVW x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value" - MOVL x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value" - MOVQ x_cap+16(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)" - MOVQ y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)" - MOVQ y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-32 - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value" - MOVQ x+0(FP), AX - MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value" - MOVL x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value" - MOVQ x_type+0(FP), AX - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)" - MOVW x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value" - MOVL x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value" - MOVQ x_data+8(FP), AX - MOVW y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value" - MOVL y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value" - MOVQ y+16(FP), AX - MOVW y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value" - MOVL y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value" - MOVQ y_itable+16(FP), AX - MOVQ y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)" - MOVW y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVL y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVQ y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)" - MOVW y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value" - MOVL y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value" - MOVQ y_data+24(FP), AX - RET - -TEXT ·returnint(SB),0,$0-8 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value" - MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value" - MOVL AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value" - MOVQ AX, ret+0(FP) - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-9 - MOVQ x+0(FP), AX - MOVB AX, ret+8(FP) - MOVW AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value" - MOVL AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value" - MOVQ AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value" - MOVB AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-41 - MOVB x+0(FP), AX - MOVQ AX, r1+8(FP) - MOVW AX, r2+16(FP) - MOVQ AX, r3+24(FP) - MOVQ AX, r3_base+24(FP) - MOVQ AX, r3_len+32(FP) - MOVB AX, r4+40(FP) - MOVL AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value" - RET - -TEXT ·returnintmissing(SB),0,$0-8 - RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)" diff --git a/cmd/vet/testdata/asm2.s b/cmd/vet/testdata/asm2.s deleted file mode 100644 index c33c02a70b..0000000000 --- a/cmd/vet/testdata/asm2.s +++ /dev/null @@ -1,257 +0,0 @@ -// 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. - -// +build 386 -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - MOVB y+1(FP), BX - MOVW x+0(FP), AX // ERROR "\[386\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value" - MOVL y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value" - MOVQ y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - TESTB x+0(FP), AX - TESTB y+1(FP), BX - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value" - TESTW y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value" - TESTL y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value" - TESTQ y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value" - TESTB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - TESTB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - MOVB 4(SP), AX // ERROR "4\(SP\) should be x\+0\(FP\)" - MOVB 5(SP), AX // ERROR "5\(SP\) should be y\+1\(FP\)" - MOVB 6(SP), AX // ERROR "use of 6\(SP\) points beyond argument frame" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+0(FP), AX - MOVW y+2(FP), BX - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value" - MOVL y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value" - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value" - MOVQ y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value" - MOVW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value" - TESTB y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+0(FP), AX - TESTW y+2(FP), BX - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value" - TESTL y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value" - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value" - TESTQ y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value" - TESTW x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - TESTW y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value" - MOVL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value" - TESTL x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTL y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value" - MOVL x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - MOVL x_lo+0(FP), AX - MOVL x_hi+4(FP), AX - MOVL y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - MOVL y_lo+8(FP), AX - MOVL y_hi+12(FP), AX - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value" - TESTB y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value" - TESTW y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value" - TESTL x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - TESTL y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - TESTQ x+0(FP), AX - TESTQ y+8(FP), AX - TESTQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value" - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value" - TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value" - MOVW y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value" - MOVL x+0(FP), AX - MOVL y+4(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value" - MOVQ y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value" - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - TESTB x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value" - TESTB y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value" - TESTW x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value" - TESTW y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value" - TESTL x+0(FP), AX - TESTL y+4(FP), AX - TESTQ x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value" - TESTQ y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value" - TESTQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - TESTQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - MOVW c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value" - MOVW m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value" - MOVW f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value" - RET - -TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value" - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value" - MOVL x_base+0(FP), AX - MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value" - MOVL x_len+4(FP), AX - MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value" - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" - RET - -TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value" - MOVW x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value" - MOVL x_base+0(FP), AX - MOVQ x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVL x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value" - MOVL x_len+4(FP), AX - MOVQ x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value" - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVL x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVW x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVL x_cap+8(FP), AX - MOVQ x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" - MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-16 - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value" - MOVL x+0(FP), AX - MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value" - MOVW x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value" - MOVL x_type+0(FP), AX - MOVQ x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value" - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVL x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVW x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value" - MOVL x_data+4(FP), AX - MOVQ x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value" - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value" - MOVL y+8(FP), AX - MOVQ y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value" - MOVW y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVL y_itable+8(FP), AX - MOVQ y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" - MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVL y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVW y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value" - MOVL y_data+12(FP), AX - MOVQ y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value" - RET - -TEXT ·returnint(SB),0,$0-4 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" - MOVW AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value" - MOVL AX, ret+0(FP) - MOVQ AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value" - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-5 - MOVL x+0(FP), AX - MOVB AX, ret+4(FP) - MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" - MOVL AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value" - MOVQ AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value" - MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-21 - MOVB x+0(FP), AX - MOVL AX, r1+4(FP) - MOVW AX, r2+8(FP) - MOVL AX, r3+12(FP) - MOVL AX, r3_base+12(FP) - MOVL AX, r3_len+16(FP) - MOVB AX, r4+20(FP) - MOVQ AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value" - RET - -TEXT ·returnintmissing(SB),0,$0-4 - RET // ERROR "RET without writing to 4-byte ret\+0\(FP\)" diff --git a/cmd/vet/testdata/asm3.s b/cmd/vet/testdata/asm3.s deleted file mode 100644 index 3d69356a0f..0000000000 --- a/cmd/vet/testdata/asm3.s +++ /dev/null @@ -1,178 +0,0 @@ -// 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. - -// +build arm -// +build vet_test - -TEXT ·arg1(SB),0,$0-2 - MOVB x+0(FP), AX - MOVB y+1(FP), BX - MOVH x+0(FP), AX // ERROR "\[arm\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value" - MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value" - MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value" - MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)" - MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)" - MOVB 8(R13), AX // ERROR "8\(R13\) should be x\+0\(FP\)" - MOVB 9(R13), AX // ERROR "9\(R13\) should be y\+1\(FP\)" - MOVB 10(R13), AX // ERROR "use of 10\(R13\) points beyond argument frame" - RET - -TEXT ·arg2(SB),0,$0-4 - MOVB x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value" - MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value" - MOVH x+0(FP), AX - MOVH y+2(FP), BX - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value" - MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value" - MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)" - MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)" - RET - -TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value" - MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value" - MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value" - MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)" - MOVW x_lo+0(FP), AX - MOVW x_hi+4(FP), AX - MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)" - MOVW y_lo+8(FP), AX - MOVW y_hi+12(FP), AX - MOVQ x+0(FP), AX - MOVQ y+8(FP), AX - MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)" - RET - -TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - RET - -TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20" - MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value" - MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value" - MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value" - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)" - MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)" - MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value" - MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value" - MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value" - RET - -TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value" - MOVW x+0(FP), AX - MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value" - MOVW x_base+0(FP), AX - MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value" - MOVW x_len+4(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)" - RET - -TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24" - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value" - MOVW x+0(FP), AX - MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value" - MOVW x_base+0(FP), AX - MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)" - MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value" - MOVW x_len+4(FP), AX - MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)" - MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value" - MOVW x_cap+8(FP), AX - MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)" - MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)" - MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)" - RET - -TEXT ·argiface(SB),0,$0-16 - MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value" - MOVW x+0(FP), AX - MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value" - MOVW x_type+0(FP), AX - MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)" - MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)" - MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)" - MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value" - MOVW x_data+4(FP), AX - MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value" - MOVW y+8(FP), AX - MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value" - MOVW y_itable+8(FP), AX - MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)" - MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)" - MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value" - MOVW y_data+12(FP), AX - RET - -TEXT ·returnint(SB),0,$0-4 - MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value" - MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value" - MOVW AX, ret+0(FP) - MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)" - MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)" - RET - -TEXT ·returnbyte(SB),0,$0-5 - MOVW x+0(FP), AX - MOVB AX, ret+4(FP) - MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value" - MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value" - MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)" - RET - -TEXT ·returnnamed(SB),0,$0-21 - MOVB x+0(FP), AX - MOVW AX, r1+4(FP) - MOVH AX, r2+8(FP) - MOVW AX, r3+12(FP) - MOVW AX, r3_base+12(FP) - MOVW AX, r3_len+16(FP) - MOVB AX, r4+20(FP) - MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value" - RET - -TEXT ·returnintmissing(SB),0,$0-4 - RET // ERROR "RET without writing to 4-byte ret\+0\(FP\)" - -TEXT ·leaf(SB),0,$-4-12 - MOVW x+0(FP), AX - MOVW y+4(FP), AX - MOVW AX, ret+8(FP) - RET diff --git a/cmd/vet/testdata/asm4.s b/cmd/vet/testdata/asm4.s deleted file mode 100644 index 044b050b6b..0000000000 --- a/cmd/vet/testdata/asm4.s +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - -// +build amd64 -// +build vet_test - -// Test cases for symbolic NOSPLIT etc. on TEXT symbols. - -TEXT ·noprof(SB),NOPROF,$0-8 - RET - -TEXT ·dupok(SB),DUPOK,$0-8 - RET - -TEXT ·nosplit(SB),NOSPLIT,$0 - RET - -TEXT ·rodata(SB),RODATA,$0-8 - RET - -TEXT ·noptr(SB),NOPTR|NOSPLIT,$0 - RET - -TEXT ·wrapper(SB),WRAPPER,$0-8 - RET diff --git a/cmd/vet/testdata/assign.go b/cmd/vet/testdata/assign.go deleted file mode 100644 index 32ba8683c1..0000000000 --- a/cmd/vet/testdata/assign.go +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -// This file contains tests for the useless-assignment checker. - -package testdata - -type ST struct { - x int -} - -func (s *ST) SetX(x int) { - // Accidental self-assignment; it should be "s.x = x" - x = x // ERROR "self-assignment of x to x" - // Another mistake - s.x = s.x // ERROR "self-assignment of s.x to s.x" -} diff --git a/cmd/vet/testdata/atomic.go b/cmd/vet/testdata/atomic.go deleted file mode 100644 index 1ba261d941..0000000000 --- a/cmd/vet/testdata/atomic.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -// This file contains tests for the atomic checker. - -package testdata - -import ( - "sync/atomic" -) - -type Counter uint64 - -func AtomicTests() { - x := uint64(1) - x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" - _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" - x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" - - y := &x - *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value" - - var su struct{ Counter uint64 } - su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value" - z1 := atomic.AddUint64(&su.Counter, 1) - _ = z1 // Avoid err "z declared and not used" - - var sp struct{ Counter *uint64 } - *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value" - z2 := atomic.AddUint64(sp.Counter, 1) - _ = z2 // Avoid err "z declared and not used" - - au := []uint64{10, 20} - au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value" - au[1] = atomic.AddUint64(&au[0], 1) - - ap := []*uint64{&au[0], &au[1]} - *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value" - *ap[1] = atomic.AddUint64(ap[0], 1) - - x = atomic.AddUint64() // Used to make vet crash; now silently ignored. -} diff --git a/cmd/vet/testdata/bool.go b/cmd/vet/testdata/bool.go deleted file mode 100644 index af6cc011dd..0000000000 --- a/cmd/vet/testdata/bool.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014 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. - -// This file contains tests for the bool checker. - -package testdata - -import "io" - -func RatherStupidConditions() { - var f, g func() int - if f() == 0 || f() == 0 { // OK f might have side effects - } - if v, w := f(), g(); v == w || v == w { // ERROR "redundant or: v == w || v == w" - } - _ = f == nil || f == nil // ERROR "redundant or: f == nil || f == nil" - - _ = i == byte(1) || i == byte(1) // TODO conversions are treated as if they may have side effects - - var c chan int - _ = 0 == <-c || 0 == <-c // OK subsequent receives may yield different values - for i, j := <-c, <-c; i == j || i == j; i, j = <-c, <-c { // ERROR "redundant or: i == j || i == j" - } - - var i, j, k int - _ = i+1 == 1 || i+1 == 1 // ERROR "redundant or: i\+1 == 1 || i\+1 == 1" - _ = i == 1 || j+1 == i || i == 1 // ERROR "redundant or: i == 1 || i == 1" - - _ = i == 1 || i == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || f() == 1 || i == 1 // OK f may alter i as a side effect - _ = f() == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1" - - // Test partition edge cases - _ = f() == 1 || i == 1 || i == 1 || j == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = f() == 1 || j == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || f() == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || i == 1 || f() == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || i == 1 || j == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = j == 1 || i == 1 || i == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || f() == 1 || f() == 1 || i == 1 - - _ = i == 1 || (i == 1 || i == 2) // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || (f() == 1 || i == 1) // OK f may alter i as a side effect - _ = i == 1 || (i == 1 || f() == 1) // ERROR "redundant or: i == 1 || i == 1" - _ = i == 1 || (i == 2 || (i == 1 || i == 3)) // ERROR "redundant or: i == 1 || i == 1" - - var a, b bool - _ = i == 1 || (a || (i == 1 || b)) // ERROR "redundant or: i == 1 || i == 1" - - // Check that all redundant ors are flagged - _ = j == 0 || - i == 1 || - f() == 1 || - j == 0 || // ERROR "redundant or: j == 0 || j == 0" - i == 1 || // ERROR "redundant or: i == 1 || i == 1" - i == 1 || // ERROR "redundant or: i == 1 || i == 1" - i == 1 || - j == 0 || - k == 0 - - _ = i == 1*2*3 || i == 1*2*3 // ERROR "redundant or: i == 1\*2\*3 || i == 1\*2\*3" - - // These test that redundant, suspect expressions do not trigger multiple errors. - _ = i != 0 || i != 0 // ERROR "redundant or: i != 0 || i != 0" - _ = i == 0 && i == 0 // ERROR "redundant and: i == 0 && i == 0" - - // and is dual to or; check the basics and - // let the or tests pull the rest of the weight. - _ = 0 != <-c && 0 != <-c // OK subsequent receives may yield different values - _ = f() != 0 && f() != 0 // OK f might have side effects - _ = f != nil && f != nil // ERROR "redundant and: f != nil && f != nil" - _ = i != 1 && i != 1 && f() != 1 // ERROR "redundant and: i != 1 && i != 1" - _ = i != 1 && f() != 1 && i != 1 // OK f may alter i as a side effect - _ = f() != 1 && i != 1 && i != 1 // ERROR "redundant and: i != 1 && i != 1" -} - -func RoyallySuspectConditions() { - var i, j int - - _ = i == 0 || i == 1 // OK - _ = i != 0 || i != 1 // ERROR "suspect or: i != 0 || i != 1" - _ = i != 0 || 1 != i // ERROR "suspect or: i != 0 || 1 != i" - _ = 0 != i || 1 != i // ERROR "suspect or: 0 != i || 1 != i" - _ = 0 != i || i != 1 // ERROR "suspect or: 0 != i || i != 1" - - _ = (0 != i) || i != 1 // ERROR "suspect or: 0 != i || i != 1" - - _ = i+3 != 7 || j+5 == 0 || i+3 != 9 // ERROR "suspect or: i\+3 != 7 || i\+3 != 9" - - _ = i != 0 || j == 0 || i != 1 // ERROR "suspect or: i != 0 || i != 1" - - _ = i != 0 || i != 1<<4 // ERROR "suspect or: i != 0 || i != 1<<4" - - _ = i != 0 || j != 0 - _ = 0 != i || 0 != j - - var s string - _ = s != "one" || s != "the other" // ERROR "suspect or: s != .one. || s != .the other." - - _ = "et" != "alii" || "et" != "cetera" // ERROR "suspect or: .et. != .alii. || .et. != .cetera." - _ = "me gustas" != "tu" || "le gustas" != "tu" // OK we could catch this case, but it's not worth the code - - var err error - _ = err != nil || err != io.EOF // TODO catch this case? - - // Sanity check and. - _ = i != 0 && i != 1 // OK - _ = i == 0 && i == 1 // ERROR "suspect and: i == 0 && i == 1" - _ = i == 0 && 1 == i // ERROR "suspect and: i == 0 && 1 == i" - _ = 0 == i && 1 == i // ERROR "suspect and: 0 == i && 1 == i" - _ = 0 == i && i == 1 // ERROR "suspect and: 0 == i && i == 1" -} diff --git a/cmd/vet/testdata/buildtag.go b/cmd/vet/testdata/buildtag.go deleted file mode 100644 index eb36fd3259..0000000000 --- a/cmd/vet/testdata/buildtag.go +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -// This file contains tests for the buildtag checker. - -// +builder // ERROR "possible malformed \+build comment" -// +build !ignore - -package testdata - -// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line" - -var _ = 3 diff --git a/cmd/vet/testdata/buildtag_bad.go b/cmd/vet/testdata/buildtag_bad.go deleted file mode 100644 index fbe10cf748..0000000000 --- a/cmd/vet/testdata/buildtag_bad.go +++ /dev/null @@ -1,15 +0,0 @@ -// This file contains misplaced or malformed build constraints. -// The Go tool will skip it, because the constraints are invalid. -// It serves only to test the tag checker during make test. - -// Mention +build // ERROR "possible malformed \+build comment" - -// +build !!bang // ERROR "invalid double negative in build constraint" -// +build @#$ // ERROR "invalid non-alphanumeric build constraint" - -// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line" -package bad - -// This is package 'bad' rather than 'main' so the erroneous build -// tag doesn't end up looking like a package doc for the vet command -// when examined by godoc. diff --git a/cmd/vet/testdata/composite.go b/cmd/vet/testdata/composite.go deleted file mode 100644 index 69e7d7ccb0..0000000000 --- a/cmd/vet/testdata/composite.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2012 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. - -// This file contains tests for the untagged struct literal checker. - -// This file contains the test for untagged struct literals. - -package testdata - -import ( - "flag" - "go/scanner" -) - -var Okay1 = []string{ - "Name", - "Usage", - "DefValue", -} - -var Okay2 = map[string]bool{ - "Name": true, - "Usage": true, - "DefValue": true, -} - -var Okay3 = struct { - X string - Y string - Z string -}{ - "Name", - "Usage", - "DefValue", -} - -type MyStruct struct { - X string - Y string - Z string -} - -var Okay4 = MyStruct{ - "Name", - "Usage", - "DefValue", -} - -// Testing is awkward because we need to reference things from a separate package -// to trigger the warnings. - -var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "unkeyed fields" - "Name", - "Usage", - nil, // Value - "DefValue", -} - -// Used to test the check for slices and arrays: If that test is disabled and -// vet is run with --compositewhitelist=false, this line triggers an error. -// Clumsy but sufficient. -var scannerErrorListTest = scanner.ErrorList{nil, nil} diff --git a/cmd/vet/testdata/copylock_func.go b/cmd/vet/testdata/copylock_func.go deleted file mode 100644 index 108c044209..0000000000 --- a/cmd/vet/testdata/copylock_func.go +++ /dev/null @@ -1,90 +0,0 @@ -// 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. - -// This file contains tests for the copylock checker's -// function declaration analysis. - -package testdata - -import "sync" - -func OkFunc(*sync.Mutex) {} -func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes Lock by value: sync.Mutex" -func OkRet() *sync.Mutex {} -func BadRet() sync.Mutex {} // ERROR "BadRet returns Lock by value: sync.Mutex" - -type EmbeddedRWMutex struct { - sync.RWMutex -} - -func (*EmbeddedRWMutex) OkMeth() {} -func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes Lock by value: testdata.EmbeddedRWMutex" -func OkFunc(e *EmbeddedRWMutex) {} -func BadFunc(EmbeddedRWMutex) {} // ERROR "BadFunc passes Lock by value: testdata.EmbeddedRWMutex" -func OkRet() *EmbeddedRWMutex {} -func BadRet() EmbeddedRWMutex {} // ERROR "BadRet returns Lock by value: testdata.EmbeddedRWMutex" - -type FieldMutex struct { - s sync.Mutex -} - -func (*FieldMutex) OkMeth() {} -func (FieldMutex) BadMeth() {} // ERROR "BadMeth passes Lock by value: testdata.FieldMutex contains sync.Mutex" -func OkFunc(*FieldMutex) {} -func BadFunc(FieldMutex, int) {} // ERROR "BadFunc passes Lock by value: testdata.FieldMutex contains sync.Mutex" - -type L0 struct { - L1 -} - -type L1 struct { - l L2 -} - -type L2 struct { - sync.Mutex -} - -func (*L0) Ok() {} -func (L0) Bad() {} // ERROR "Bad passes Lock by value: testdata.L0 contains testdata.L1 contains testdata.L2" - -type EmbeddedMutexPointer struct { - s *sync.Mutex // safe to copy this pointer -} - -func (*EmbeddedMutexPointer) Ok() {} -func (EmbeddedMutexPointer) AlsoOk() {} -func StillOk(EmbeddedMutexPointer) {} -func LookinGood() EmbeddedMutexPointer {} - -type EmbeddedLocker struct { - sync.Locker // safe to copy interface values -} - -func (*EmbeddedLocker) Ok() {} -func (EmbeddedLocker) AlsoOk() {} - -type CustomLock struct{} - -func (*CustomLock) Lock() {} -func (*CustomLock) Unlock() {} - -func Ok(*CustomLock) {} -func Bad(CustomLock) {} // ERROR "Bad passes Lock by value: testdata.CustomLock" - -// TODO: Unfortunate cases - -// Non-ideal error message: -// Since we're looking for Lock methods, sync.Once's underlying -// sync.Mutex gets called out, but without any reference to the sync.Once. -type LocalOnce sync.Once - -func (LocalOnce) Bad() {} // ERROR "Bad passes Lock by value: testdata.LocalOnce contains sync.Mutex" - -// False negative: -// LocalMutex doesn't have a Lock method. -// Nevertheless, it is probably a bad idea to pass it by value. -type LocalMutex sync.Mutex - -func (LocalMutex) Bad() {} // WANTED: An error here :( diff --git a/cmd/vet/testdata/copylock_range.go b/cmd/vet/testdata/copylock_range.go deleted file mode 100644 index f95b0252b6..0000000000 --- a/cmd/vet/testdata/copylock_range.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 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. - -// This file contains tests for the copylock checker's -// range statement analysis. - -package testdata - -import "sync" - -func rangeMutex() { - var mu sync.Mutex - var i int - - var s []sync.Mutex - for range s { - } - for i = range s { - } - for i := range s { - } - for i, _ = range s { - } - for i, _ := range s { - } - for _, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex" - } - for _, m := range s { // ERROR "range var m copies Lock: sync.Mutex" - } - for i, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex" - } - for i, m := range s { // ERROR "range var m copies Lock: sync.Mutex" - } - - var a [3]sync.Mutex - for _, m := range a { // ERROR "range var m copies Lock: sync.Mutex" - } - - var m map[sync.Mutex]sync.Mutex - for k := range m { // ERROR "range var k copies Lock: sync.Mutex" - } - for mu, _ = range m { // ERROR "range var mu copies Lock: sync.Mutex" - } - for k, _ := range m { // ERROR "range var k copies Lock: sync.Mutex" - } - for _, mu = range m { // ERROR "range var mu copies Lock: sync.Mutex" - } - for _, v := range m { // ERROR "range var v copies Lock: sync.Mutex" - } - - var c chan sync.Mutex - for range c { - } - for mu = range c { // ERROR "range var mu copies Lock: sync.Mutex" - } - for v := range c { // ERROR "range var v copies Lock: sync.Mutex" - } - - // Test non-idents in range variables - var t struct { - i int - mu sync.Mutex - } - for t.i, t.mu = range s { // ERROR "range var t.mu copies Lock: sync.Mutex" - } -} diff --git a/cmd/vet/testdata/deadcode.go b/cmd/vet/testdata/deadcode.go deleted file mode 100644 index 5370bc32f6..0000000000 --- a/cmd/vet/testdata/deadcode.go +++ /dev/null @@ -1,2125 +0,0 @@ -// 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. - -// +build ignore - -// This file contains tests for the dead code checker. - -package testdata - -type T int - -var x interface{} -var c chan int - -func external() int // ok - -func _() int { -} - -func _() int { - print(1) -} - -func _() int { - print(1) - return 2 - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - goto L - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -func _() int { - var panic = func(int) {} - print(1) - panic(2) - println() // ok -} - -func _() int { - { - print(1) - return 2 - println() // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - print(1) - return 2 - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - { - print(1) - goto L - println() // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - { - print(1) - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - { - panic(2) - } -} - -func _() int { - print(1) - { - panic(2) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - { - panic(2) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - return 2 - { // ERROR "unreachable code" - } -} - -func _() int { -L: - print(1) - goto L - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - panic(2) - { // ERROR "unreachable code" - } -} - -func _() int { - { - print(1) - return 2 - { // ERROR "unreachable code" - } - } -} - -func _() int { -L: - { - print(1) - goto L - { // ERROR "unreachable code" - } - } -} - -func _() int { - print(1) - { - panic(2) - { // ERROR "unreachable code" - } - } -} - -func _() int { - { - print(1) - return 2 - } - { // ERROR "unreachable code" - } -} - -func _() int { -L: - { - print(1) - goto L - } - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - { - panic(2) - } - { // ERROR "unreachable code" - } -} - -func _() int { - print(1) - if x == nil { - panic(2) - } else { - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 2 { - panic(3) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -// if-else chain missing final else is not okay, even if the -// conditions cover every possible case. - -func _() int { - print(1) - if x == nil { - panic(2) - } else if x != nil { - panic(3) - } - println() // ok -} - -func _() int { - print(1) - if x == nil { - panic(2) - } - println() // ok -} - -func _() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 1 { - panic(3) - } - println() // ok -} - -func _() int { - print(1) - for { - } - println() // ERROR "unreachable code" -} - -func _() int { - for { - for { - break - } - } - println() // ERROR "unreachable code" -} - -func _() int { - for { - for { - break - println() // ERROR "unreachable code" - } - } -} - -func _() int { - for { - for { - continue - println() // ERROR "unreachable code" - } - } -} - -func _() int { - for { - L: - for { - break L - } - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - for { - break - } - println() // ok -} - -func _() int { - for { - for { - } - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - for { - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - for x == nil { - } - println() // ok -} - -func _() int { - for x == nil { - for { - break - } - } - println() // ok -} - -func _() int { - for x == nil { - L: - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - for true { - } - println() // ok -} - -func _() int { - for true { - for { - break - } - } - println() // ok -} - -func _() int { - for true { - L: - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) - select {} - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - for { - } - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - for { - } - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - case c <- 1: - print(2) - goto L - println() // ERROR "unreachable code" - } -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - default: - select {} - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - select {} - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - select { - case <-c: - print(2) - } - println() // ok -} - -func _() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - goto L // ERROR "unreachable code" - case c <- 1: - print(2) - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - print(2) - } - println() // ok -} - -func _() int { - print(1) - select { - default: - break - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - print(1) -L: - select { - case <-c: - print(2) - for { - break L - } - } - println() // ok -} - -func _() int { - print(1) -L: - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - break L - } - println() // ok -} - -func _() int { - print(1) - select { - case <-c: - print(1) - panic("abc") - default: - select {} - break // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x { - default: - return 4 - println() // ERROR "unreachable code" - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - default: - return 4 - case 1: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch { - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - case 2: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 2: - return 4 - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - case 2: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x { - case 1: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x { - default: - return 4 - break // ERROR "unreachable code" - case 1: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x { - case 1: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - println() // ERROR "unreachable code" - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - case int: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -func _() int { - print(1) - switch { - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - case float64: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case float64: - return 4 - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - case float64: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -func _() int { - print(1) - switch x.(type) { - default: - return 4 - break // ERROR "unreachable code" - case int: - print(2) - panic(3) - } - println() // ok -} - -func _() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -// again, but without the leading print(1). -// testing that everything works when the terminating statement is first. - -func _() int { - println() // ok -} - -func _() int { - return 2 - println() // ERROR "unreachable code" -} - -func _() int { -L: - goto L - println() // ERROR "unreachable code" -} - -func _() int { - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -func _() int { - var panic = func(int) {} - panic(2) - println() // ok -} - -func _() int { - { - return 2 - println() // ERROR "unreachable code" - } -} - -func _() int { - { - return 2 - } - println() // ERROR "unreachable code" -} - -func _() int { -L: - { - goto L - println() // ERROR "unreachable code" - } -} - -func _() int { -L: - { - goto L - } - println() // ERROR "unreachable code" -} - -func _() int { - { - panic(2) - println() // ERROR "unreachable code" - } -} - -func _() int { - { - panic(2) - } - println() // ERROR "unreachable code" -} - -func _() int { - return 2 - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - goto L - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - panic(2) - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - return 2 - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { -L: - { - goto L - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { - { - panic(2) - { // ERROR "unreachable code" - } - } - println() // ok -} - -func _() int { - { - return 2 - } - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { -L: - { - goto L - } - { // ERROR "unreachable code" - } - println() // ok -} - -func _() int { - { - panic(2) - } - { // ERROR "unreachable code" - } - println() // ok -} - -// again, with func literals - -var _ = func() int { -} - -var _ = func() int { - print(1) -} - -var _ = func() int { - print(1) - return 2 - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - goto L - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -var _ = func() int { - var panic = func(int) {} - print(1) - panic(2) - println() // ok -} - -var _ = func() int { - { - print(1) - return 2 - println() // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - print(1) - return 2 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - { - print(1) - goto L - println() // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - { - print(1) - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - { - panic(2) - } -} - -var _ = func() int { - print(1) - { - panic(2) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - { - panic(2) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - return 2 - { // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - print(1) - goto L - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - panic(2) - { // ERROR "unreachable code" - } -} - -var _ = func() int { - { - print(1) - return 2 - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { -L: - { - print(1) - goto L - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { - print(1) - { - panic(2) - { // ERROR "unreachable code" - } - } -} - -var _ = func() int { - { - print(1) - return 2 - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - { - print(1) - goto L - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - { - panic(2) - } - { // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } else { - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 2 { - panic(3) - } else { - goto L - } - println() // ERROR "unreachable code" -} - -// if-else chain missing final else is not okay, even if the -// conditions cover every possible case. - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } else if x != nil { - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - if x == nil { - panic(2) - } - println() // ok -} - -var _ = func() int { -L: - print(1) - if x == nil { - panic(2) - } else if x == 1 { - return 0 - } else if x != 1 { - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - for { - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - for { - for { - break - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - for { - for { - break - println() // ERROR "unreachable code" - } - } -} - -var _ = func() int { - for { - for { - continue - println() // ERROR "unreachable code" - } - } -} - -var _ = func() int { - for { - L: - for { - break L - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - for { - break - } - println() // ok -} - -var _ = func() int { - for { - for { - } - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - for { - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - for x == nil { - } - println() // ok -} - -var _ = func() int { - for x == nil { - for { - break - } - } - println() // ok -} - -var _ = func() int { - for x == nil { - L: - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - for true { - } - println() // ok -} - -var _ = func() int { - for true { - for { - break - } - } - println() // ok -} - -var _ = func() int { - for true { - L: - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) - select {} - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - for { - } - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - for { - } - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - case c <- 1: - print(2) - goto L - println() // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - println() // ERROR "unreachable code" - default: - select {} - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - select {} - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - } - println() // ok -} - -var _ = func() int { -L: - print(1) - select { - case <-c: - print(2) - panic("abc") - goto L // ERROR "unreachable code" - case c <- 1: - print(2) - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - default: - print(2) - } - println() // ok -} - -var _ = func() int { - print(1) - select { - default: - break - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(2) - panic("abc") - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - print(1) -L: - select { - case <-c: - print(2) - for { - break L - } - } - println() // ok -} - -var _ = func() int { - print(1) -L: - select { - case <-c: - print(2) - panic("abc") - case c <- 1: - print(2) - break L - } - println() // ok -} - -var _ = func() int { - print(1) - select { - case <-c: - print(1) - panic("abc") - default: - select {} - break // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - println() // ERROR "unreachable code" - case 1: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - case 1: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch { - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - case 2: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 2: - return 4 - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - fallthrough - case 2: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x { - case 1: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x { - default: - return 4 - break // ERROR "unreachable code" - case 1: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x { - case 1: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - println() // ERROR "unreachable code" - case int: - print(2) - panic(3) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - case int: - print(2) - panic(3) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - default: - return 4 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - print(1) - switch { - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - case float64: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case float64: - return 4 - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - fallthrough - case float64: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - panic(3) - break L // ERROR "unreachable code" - default: - return 4 - } - println() // ok -} - -var _ = func() int { - print(1) - switch x.(type) { - default: - return 4 - break // ERROR "unreachable code" - case int: - print(2) - panic(3) - } - println() // ok -} - -var _ = func() int { - print(1) -L: - switch x.(type) { - case int: - print(2) - for { - break L - } - default: - return 4 - } - println() // ok -} - -// again, but without the leading print(1). -// testing that everything works when the terminating statement is first. - -var _ = func() int { - println() // ok -} - -var _ = func() int { - return 2 - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - goto L - println() // ERROR "unreachable code" -} - -var _ = func() int { - panic(2) - println() // ERROR "unreachable code" -} - -// but only builtin panic -var _ = func() int { - var panic = func(int) {} - panic(2) - println() // ok -} - -var _ = func() int { - { - return 2 - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - { - return 2 - } - println() // ERROR "unreachable code" -} - -var _ = func() int { -L: - { - goto L - println() // ERROR "unreachable code" - } -} - -var _ = func() int { -L: - { - goto L - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - { - panic(2) - println() // ERROR "unreachable code" - } -} - -var _ = func() int { - { - panic(2) - } - println() // ERROR "unreachable code" -} - -var _ = func() int { - return 2 - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - goto L - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - panic(2) - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - return 2 - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { -L: - { - goto L - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { - { - panic(2) - { // ERROR "unreachable code" - } - } - println() // ok -} - -var _ = func() int { - { - return 2 - } - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { -L: - { - goto L - } - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() int { - { - panic(2) - } - { // ERROR "unreachable code" - } - println() // ok -} - -var _ = func() { - // goto without label used to panic - goto -} diff --git a/cmd/vet/testdata/divergent/buf.go b/cmd/vet/testdata/divergent/buf.go deleted file mode 100644 index 0efe0f838d..0000000000 --- a/cmd/vet/testdata/divergent/buf.go +++ /dev/null @@ -1,17 +0,0 @@ -// Test of examples with divergent packages. - -// Package buf ... -package buf - -// Buf is a ... -type Buf []byte - -// Append ... -func (*Buf) Append([]byte) {} - -func (Buf) Reset() {} - -func (Buf) Len() int { return 0 } - -// DefaultBuf is a ... -var DefaultBuf Buf diff --git a/cmd/vet/testdata/divergent/buf_test.go b/cmd/vet/testdata/divergent/buf_test.go deleted file mode 100644 index 6b9cba3f01..0000000000 --- a/cmd/vet/testdata/divergent/buf_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Test of examples with divergent packages. - -package buf_test - -func Example() {} // OK because is package-level. - -func Example_suffix() // OK because refers to suffix annotation. - -func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix" - -func ExampleBuf() // OK because refers to known top-level type. - -func ExampleBuf_Append() {} // OK because refers to known method. - -func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear" - -func ExampleBuf_suffix() {} // OK because refers to suffix annotation. - -func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad" - -func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix. - -func ExampleDefaultBuf() {} // OK because refers to top-level identifier. - -func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing" - -func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic" - -// "Puffer" is German for "Buffer". - -func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer" - -func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer" - -func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer" diff --git a/cmd/vet/testdata/examples_test.go b/cmd/vet/testdata/examples_test.go deleted file mode 100644 index 9c53672a7d..0000000000 --- a/cmd/vet/testdata/examples_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Test of examples. - -package testdata - -// Buf is a ... -type Buf []byte - -// Append ... -func (*Buf) Append([]byte) {} - -func (Buf) Reset() {} - -func (Buf) Len() int { return 0 } - -// DefaultBuf is a ... -var DefaultBuf Buf - -func Example() {} // OK because is package-level. - -func Example_goodSuffix() // OK because refers to suffix annotation. - -func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix" - -func ExampleBuf() // OK because refers to known top-level type. - -func ExampleBuf_Append() {} // OK because refers to known method. - -func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear" - -func ExampleBuf_suffix() {} // OK because refers to suffix annotation. - -func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad" - -func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix. - -func ExampleDefaultBuf() {} // OK because refers to top-level identifier. - -func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing" - -func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic" - -// "Puffer" is German for "Buffer". - -func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer" - -func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer" - -func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer" diff --git a/cmd/vet/testdata/incomplete/examples_test.go b/cmd/vet/testdata/incomplete/examples_test.go deleted file mode 100644 index 445502b39e..0000000000 --- a/cmd/vet/testdata/incomplete/examples_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Test of examples. - -package testdata - -func Example() {} // OK because is package-level. - -func Example_suffix() // OK because refers to suffix annotation. - -func Example_BadSuffix() // OK because non-test package was excluded. No false positives wanted. - -func ExampleBuf() // OK because non-test package was excluded. No false positives wanted. - -func ExampleBuf_Append() {} // OK because non-test package was excluded. No false positives wanted. - -func ExampleBuf_Clear() {} // OK because non-test package was excluded. No false positives wanted. - -func ExampleBuf_suffix() {} // OK because refers to suffix annotation. - -func ExampleBuf_Append_Bad() {} // OK because non-test package was excluded. No false positives wanted. - -func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix. - -func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing" - -func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic" - -// "Puffer" is German for "Buffer". - -func ExamplePuffer() // OK because non-test package was excluded. No false positives wanted. - -func ExamplePuffer_Append() // OK because non-test package was excluded. No false positives wanted. - -func ExamplePuffer_suffix() // OK because non-test package was excluded. No false positives wanted. diff --git a/cmd/vet/testdata/method.go b/cmd/vet/testdata/method.go deleted file mode 100644 index 52b500df27..0000000000 --- a/cmd/vet/testdata/method.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2010 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. - -// This file contains tests for the canonical method checker. - -// This file contains the code to check canonical methods. - -package testdata - -import ( - "fmt" -) - -type MethodTest int - -func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan" -} - -type MethodTestInterface interface { - ReadByte() byte // ERROR "should have signature ReadByte" -} diff --git a/cmd/vet/testdata/nilfunc.go b/cmd/vet/testdata/nilfunc.go deleted file mode 100644 index 2ce7bc8ca8..0000000000 --- a/cmd/vet/testdata/nilfunc.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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 testdata - -func F() {} - -type T struct { - F func() -} - -func (T) M() {} - -var Fv = F - -func Comparison() { - var t T - var fn func() - if fn == nil || Fv == nil || t.F == nil { - // no error; these func vars or fields may be nil - } - if F == nil { // ERROR "comparison of function F == nil is always false" - panic("can't happen") - } - if t.M == nil { // ERROR "comparison of function M == nil is always false" - panic("can't happen") - } - if F != nil { // ERROR "comparison of function F != nil is always true" - if t.M != nil { // ERROR "comparison of function M != nil is always true" - return - } - } - panic("can't happen") -} diff --git a/cmd/vet/testdata/print.go b/cmd/vet/testdata/print.go deleted file mode 100644 index 3390a31f2c..0000000000 --- a/cmd/vet/testdata/print.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2010 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. - -// This file contains tests for the printf checker. - -package testdata - -import ( - "fmt" - "math" - "os" - "unsafe" // just for test case printing unsafe.Pointer -) - -func UnsafePointerPrintfTest() { - var up unsafe.Pointer - fmt.Printf("%p, %x %X", up, up, up) -} - -// Error methods that do not satisfy the Error interface and should be checked. -type errorTest1 int - -func (errorTest1) Error(...interface{}) string { - return "hi" -} - -type errorTest2 int // Analogous to testing's *T type. -func (errorTest2) Error(...interface{}) { -} - -type errorTest3 int - -func (errorTest3) Error() { // No return value. -} - -type errorTest4 int - -func (errorTest4) Error() int { // Different return type. - return 3 -} - -type errorTest5 int - -func (errorTest5) error() { // niladic; don't complain if no args (was bug) -} - -// This function never executes, but it serves as a simple test for the program. -// Test with make test. -func PrintfTests() { - var b bool - var i int - var r rune - var s string - var x float64 - var p *int - var imap map[int]int - var fslice []float64 - var c complex64 - // Some good format/argtypes - fmt.Printf("") - fmt.Printf("%b %b %b", 3, i, x) - fmt.Printf("%c %c %c %c", 3, i, 'x', r) - fmt.Printf("%d %d %d", 3, i, imap) - fmt.Printf("%e %e %e %e", 3e9, x, fslice, c) - fmt.Printf("%E %E %E %E", 3e9, x, fslice, c) - fmt.Printf("%f %f %f %f", 3e9, x, fslice, c) - fmt.Printf("%F %F %F %F", 3e9, x, fslice, c) - fmt.Printf("%g %g %g %g", 3e9, x, fslice, c) - fmt.Printf("%G %G %G %G", 3e9, x, fslice, c) - fmt.Printf("%b %b %b %b", 3e9, x, fslice, c) - fmt.Printf("%o %o", 3, i) - fmt.Printf("%p %p", p, nil) - fmt.Printf("%q %q %q %q", 3, i, 'x', r) - fmt.Printf("%s %s %s", "hi", s, []byte{65}) - fmt.Printf("%t %t", true, b) - fmt.Printf("%T %T", 3, i) - fmt.Printf("%U %U", 3, i) - fmt.Printf("%v %v", 3, i) - fmt.Printf("%x %x %x %x", 3, i, "hi", s) - fmt.Printf("%X %X %X %X", 3, i, "hi", s) - fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) - fmt.Printf("%s", &stringerv) - fmt.Printf("%v", &stringerv) - fmt.Printf("%T", &stringerv) - fmt.Printf("%v", notstringerv) - fmt.Printf("%T", notstringerv) - fmt.Printf("%q", stringerarrayv) - fmt.Printf("%v", stringerarrayv) - fmt.Printf("%s", stringerarrayv) - fmt.Printf("%v", notstringerarrayv) - fmt.Printf("%T", notstringerarrayv) - fmt.Printf("%d", new(Formatter)) - fmt.Printf("%*%", 2) // Ridiculous but allowed. - fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say. - - fmt.Printf("%g", 1+2i) - // Some bad format/argTypes - fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" - fmt.Printf("%t", c) // ERROR "arg c for printf verb %t of wrong type" - fmt.Printf("%t", 1+2i) // ERROR "arg 1 \+ 2i for printf verb %t of wrong type" - fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" - fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" - fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" - fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" - fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" - fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" - fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" - fmt.Printf("%g", imap) // ERROR "arg imap for printf verb %g of wrong type" - fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" - fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" - fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" - fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" - fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" - fmt.Printf("%s", byte(65)) // ERROR "arg byte\(65\) for printf verb %s of wrong type" - fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" - fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" - fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" - fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" - fmt.Printf("%s", stringerv) // ERROR "arg stringerv for printf verb %s of wrong type" - fmt.Printf("%t", stringerv) // ERROR "arg stringerv for printf verb %t of wrong type" - fmt.Printf("%q", notstringerv) // ERROR "arg notstringerv for printf verb %q of wrong type" - fmt.Printf("%t", notstringerv) // ERROR "arg notstringerv for printf verb %t of wrong type" - fmt.Printf("%t", stringerarrayv) // ERROR "arg stringerarrayv for printf verb %t of wrong type" - fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type" - fmt.Printf("%q", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %q of wrong type" - fmt.Printf("%d", Formatter(true)) // correct (the type is responsible for formatting) - fmt.Printf("%s", nonemptyinterface) // correct (the dynamic type of nonemptyinterface may be a stringer) - fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" - fmt.Println() // not an error - fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" - fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" - _ = fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Sprintf call" - fmt.Printf("%s%%%d", "hi", 3) // correct - fmt.Printf("%08s", "woo") // correct - fmt.Printf("% 8s", "woo") // correct - fmt.Printf("%.*d", 3, 3) // correct - fmt.Printf("%.*d", 3, 3, 3, 3) // ERROR "wrong number of args for format in Printf call.*4 args" - fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" - fmt.Printf("%.*d", i, 3) // correct - fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" - fmt.Printf("%*%", 0.22) // ERROR "arg 0.22 for \* in printf format not of type int" - fmt.Printf("%q %q", multi()...) // ok - fmt.Printf("%#q", `blah`) // ok - printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("hi") // ok - const format = "%s %s\n" - Printf(format, "hi", "there") - Printf(format, "hi") // ERROR "missing argument for Printf..%s..: format reads arg 2, have only 1" - Printf("%s %d %.3v %q", "str", 4) // ERROR "missing argument for Printf..%.3v..: format reads arg 3, have only 2" - f := new(stringer) - f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" - f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" - f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" - f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" - Printf("d%", 2) // ERROR "missing verb at end of format string in Printf call" - Printf("%d", percentDV) - Printf("%d", &percentDV) - Printf("%d", notPercentDV) // ERROR "arg notPercentDV for printf verb %d of wrong type" - Printf("%d", ¬PercentDV) // ERROR "arg ¬PercentDV for printf verb %d of wrong type" - Printf("%p", ¬PercentDV) // Works regardless: we print it as a pointer. - Printf("%s", percentSV) - Printf("%s", &percentSV) - // Good argument reorderings. - Printf("%[1]d", 3) - Printf("%[1]*d", 3, 1) - Printf("%[2]*[1]d", 1, 3) - Printf("%[2]*.[1]*[3]d", 2, 3, 4) - fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly. - // Bad argument reorderings. - Printf("%[xd", 3) // ERROR "illegal syntax for printf argument index" - Printf("%[x]d", 3) // ERROR "illegal syntax for printf argument index" - Printf("%[3]*s", "hi", 2) // ERROR "missing argument for Printf.* reads arg 3, have only 2" - _ = fmt.Sprintf("%[3]d", 2) // ERROR "missing argument for Sprintf.* reads arg 3, have only 1" - Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int" - Printf("%[0]s", "arg1") // ERROR "index value \[0\] for Printf.*; indexes start at 1" - Printf("%[0]d", 1) // ERROR "index value \[0\] for Printf.*; indexes start at 1" - // Something that satisfies the error interface. - var e error - fmt.Println(e.Error()) // ok - // Something that looks like an error interface but isn't, such as the (*T).Error method - // in the testing package. - var et1 errorTest1 - fmt.Println(et1.Error()) // ERROR "no args in Error call" - fmt.Println(et1.Error("hi")) // ok - fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" - var et2 errorTest2 - et2.Error() // ERROR "no args in Error call" - et2.Error("hi") // ok, not an error method. - et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" - var et3 errorTest3 - et3.Error() // ok, not an error method. - var et4 errorTest4 - et4.Error() // ok, not an error method. - var et5 errorTest5 - et5.error() // ok, not an error method. - // Bug: used to recur forever. - Printf("%p %x", recursiveStructV, recursiveStructV.next) - Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next) - Printf("%p %x", recursiveSliceV, recursiveSliceV) - Printf("%p %x", recursiveMapV, recursiveMapV) - // Special handling for Log. - math.Log(3) // OK - Log(3) // OK - Log("%d", 3) // ERROR "possible formatting directive in Log call" - Logf("%d", 3) - Logf("%d", "hi") // ERROR "arg .hi. for printf verb %d of wrong type: untyped string" - -} - -// Printf is used by the test so we must declare it. -func Printf(format string, args ...interface{}) { - panic("don't call - testing only") -} - -// printf is used by the test so we must declare it. -func printf(format string, args ...interface{}) { - panic("don't call - testing only") -} - -// multi is used by the test. -func multi() []interface{} { - panic("don't call - testing only") -} - -type stringer float64 - -var stringerv stringer - -func (*stringer) String() string { - return "string" -} - -func (*stringer) Warn(int, ...interface{}) string { - return "warn" -} - -func (*stringer) Warnf(int, string, ...interface{}) string { - return "warnf" -} - -type notstringer struct { - f float64 -} - -var notstringerv notstringer - -type stringerarray [4]float64 - -func (stringerarray) String() string { - return "string" -} - -var stringerarrayv stringerarray - -type notstringerarray [4]float64 - -var notstringerarrayv notstringerarray - -var nonemptyinterface = interface { - f() -}(nil) - -// A data type we can print with "%d". -type percentDStruct struct { - a int - b []byte - c *float64 -} - -var percentDV percentDStruct - -// A data type we cannot print correctly with "%d". -type notPercentDStruct struct { - a int - b []byte - c bool -} - -var notPercentDV notPercentDStruct - -// A data type we can print with "%s". -type percentSStruct struct { - a string - b []byte - c stringerarray -} - -var percentSV percentSStruct - -type recursiveStringer int - -func (s recursiveStringer) String() string { - _ = fmt.Sprintf("%d", s) - _ = fmt.Sprintf("%#v", s) - _ = fmt.Sprintf("%v", s) // ERROR "arg s for printf causes recursive call to String method" - _ = fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method" - _ = fmt.Sprintf("%T", s) // ok; does not recursively call String - return fmt.Sprintln(s) // ERROR "arg s for print causes recursive call to String method" -} - -type recursivePtrStringer int - -func (p *recursivePtrStringer) String() string { - _ = fmt.Sprintf("%v", *p) - return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method" -} - -type Formatter bool - -func (*Formatter) Format(fmt.State, rune) { -} - -type RecursiveSlice []RecursiveSlice - -var recursiveSliceV = &RecursiveSlice{} - -type RecursiveMap map[int]RecursiveMap - -var recursiveMapV = make(RecursiveMap) - -type RecursiveStruct struct { - next *RecursiveStruct -} - -var recursiveStructV = &RecursiveStruct{} - -type RecursiveStruct1 struct { - next *Recursive2Struct -} - -type RecursiveStruct2 struct { - next *Recursive1Struct -} - -var recursiveStruct1V = &RecursiveStruct1{} - -// Fix for issue 7149: Missing return type on String method caused fault. -func (int) String() { - return "" -} diff --git a/cmd/vet/testdata/rangeloop.go b/cmd/vet/testdata/rangeloop.go deleted file mode 100644 index 37b5940ddd..0000000000 --- a/cmd/vet/testdata/rangeloop.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2012 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. - -// This file contains tests for the rangeloop checker. - -package testdata - -func RangeLoopTests() { - var s []int - for i, v := range s { - go func() { - println(i) // ERROR "range variable i captured by func literal" - println(v) // ERROR "range variable v captured by func literal" - }() - } - for i, v := range s { - defer func() { - println(i) // ERROR "range variable i captured by func literal" - println(v) // ERROR "range variable v captured by func literal" - }() - } - for i := range s { - go func() { - println(i) // ERROR "range variable i captured by func literal" - }() - } - for _, v := range s { - go func() { - println(v) // ERROR "range variable v captured by func literal" - }() - } - for i, v := range s { - go func() { - println(i, v) - }() - println("unfortunately, we don't catch the error above because of this statement") - } - for i, v := range s { - go func(i, v int) { - println(i, v) - }(i, v) - } - for i, v := range s { - i, v := i, v - go func() { - println(i, v) - }() - } - // If the key of the range statement is not an identifier - // the code should not panic (it used to). - var x [2]int - var f int - for x[0], f = range s { - go func() { - _ = f // ERROR "range variable f captured by func literal" - }() - } -} diff --git a/cmd/vet/testdata/shadow.go b/cmd/vet/testdata/shadow.go deleted file mode 100644 index 34a680681b..0000000000 --- a/cmd/vet/testdata/shadow.go +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -// This file contains tests for the shadowed variable checker. -// Some of these errors are caught by the compiler (shadowed return parameters for example) -// but are nonetheless useful tests. - -package testdata - -import "os" - -func ShadowRead(f *os.File, buf []byte) (err error) { - var x int - if f != nil { - err := 3 // OK - different type. - _ = err - } - if f != nil { - _, err := f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13" - if err != nil { - return err - } - i := 3 // OK - _ = i - } - if f != nil { - var _, err = f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13" - if err != nil { - return err - } - } - for i := 0; i < 10; i++ { - i := i // OK: obviously intentional idiomatic redeclaration - go func() { - println(i) - }() - } - var shadowTemp interface{} - switch shadowTemp := shadowTemp.(type) { // OK: obviously intentional idiomatic redeclaration - case int: - println("OK") - _ = shadowTemp - } - if shadowTemp := shadowTemp; true { // OK: obviously intentional idiomatic redeclaration - var f *os.File // OK because f is not mentioned later in the function. - // The declaration of x is a shadow because x is mentioned below. - var x int // ERROR "declaration of x shadows declaration at testdata/shadow.go:14" - _, _, _ = x, f, shadowTemp - } - // Use a couple of variables to trigger shadowing errors. - _, _ = err, x - return -} diff --git a/cmd/vet/testdata/shift.go b/cmd/vet/testdata/shift.go deleted file mode 100644 index 6624f09cc1..0000000000 --- a/cmd/vet/testdata/shift.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2014 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. - -// This file contains tests for the suspicious shift checker. - -package testdata - -func ShiftTest() { - var i8 int8 - _ = i8 << 7 - _ = (i8 + 1) << 8 // ERROR "\(i8 \+ 1\) too small for shift of 8" - _ = i8 << (7 + 1) // ERROR "i8 too small for shift of 8" - _ = i8 >> 8 // ERROR "i8 too small for shift of 8" - i8 <<= 8 // ERROR "i8 too small for shift of 8" - i8 >>= 8 // ERROR "i8 too small for shift of 8" - var i16 int16 - _ = i16 << 15 - _ = i16 << 16 // ERROR "i16 too small for shift of 16" - _ = i16 >> 16 // ERROR "i16 too small for shift of 16" - i16 <<= 16 // ERROR "i16 too small for shift of 16" - i16 >>= 16 // ERROR "i16 too small for shift of 16" - var i32 int32 - _ = i32 << 31 - _ = i32 << 32 // ERROR "i32 too small for shift of 32" - _ = i32 >> 32 // ERROR "i32 too small for shift of 32" - i32 <<= 32 // ERROR "i32 too small for shift of 32" - i32 >>= 32 // ERROR "i32 too small for shift of 32" - var i64 int64 - _ = i64 << 63 - _ = i64 << 64 // ERROR "i64 too small for shift of 64" - _ = i64 >> 64 // ERROR "i64 too small for shift of 64" - i64 <<= 64 // ERROR "i64 too small for shift of 64" - i64 >>= 64 // ERROR "i64 too small for shift of 64" - var u8 uint8 - _ = u8 << 7 - _ = u8 << 8 // ERROR "u8 too small for shift of 8" - _ = u8 >> 8 // ERROR "u8 too small for shift of 8" - u8 <<= 8 // ERROR "u8 too small for shift of 8" - u8 >>= 8 // ERROR "u8 too small for shift of 8" - var u16 uint16 - _ = u16 << 15 - _ = u16 << 16 // ERROR "u16 too small for shift of 16" - _ = u16 >> 16 // ERROR "u16 too small for shift of 16" - u16 <<= 16 // ERROR "u16 too small for shift of 16" - u16 >>= 16 // ERROR "u16 too small for shift of 16" - var u32 uint32 - _ = u32 << 31 - _ = u32 << 32 // ERROR "u32 too small for shift of 32" - _ = u32 >> 32 // ERROR "u32 too small for shift of 32" - u32 <<= 32 // ERROR "u32 too small for shift of 32" - u32 >>= 32 // ERROR "u32 too small for shift of 32" - var u64 uint64 - _ = u64 << 63 - _ = u64 << 64 // ERROR "u64 too small for shift of 64" - _ = u64 >> 64 // ERROR "u64 too small for shift of 64" - u64 <<= 64 // ERROR "u64 too small for shift of 64" - u64 >>= 64 // ERROR "u64 too small for shift of 64" - _ = u64 << u64 // Non-constant shifts should succeed. - var i int - _ = i << 31 - _ = i << 32 // ERROR "i might be too small for shift of 32" - _ = i >> 32 // ERROR "i might be too small for shift of 32" - i <<= 32 // ERROR "i might be too small for shift of 32" - i >>= 32 // ERROR "i might be too small for shift of 32" - var u uint - _ = u << 31 - _ = u << 32 // ERROR "u might be too small for shift of 32" - _ = u >> 32 // ERROR "u might be too small for shift of 32" - u <<= 32 // ERROR "u might be too small for shift of 32" - u >>= 32 // ERROR "u might be too small for shift of 32" - var p uintptr - _ = p << 31 - _ = p << 32 // ERROR "p might be too small for shift of 32" - _ = p >> 32 // ERROR "p might be too small for shift of 32" - p <<= 32 // ERROR "p might be too small for shift of 32" - p >>= 32 // ERROR "p might be too small for shift of 32" -} diff --git a/cmd/vet/testdata/structtag.go b/cmd/vet/testdata/structtag.go deleted file mode 100644 index 6878f5642d..0000000000 --- a/cmd/vet/testdata/structtag.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010 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. - -// This file contains the test for canonical struct tags. - -package testdata - -type StructTagTest struct { - A int "hello" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair" - B int "\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key" - C int "x:\"y\"\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get" - D int "x:`y`" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" - E int "ct\brl:\"char\"" // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair" - F int `:"emptykey"` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key" - G int `x:"noEndQuote` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" - H int `x:"trunc\x0"` // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value" - OK0 int `x:"y" u:"v" w:""` - OK1 int `x:"y:z" u:"v" w:""` // note multiple colons. - OK2 int "k0:\"values contain spaces\" k1:\"literal\ttabs\" k2:\"and\\tescaped\\tabs\"" - OK3 int `under_scores:"and" CAPS:"ARE_OK"` -} - -type UnexportedEncodingTagTest struct { - x int `json:"xx"` // ERROR "struct field x has json tag but is not exported" - y int `xml:"yy"` // ERROR "struct field y has xml tag but is not exported" - z int - A int `json:"aa" xml:"bb"` -} - -type unexp struct{} - -type JSONEmbeddedField struct { - UnexportedEncodingTagTest `is:"embedded"` - unexp `is:"embedded,notexported" json:"unexp"` // OK for now, see issue 7363 -} diff --git a/cmd/vet/testdata/tagtest/file1.go b/cmd/vet/testdata/tagtest/file1.go deleted file mode 100644 index 22a1509acc..0000000000 --- a/cmd/vet/testdata/tagtest/file1.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2015 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. - -// +build testtag - -package main - -func main() { -} diff --git a/cmd/vet/testdata/tagtest/file2.go b/cmd/vet/testdata/tagtest/file2.go deleted file mode 100644 index ba7dd91bbd..0000000000 --- a/cmd/vet/testdata/tagtest/file2.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2015 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. - -// +build !testtag - -package main - -func ignore() { -} diff --git a/cmd/vet/testdata/unsafeptr.go b/cmd/vet/testdata/unsafeptr.go deleted file mode 100644 index 8f64030b85..0000000000 --- a/cmd/vet/testdata/unsafeptr.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 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 testdata - -import ( - "reflect" - "unsafe" -) - -func f() { - var x unsafe.Pointer - var y uintptr - x = unsafe.Pointer(y) // ERROR "possible misuse of unsafe.Pointer" - y = uintptr(x) - - // only allowed pointer arithmetic is ptr +/- num. - // num+ptr is technically okay but still flagged: write ptr+num instead. - x = unsafe.Pointer(uintptr(x) + 1) - x = unsafe.Pointer(1 + uintptr(x)) // ERROR "possible misuse of unsafe.Pointer" - x = unsafe.Pointer(uintptr(x) + uintptr(x)) // ERROR "possible misuse of unsafe.Pointer" - x = unsafe.Pointer(uintptr(x) - 1) - x = unsafe.Pointer(1 - uintptr(x)) // ERROR "possible misuse of unsafe.Pointer" - - // certain uses of reflect are okay - var v reflect.Value - x = unsafe.Pointer(v.Pointer()) - x = unsafe.Pointer(v.UnsafeAddr()) - var s1 *reflect.StringHeader - x = unsafe.Pointer(s1.Data) - var s2 *reflect.SliceHeader - x = unsafe.Pointer(s2.Data) - var s3 reflect.StringHeader - x = unsafe.Pointer(s3.Data) // ERROR "possible misuse of unsafe.Pointer" - var s4 reflect.SliceHeader - x = unsafe.Pointer(s4.Data) // ERROR "possible misuse of unsafe.Pointer" - - // but only in reflect - var vv V - x = unsafe.Pointer(vv.Pointer()) // ERROR "possible misuse of unsafe.Pointer" - x = unsafe.Pointer(vv.UnsafeAddr()) // ERROR "possible misuse of unsafe.Pointer" - var ss1 *StringHeader - x = unsafe.Pointer(ss1.Data) // ERROR "possible misuse of unsafe.Pointer" - var ss2 *SliceHeader - x = unsafe.Pointer(ss2.Data) // ERROR "possible misuse of unsafe.Pointer" - -} - -type V interface { - Pointer() uintptr - UnsafeAddr() uintptr -} - -type StringHeader struct { - Data uintptr -} - -type SliceHeader struct { - Data uintptr -} diff --git a/cmd/vet/testdata/unused.go b/cmd/vet/testdata/unused.go deleted file mode 100644 index d50f6594d9..0000000000 --- a/cmd/vet/testdata/unused.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 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. - -// This file contains tests for the unusedresult checker. - -package testdata - -import ( - "bytes" - "errors" - "fmt" -) - -func _() { - fmt.Errorf("") // ERROR "result of fmt.Errorf call not used" - _ = fmt.Errorf("") - - errors.New("") // ERROR "result of errors.New call not used" - - err := errors.New("") - err.Error() // ERROR "result of \(error\).Error call not used" - - var buf bytes.Buffer - buf.String() // ERROR "result of \(bytes.Buffer\).String call not used" - - fmt.Sprint("") // ERROR "result of fmt.Sprint call not used" - fmt.Sprintf("") // ERROR "result of fmt.Sprintf call not used" -} diff --git a/cmd/vet/types.go b/cmd/vet/types.go deleted file mode 100644 index 084be85d68..0000000000 --- a/cmd/vet/types.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2010 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. - -// This file contains the pieces of the tool that use typechecking from the go/types package. - -package main - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -// imports is the canonical map of imported packages we need for typechecking. -// It is created during initialization. -var imports = make(map[string]*types.Package) - -var ( - errorType *types.Interface - stringerType *types.Interface // possibly nil - formatterType *types.Interface // possibly nil -) - -func init() { - errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) - - if typ := importType("fmt", "Stringer"); typ != nil { - stringerType = typ.Underlying().(*types.Interface) - } - - if typ := importType("fmt", "Formatter"); typ != nil { - formatterType = typ.Underlying().(*types.Interface) - } -} - -// importType returns the type denoted by the qualified identifier -// path.name, and adds the respective package to the imports map -// as a side effect. In case of an error, importType returns nil. -func importType(path, name string) types.Type { - pkg, err := types.DefaultImport(imports, path) - if err != nil { - // This can happen if the package at path hasn't been compiled yet. - warnf("import failed: %v", err) - return nil - } - if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { - return obj.Type() - } - warnf("invalid type name %q", name) - return nil -} - -func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { - pkg.defs = make(map[*ast.Ident]types.Object) - pkg.uses = make(map[*ast.Ident]types.Object) - pkg.selectors = make(map[*ast.SelectorExpr]*types.Selection) - pkg.spans = make(map[types.Object]Span) - pkg.types = make(map[ast.Expr]types.TypeAndValue) - config := types.Config{ - // We provide the same packages map for all imports to ensure - // that everybody sees identical packages for the given paths. - Packages: imports, - // By providing a Config with our own error function, it will continue - // past the first error. There is no need for that function to do anything. - Error: func(error) {}, - } - info := &types.Info{ - Selections: pkg.selectors, - Types: pkg.types, - Defs: pkg.defs, - Uses: pkg.uses, - } - typesPkg, err := config.Check(pkg.path, fs, astFiles, info) - pkg.typesPkg = typesPkg - // update spans - for id, obj := range pkg.defs { - pkg.growSpan(id, obj) - } - for id, obj := range pkg.uses { - pkg.growSpan(id, obj) - } - return err -} - -// isStruct reports whether the composite literal c is a struct. -// If it is not (probably a struct), it returns a printable form of the type. -func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { - // Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible. - typ := pkg.types[c].Type - // If it's a named type, pull out the underlying type. If it's not, the Underlying - // method returns the type itself. - actual := typ - if actual != nil { - actual = actual.Underlying() - } - if actual == nil { - // No type information available. Assume true, so we do the check. - return true, "" - } - switch actual.(type) { - case *types.Struct: - return true, typ.String() - default: - return false, "" - } -} - -// matchArgType reports an error if printf verb t is not appropriate -// for operand arg. -// -// typ is used only for recursive calls; external callers must supply nil. -// -// (Recursion arises from the compound types {map,chan,slice} which -// may be printed with %d etc. if that is appropriate for their element -// types.) -func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool { - return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool)) -} - -// matchArgTypeInternal is the internal version of matchArgType. It carries a map -// remembering what types are in progress so we don't recur when faced with recursive -// types or mutually recursive types. -func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool { - // %v, %T accept any argument type. - if t == anyType { - return true - } - if typ == nil { - // external call - typ = f.pkg.types[arg].Type - if typ == nil { - return true // probably a type check problem - } - } - // If the type implements fmt.Formatter, we have nothing to check. - // formatterTyp may be nil - be conservative and check for Format method in that case. - if formatterType != nil && types.Implements(typ, formatterType) || f.hasMethod(typ, "Format") { - return true - } - // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? - if t&argString != 0 { - if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) { - return true - } - } - - typ = typ.Underlying() - if inProgress[typ] { - // We're already looking at this type. The call that started it will take care of it. - return true - } - inProgress[typ] = true - - switch typ := typ.(type) { - case *types.Signature: - return t&argPointer != 0 - - case *types.Map: - // Recur: map[int]int matches %d. - return t&argPointer != 0 || - (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)) - - case *types.Chan: - return t&argPointer != 0 - - case *types.Array: - // Same as slice. - if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { - return true // %s matches []byte - } - // Recur: []int matches %d. - return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) - - case *types.Slice: - // Same as array. - if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { - return true // %s matches []byte - } - // Recur: []int matches %d. But watch out for - // type T []T - // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below. - return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) - - case *types.Pointer: - // Ugly, but dealing with an edge case: a known pointer to an invalid type, - // probably something from a failed import. - if typ.Elem().String() == "invalid type" { - if *verbose { - f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg)) - } - return true // special case - } - // If it's actually a pointer with %p, it prints as one. - if t == argPointer { - return true - } - // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct. - if str, ok := typ.Elem().Underlying().(*types.Struct); ok { - return f.matchStructArgType(t, str, arg, inProgress) - } - // The rest can print with %p as pointers, or as integers with %x etc. - return t&(argInt|argPointer) != 0 - - case *types.Struct: - return f.matchStructArgType(t, typ, arg, inProgress) - - case *types.Interface: - // If the static type of the argument is empty interface, there's little we can do. - // Example: - // func f(x interface{}) { fmt.Printf("%s", x) } - // Whether x is valid for %s depends on the type of the argument to f. One day - // we will be able to do better. For now, we assume that empty interface is OK - // but non-empty interfaces, with Stringer and Error handled above, are errors. - return typ.NumMethods() == 0 - - case *types.Basic: - switch typ.Kind() { - case types.UntypedBool, - types.Bool: - return t&argBool != 0 - - case types.UntypedInt, - types.Int, - types.Int8, - types.Int16, - types.Int32, - types.Int64, - types.Uint, - types.Uint8, - types.Uint16, - types.Uint32, - types.Uint64, - types.Uintptr: - return t&argInt != 0 - - case types.UntypedFloat, - types.Float32, - types.Float64: - return t&argFloat != 0 - - case types.UntypedComplex, - types.Complex64, - types.Complex128: - return t&argComplex != 0 - - case types.UntypedString, - types.String: - return t&argString != 0 - - case types.UnsafePointer: - return t&(argPointer|argInt) != 0 - - case types.UntypedRune: - return t&(argInt|argRune) != 0 - - case types.UntypedNil: - return t&argPointer != 0 // TODO? - - case types.Invalid: - if *verbose { - f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg)) - } - return true // Probably a type check problem. - } - panic("unreachable") - } - - return false -} - -// hasBasicType reports whether x's type is a types.Basic with the given kind. -func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool { - t := f.pkg.types[x].Type - if t != nil { - t = t.Underlying() - } - b, ok := t.(*types.Basic) - return ok && b.Kind() == kind -} - -// matchStructArgType reports whether all the elements of the struct match the expected -// type. For instance, with "%d" all the elements must be printable with the "%d" format. -func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool { - for i := 0; i < typ.NumFields(); i++ { - if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) { - return false - } - } - return true -} - -// numArgsInSignature tells how many formal arguments the function type -// being called has. -func (f *File) numArgsInSignature(call *ast.CallExpr) int { - // Check the type of the function or method declaration - typ := f.pkg.types[call.Fun].Type - if typ == nil { - return 0 - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return 0 - } - return sig.Params().Len() -} - -// isErrorMethodCall reports whether the call is of a method with signature -// func Error() string -// where "string" is the universe's string type. We know the method is called "Error". -func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { - typ := f.pkg.types[call].Type - if typ != nil { - // We know it's called "Error", so just check the function signature - // (stringerType has exactly one method, String). - if stringerType != nil && stringerType.NumMethods() == 1 { - return types.Identical(f.pkg.types[call.Fun].Type, stringerType.Method(0).Type()) - } - } - // Without types, we can still check by hand. - // Is it a selector expression? Otherwise it's a function call, not a method call. - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - // The package is type-checked, so if there are no arguments, we're done. - if len(call.Args) > 0 { - return false - } - // Check the type of the method declaration - typ = f.pkg.types[sel].Type - if typ == nil { - return false - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return false - } - // There must be a receiver for it to be a method call. Otherwise it is - // a function, not something that satisfies the error interface. - if sig.Recv() == nil { - return false - } - // There must be no arguments. Already verified by type checking, but be thorough. - if sig.Params().Len() > 0 { - return false - } - // Finally the real questions. - // There must be one result. - if sig.Results().Len() != 1 { - return false - } - // It must have return type "string" from the universe. - return sig.Results().At(0).Type() == types.Typ[types.String] -} - -// hasMethod reports whether the type contains a method with the given name. -// It is part of the workaround for Formatters and should be deleted when -// that workaround is no longer necessary. -// TODO: This could be better once issue 6259 is fixed. -func (f *File) hasMethod(typ types.Type, name string) bool { - // assume we have an addressable variable of type typ - obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name) - _, ok := obj.(*types.Func) - return ok -} diff --git a/cmd/vet/unsafeptr.go b/cmd/vet/unsafeptr.go deleted file mode 100644 index ca15f72578..0000000000 --- a/cmd/vet/unsafeptr.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2014 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. - -// Check for invalid uintptr -> unsafe.Pointer conversions. - -package main - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -func init() { - register("unsafeptr", - "check for misuse of unsafe.Pointer", - checkUnsafePointer, - callExpr) -} - -func checkUnsafePointer(f *File, node ast.Node) { - x := node.(*ast.CallExpr) - if len(x.Args) != 1 { - return - } - if f.hasBasicType(x.Fun, types.UnsafePointer) && f.hasBasicType(x.Args[0], types.Uintptr) && !f.isSafeUintptr(x.Args[0]) { - f.Badf(x.Pos(), "possible misuse of unsafe.Pointer") - } -} - -// isSafeUintptr reports whether x - already known to be a uintptr - -// is safe to convert to unsafe.Pointer. It is safe if x is itself derived -// directly from an unsafe.Pointer via conversion and pointer arithmetic -// or if x is the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr -// or obtained from the Data field of a *reflect.SliceHeader or *reflect.StringHeader. -func (f *File) isSafeUintptr(x ast.Expr) bool { - switch x := x.(type) { - case *ast.ParenExpr: - return f.isSafeUintptr(x.X) - - case *ast.SelectorExpr: - switch x.Sel.Name { - case "Data": - // reflect.SliceHeader and reflect.StringHeader are okay, - // but only if they are pointing at a real slice or string. - // It's not okay to do: - // var x SliceHeader - // x.Data = uintptr(unsafe.Pointer(...)) - // ... use x ... - // p := unsafe.Pointer(x.Data) - // because in the middle the garbage collector doesn't - // see x.Data as a pointer and so x.Data may be dangling - // by the time we get to the conversion at the end. - // For now approximate by saying that *Header is okay - // but Header is not. - pt, ok := f.pkg.types[x.X].Type.(*types.Pointer) - if ok { - t, ok := pt.Elem().(*types.Named) - if ok && t.Obj().Pkg().Path() == "reflect" { - switch t.Obj().Name() { - case "StringHeader", "SliceHeader": - return true - } - } - } - } - - case *ast.CallExpr: - switch len(x.Args) { - case 0: - // maybe call to reflect.Value.Pointer or reflect.Value.UnsafeAddr. - sel, ok := x.Fun.(*ast.SelectorExpr) - if !ok { - break - } - switch sel.Sel.Name { - case "Pointer", "UnsafeAddr": - t, ok := f.pkg.types[sel.X].Type.(*types.Named) - if ok && t.Obj().Pkg().Path() == "reflect" && t.Obj().Name() == "Value" { - return true - } - } - - case 1: - // maybe conversion of uintptr to unsafe.Pointer - return f.hasBasicType(x.Fun, types.Uintptr) && f.hasBasicType(x.Args[0], types.UnsafePointer) - } - - case *ast.BinaryExpr: - switch x.Op { - case token.ADD, token.SUB: - return f.isSafeUintptr(x.X) && !f.isSafeUintptr(x.Y) - } - } - return false -} diff --git a/cmd/vet/unused.go b/cmd/vet/unused.go deleted file mode 100644 index b9d7b65f44..0000000000 --- a/cmd/vet/unused.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015 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. - -// This file defines the check for unused results of calls to certain -// pure functions. - -package main - -import ( - "flag" - "go/ast" - "go/token" - "strings" - - "golang.org/x/tools/go/types" -) - -var unusedFuncsFlag = flag.String("unusedfuncs", - "errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse", - "comma-separated list of functions whose results must be used") - -var unusedStringMethodsFlag = flag.String("unusedstringmethods", - "Error,String", - "comma-separated list of names of methods of type func() string whose results must be used") - -func init() { - register("unusedresult", - "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list", - checkUnusedResult, - exprStmt) -} - -// func() string -var sigNoArgsStringResult = types.NewSignature(nil, nil, - types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), - false) - -var unusedFuncs = make(map[string]bool) -var unusedStringMethods = make(map[string]bool) - -func initUnusedFlags() { - commaSplit := func(s string, m map[string]bool) { - if s != "" { - for _, name := range strings.Split(s, ",") { - if len(name) == 0 { - flag.Usage() - } - m[name] = true - } - } - } - commaSplit(*unusedFuncsFlag, unusedFuncs) - commaSplit(*unusedStringMethodsFlag, unusedStringMethods) -} - -func checkUnusedResult(f *File, n ast.Node) { - call, ok := unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr) - if !ok { - return // not a call statement - } - fun := unparen(call.Fun) - - if f.pkg.types[fun].IsType() { - return // a conversion, not a call - } - - selector, ok := fun.(*ast.SelectorExpr) - if !ok { - return // neither a method call nor a qualified ident - } - - sel, ok := f.pkg.selectors[selector] - if ok && sel.Kind() == types.MethodVal { - // method (e.g. foo.String()) - obj := sel.Obj().(*types.Func) - sig := sel.Type().(*types.Signature) - if types.Identical(sig, sigNoArgsStringResult) { - if unusedStringMethods[obj.Name()] { - f.Badf(call.Lparen, "result of (%s).%s call not used", - sig.Recv().Type(), obj.Name()) - } - } - } else if !ok { - // package-qualified function (e.g. fmt.Errorf) - obj, _ := f.pkg.uses[selector.Sel] - if obj, ok := obj.(*types.Func); ok { - qname := obj.Pkg().Path() + "." + obj.Name() - if unusedFuncs[qname] { - f.Badf(call.Lparen, "result of %v call not used", qname) - } - } - } -} diff --git a/cmd/vet/vet_test.go b/cmd/vet/vet_test.go deleted file mode 100644 index 7508193659..0000000000 --- a/cmd/vet/vet_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// 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. - -// No testdata on Android. - -// +build !android - -package main_test - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" -) - -const ( - dataDir = "testdata" - binary = "testvet.exe" -) - -func CanRun(t *testing.T) bool { - // Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk. - switch runtime.GOOS { - case "plan9", "windows": - t.Skip("skipping test; no Perl on %q", runtime.GOOS) - return false - } - return true -} - -func Build(t *testing.T) { - // go build - cmd := exec.Command("go", "build", "-o", binary) - run(cmd, t) -} - -func Vet(t *testing.T, files []string) { - errchk := filepath.Join(runtime.GOROOT(), "test", "errchk") - flags := []string{ - "./" + binary, - "-printfuncs=Warn:1,Warnf:1", - "-test", // TODO: Delete once -shadow is part of -all. - } - cmd := exec.Command(errchk, append(flags, files...)...) - if !run(cmd, t) { - t.Fatal("vet command failed") - } -} - -// Run this shell script, but do it in Go so it can be run by "go test". -// go build -o testvet -// $(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s -// rm testvet -// - -func TestVet(t *testing.T) { - if !CanRun(t) { - t.Skip("cannot run on this environment") - } - Build(t) - defer os.Remove(binary) - - // errchk ./testvet - gos, err := filepath.Glob(filepath.Join(dataDir, "*.go")) - if err != nil { - t.Fatal(err) - } - asms, err := filepath.Glob(filepath.Join(dataDir, "*.s")) - if err != nil { - t.Fatal(err) - } - files := append(gos, asms...) - Vet(t, files) -} - -func TestDivergentPackagesExamples(t *testing.T) { - if !CanRun(t) { - t.Skip("cannot run on this environment") - } - Build(t) - defer os.Remove(binary) - - // errchk ./testvet - Vet(t, []string{"testdata/divergent/buf.go", "testdata/divergent/buf_test.go"}) -} - -func TestIncompleteExamples(t *testing.T) { - if !CanRun(t) { - t.Skip("cannot run on this environment") - } - Build(t) - defer os.Remove(binary) - - // errchk ./testvet - Vet(t, []string{"testdata/incomplete/examples_test.go"}) -} - -func run(c *exec.Cmd, t *testing.T) bool { - output, err := c.CombinedOutput() - os.Stderr.Write(output) - if err != nil { - t.Fatal(err) - } - // Errchk delights by not returning non-zero status if it finds errors, so we look at the output. - // It prints "BUG" if there is a failure. - if !c.ProcessState.Success() { - return false - } - return !bytes.Contains(output, []byte("BUG")) -} - -// TestTags verifies that the -tags argument controls which files to check. -func TestTags(t *testing.T) { - // go build - cmd := exec.Command("go", "build", "-o", binary) - run(cmd, t) - - defer os.Remove(binary) - - args := []string{ - "-tags=testtag", - "-v", // We're going to look at the files it examines. - "testdata/tagtest", - } - cmd = exec.Command("./"+binary, args...) - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - // file1 has testtag and file2 has !testtag. - if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) { - t.Error("file1 was excluded, should be included") - } - if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) { - t.Error("file2 was included, should be excluded") - } -} diff --git a/cmd/vet/whitelist/whitelist.go b/cmd/vet/whitelist/whitelist.go deleted file mode 100644 index d6f0dce821..0000000000 --- a/cmd/vet/whitelist/whitelist.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 whitelist defines exceptions for the vet tool. -package whitelist // import "golang.org/x/tools/cmd/vet/whitelist" - -// UnkeyedLiteral are types that are actually slices, but -// syntactically, we cannot tell whether the Typ in pkg.Typ{1, 2, 3} -// is a slice or a struct, so we whitelist all the standard package -// library's exported slice types. -var UnkeyedLiteral = map[string]bool{ - /* - find $GOROOT/src -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ - grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/,,' | \ - sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ - sort | awk '{ print "\"" $0 "\": true," }' - */ - "crypto/x509/pkix.RDNSequence": true, - "crypto/x509/pkix.RelativeDistinguishedNameSET": true, - "database/sql.RawBytes": true, - "debug/macho.LoadBytes": true, - "encoding/asn1.ObjectIdentifier": true, - "encoding/asn1.RawContent": true, - "encoding/json.RawMessage": true, - "encoding/xml.CharData": true, - "encoding/xml.Comment": true, - "encoding/xml.Directive": true, - "go/scanner.ErrorList": true, - "image/color.Palette": true, - "net.HardwareAddr": true, - "net.IP": true, - "net.IPMask": true, - "sort.Float64Slice": true, - "sort.IntSlice": true, - "sort.StringSlice": true, - "unicode.SpecialCase": true, - - // These image and image/color struct types are frozen. We will never add fields to them. - "image/color.Alpha16": true, - "image/color.Alpha": true, - "image/color.CMYK": true, - "image/color.Gray16": true, - "image/color.Gray": true, - "image/color.NRGBA64": true, - "image/color.NRGBA": true, - "image/color.RGBA64": true, - "image/color.RGBA": true, - "image/color.YCbCr": true, - "image.Point": true, - "image.Rectangle": true, - "image.Uniform": true, -} diff --git a/go/callgraph/cha/cha14.go b/go/callgraph/cha/cha14.go deleted file mode 100644 index 5a20c8b064..0000000000 --- a/go/callgraph/cha/cha14.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// Package cha computes the call graph of a Go program using the Class -// Hierarchy Analysis (CHA) algorithm. -// -// CHA was first described in "Optimization of Object-Oriented Programs -// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove, -// and Craig Chambers, ECOOP'95. -// -// CHA is related to RTA (see go/callgraph/rta); the difference is that -// CHA conservatively computes the entire "implements" relation between -// interfaces and concrete types ahead of time, whereas RTA uses dynamic -// programming to construct it on the fly as it encounters new functions -// reachable from main. CHA may thus include spurious call edges for -// types that haven't been instantiated yet, or types that are never -// instantiated. -// -// Since CHA conservatively assumes that all functions are address-taken -// and all concrete types are put into interfaces, it is sound to run on -// partial programs, such as libraries without a main or test function. -// -package cha // import "golang.org/x/tools/go/callgraph/cha" - -import ( - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -// CallGraph computes the call graph of the specified program using the -// Class Hierarchy Analysis algorithm. -// -func CallGraph(prog *ssa.Program) *callgraph.Graph { - cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph - - allFuncs := ssautil.AllFunctions(prog) - - // funcsBySig contains all functions, keyed by signature. It is - // the effective set of address-taken functions used to resolve - // a dynamic call of a particular signature. - var funcsBySig typeutil.Map // value is []*ssa.Function - - // methodsByName contains all methods, - // grouped by name for efficient lookup. - methodsByName := make(map[string][]*ssa.Function) - - // methodsMemo records, for every abstract method call call I.f on - // interface type I, the set of concrete methods C.f of all - // types C that satisfy interface I. - methodsMemo := make(map[*types.Func][]*ssa.Function) - lookupMethods := func(m *types.Func) []*ssa.Function { - methods, ok := methodsMemo[m] - if !ok { - I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) - for _, f := range methodsByName[m.Name()] { - C := f.Signature.Recv().Type() // named or *named - if types.Implements(C, I) { - methods = append(methods, f) - } - } - methodsMemo[m] = methods - } - return methods - } - - for f := range allFuncs { - if f.Signature.Recv() == nil { - // Package initializers can never be address-taken. - if f.Name() == "init" && f.Synthetic == "package initializer" { - continue - } - funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) - funcs = append(funcs, f) - funcsBySig.Set(f.Signature, funcs) - } else { - methodsByName[f.Name()] = append(methodsByName[f.Name()], f) - } - } - - addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { - gnode := cg.CreateNode(g) - callgraph.AddEdge(fnode, site, gnode) - } - - addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { - // Because every call to a highly polymorphic and - // frequently used abstract method such as - // (io.Writer).Write is assumed to call every concrete - // Write method in the program, the call graph can - // contain a lot of duplication. - // - // TODO(adonovan): opt: consider factoring the callgraph - // API so that the Callers component of each edge is a - // slice of nodes, not a singleton. - for _, g := range callees { - addEdge(fnode, site, g) - } - } - - for f := range allFuncs { - fnode := cg.CreateNode(f) - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - if site, ok := instr.(ssa.CallInstruction); ok { - call := site.Common() - if call.IsInvoke() { - addEdges(fnode, site, lookupMethods(call.Method)) - } else if g := call.StaticCallee(); g != nil { - addEdge(fnode, site, g) - } else if _, ok := call.Value.(*ssa.Builtin); !ok { - callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) - addEdges(fnode, site, callees) - } - } - } - } - } - - return cg -} diff --git a/go/callgraph/cha/cha14_test.go b/go/callgraph/cha/cha14_test.go deleted file mode 100644 index 7f0aeebbfc..0000000000 --- a/go/callgraph/cha/cha14_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// No testdata on Android. - -// +build !android - -package cha_test - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "sort" - "strings" - "testing" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/callgraph/cha" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -var inputs = []string{ - "testdata/func.go", - "testdata/iface.go", - "testdata/recv.go", -} - -func expectation(f *ast.File) (string, token.Pos) { - for _, c := range f.Comments { - text := strings.TrimSpace(c.Text()) - if t := strings.TrimPrefix(text, "WANT:\n"); t != text { - return t, c.Pos() - } - } - return "", token.NoPos -} - -// TestCHA runs CHA on each file in inputs, prints the dynamic edges of -// the call graph, and compares it with the golden results embedded in -// the WANT comment at the end of the file. -// -func TestCHA(t *testing.T) { - for _, filename := range inputs { - content, err := ioutil.ReadFile(filename) - if err != nil { - t.Errorf("couldn't read file '%s': %s", filename, err) - continue - } - - conf := loader.Config{ - ParserMode: parser.ParseComments, - } - f, err := conf.ParseFile(filename, content) - if err != nil { - t.Error(err) - continue - } - - want, pos := expectation(f) - if pos == token.NoPos { - t.Errorf("No WANT: comment in %s", filename) - continue - } - - conf.CreateFromFiles("main", f) - iprog, err := conf.Load() - if err != nil { - t.Error(err) - continue - } - - prog := ssautil.CreateProgram(iprog, 0) - mainPkg := prog.Package(iprog.Created[0].Pkg) - prog.Build() - - cg := cha.CallGraph(prog) - - if got := printGraph(cg, mainPkg.Pkg); got != want { - t.Errorf("%s: got:\n%s\nwant:\n%s", - prog.Fset.Position(pos), got, want) - } - } -} - -func printGraph(cg *callgraph.Graph, from *types.Package) string { - var edges []string - callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { - if strings.Contains(e.Description(), "dynamic") { - edges = append(edges, fmt.Sprintf("%s --> %s", - e.Caller.Func.RelString(from), - e.Callee.Func.RelString(from))) - } - return nil - }) - sort.Strings(edges) - - var buf bytes.Buffer - buf.WriteString("Dynamic calls\n") - for _, edge := range edges { - fmt.Fprintf(&buf, " %s\n", edge) - } - return strings.TrimSpace(buf.String()) -} diff --git a/go/callgraph/rta/rta14.go b/go/callgraph/rta/rta14.go deleted file mode 100644 index 33956ad02f..0000000000 --- a/go/callgraph/rta/rta14.go +++ /dev/null @@ -1,461 +0,0 @@ -// 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. - -// +build !go1.5 - -// This package provides Rapid Type Analysis (RTA) for Go, a fast -// algorithm for call graph construction and discovery of reachable code -// (and hence dead code) and runtime types. The algorithm was first -// described in: -// -// David F. Bacon and Peter F. Sweeney. 1996. -// Fast static analysis of C++ virtual function calls. (OOPSLA '96) -// http://doi.acm.org/10.1145/236337.236371 -// -// The algorithm uses dynamic programming to tabulate the cross-product -// of the set of known "address taken" functions with the set of known -// dynamic calls of the same type. As each new address-taken function -// is discovered, call graph edges are added from each known callsite, -// and as each new call site is discovered, call graph edges are added -// from it to each known address-taken function. -// -// A similar approach is used for dynamic calls via interfaces: it -// tabulates the cross-product of the set of known "runtime types", -// i.e. types that may appear in an interface value, or be derived from -// one via reflection, with the set of known "invoke"-mode dynamic -// calls. As each new "runtime type" is discovered, call edges are -// added from the known call sites, and as each new call site is -// discovered, call graph edges are added to each compatible -// method. -// -// In addition, we must consider all exported methods of any runtime type -// as reachable, since they may be called via reflection. -// -// Each time a newly added call edge causes a new function to become -// reachable, the code of that function is analyzed for more call sites, -// address-taken functions, and runtime types. The process continues -// until a fixed point is achieved. -// -// The resulting call graph is less precise than one produced by pointer -// analysis, but the algorithm is much faster. For example, running the -// cmd/callgraph tool on its own source takes ~2.1s for RTA and ~5.4s -// for points-to analysis. -// -package rta // import "golang.org/x/tools/go/callgraph/rta" - -// TODO(adonovan): test it by connecting it to the interpreter and -// replacing all "unreachable" functions by a special intrinsic, and -// ensure that that intrinsic is never called. - -import ( - "fmt" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -// A Result holds the results of Rapid Type Analysis, which includes the -// set of reachable functions/methods, runtime types, and the call graph. -// -type Result struct { - // CallGraph is the discovered callgraph. - // It does not include edges for calls made via reflection. - CallGraph *callgraph.Graph - - // Reachable contains the set of reachable functions and methods. - // This includes exported methods of runtime types, since - // they may be accessed via reflection. - // The value indicates whether the function is address-taken. - // - // (We wrap the bool in a struct to avoid inadvertent use of - // "if Reachable[f] {" to test for set membership.) - Reachable map[*ssa.Function]struct{ AddrTaken bool } - - // RuntimeTypes contains the set of types that are needed at - // runtime, for interfaces or reflection. - // - // The value indicates whether the type is inaccessible to reflection. - // Consider: - // type A struct{B} - // fmt.Println(new(A)) - // Types *A, A and B are accessible to reflection, but the unnamed - // type struct{B} is not. - RuntimeTypes typeutil.Map -} - -// Working state of the RTA algorithm. -type rta struct { - result *Result - - prog *ssa.Program - - worklist []*ssa.Function // list of functions to visit - - // addrTakenFuncsBySig contains all address-taken *Functions, grouped by signature. - // Keys are *types.Signature, values are map[*ssa.Function]bool sets. - addrTakenFuncsBySig typeutil.Map - - // dynCallSites contains all dynamic "call"-mode call sites, grouped by signature. - // Keys are *types.Signature, values are unordered []ssa.CallInstruction. - dynCallSites typeutil.Map - - // invokeSites contains all "invoke"-mode call sites, grouped by interface. - // Keys are *types.Interface (never *types.Named), - // Values are unordered []ssa.CallInstruction sets. - invokeSites typeutil.Map - - // The following two maps together define the subset of the - // m:n "implements" relation needed by the algorithm. - - // concreteTypes maps each concrete type to the set of interfaces that it implements. - // Keys are types.Type, values are unordered []*types.Interface. - // Only concrete types used as MakeInterface operands are included. - concreteTypes typeutil.Map - - // interfaceTypes maps each interface type to - // the set of concrete types that implement it. - // Keys are *types.Interface, values are unordered []types.Type. - // Only interfaces used in "invoke"-mode CallInstructions are included. - interfaceTypes typeutil.Map -} - -// addReachable marks a function as potentially callable at run-time, -// and ensures that it gets processed. -func (r *rta) addReachable(f *ssa.Function, addrTaken bool) { - reachable := r.result.Reachable - n := len(reachable) - v := reachable[f] - if addrTaken { - v.AddrTaken = true - } - reachable[f] = v - if len(reachable) > n { - // First time seeing f. Add it to the worklist. - r.worklist = append(r.worklist, f) - } -} - -// addEdge adds the specified call graph edge, and marks it reachable. -// addrTaken indicates whether to mark the callee as "address-taken". -func (r *rta) addEdge(site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) { - r.addReachable(callee, addrTaken) - - if g := r.result.CallGraph; g != nil { - if site.Parent() == nil { - panic(site) - } - from := g.CreateNode(site.Parent()) - to := g.CreateNode(callee) - callgraph.AddEdge(from, site, to) - } -} - -// ---------- addrTakenFuncs × dynCallSites ---------- - -// visitAddrTakenFunc is called each time we encounter an address-taken function f. -func (r *rta) visitAddrTakenFunc(f *ssa.Function) { - // Create two-level map (Signature -> Function -> bool). - S := f.Signature - funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool) - if funcs == nil { - funcs = make(map[*ssa.Function]bool) - r.addrTakenFuncsBySig.Set(S, funcs) - } - if !funcs[f] { - // First time seeing f. - funcs[f] = true - - // If we've seen any dyncalls of this type, mark it reachable, - // and add call graph edges. - sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction) - for _, site := range sites { - r.addEdge(site, f, true) - } - } -} - -// visitDynCall is called each time we encounter a dynamic "call"-mode call. -func (r *rta) visitDynCall(site ssa.CallInstruction) { - S := site.Common().Signature() - - // Record the call site. - sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction) - r.dynCallSites.Set(S, append(sites, site)) - - // For each function of signature S that we know is address-taken, - // mark it reachable. We'll add the callgraph edges later. - funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool) - for g := range funcs { - r.addEdge(site, g, true) - } -} - -// ---------- concrete types × invoke sites ---------- - -// addInvokeEdge is called for each new pair (site, C) in the matrix. -func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) { - // Ascertain the concrete method of C to be called. - imethod := site.Common().Method - cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name())) - r.addEdge(site, cmethod, true) -} - -// visitInvoke is called each time the algorithm encounters an "invoke"-mode call. -func (r *rta) visitInvoke(site ssa.CallInstruction) { - I := site.Common().Value.Type().Underlying().(*types.Interface) - - // Record the invoke site. - sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction) - r.invokeSites.Set(I, append(sites, site)) - - // Add callgraph edge for each existing - // address-taken concrete type implementing I. - for _, C := range r.implementations(I) { - r.addInvokeEdge(site, C) - } -} - -// ---------- main algorithm ---------- - -// visitFunc processes function f. -func (r *rta) visitFunc(f *ssa.Function) { - var space [32]*ssa.Value // preallocate space for common case - - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - rands := instr.Operands(space[:0]) - - switch instr := instr.(type) { - case ssa.CallInstruction: - call := instr.Common() - if call.IsInvoke() { - r.visitInvoke(instr) - } else if g := call.StaticCallee(); g != nil { - r.addEdge(instr, g, false) - } else if _, ok := call.Value.(*ssa.Builtin); !ok { - r.visitDynCall(instr) - } - - // Ignore the call-position operand when - // looking for address-taken Functions. - // Hack: assume this is rands[0]. - rands = rands[1:] - - case *ssa.MakeInterface: - r.addRuntimeType(instr.X.Type(), false) - } - - // Process all address-taken functions. - for _, op := range rands { - if g, ok := (*op).(*ssa.Function); ok { - r.visitAddrTakenFunc(g) - } - } - } - } -} - -// Analyze performs Rapid Type Analysis, starting at the specified root -// functions. It returns nil if no roots were specified. -// -// If buildCallGraph is true, Result.CallGraph will contain a call -// graph; otherwise, only the other fields (reachable functions) are -// populated. -// -func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result { - if len(roots) == 0 { - return nil - } - - r := &rta{ - result: &Result{Reachable: make(map[*ssa.Function]struct{ AddrTaken bool })}, - prog: roots[0].Prog, - } - - if buildCallGraph { - // TODO(adonovan): change callgraph API to eliminate the - // notion of a distinguished root node. Some callgraphs - // have many roots, or none. - r.result.CallGraph = callgraph.New(roots[0]) - } - - hasher := typeutil.MakeHasher() - r.result.RuntimeTypes.SetHasher(hasher) - r.addrTakenFuncsBySig.SetHasher(hasher) - r.dynCallSites.SetHasher(hasher) - r.invokeSites.SetHasher(hasher) - r.concreteTypes.SetHasher(hasher) - r.interfaceTypes.SetHasher(hasher) - - // Visit functions, processing their instructions, and adding - // new functions to the worklist, until a fixed point is - // reached. - var shadow []*ssa.Function // for efficiency, we double-buffer the worklist - r.worklist = append(r.worklist, roots...) - for len(r.worklist) > 0 { - shadow, r.worklist = r.worklist, shadow[:0] - for _, f := range shadow { - r.visitFunc(f) - } - } - return r.result -} - -// interfaces(C) returns all currently known interfaces implemented by C. -func (r *rta) interfaces(C types.Type) []*types.Interface { - // Ascertain set of interfaces C implements - // and update 'implements' relation. - var ifaces []*types.Interface - r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) { - if I := I.(*types.Interface); types.Implements(C, I) { - concs, _ := concs.([]types.Type) - r.interfaceTypes.Set(I, append(concs, C)) - ifaces = append(ifaces, I) - } - }) - r.concreteTypes.Set(C, ifaces) - return ifaces -} - -// implementations(I) returns all currently known concrete types that implement I. -func (r *rta) implementations(I *types.Interface) []types.Type { - var concs []types.Type - if v := r.interfaceTypes.At(I); v != nil { - concs = v.([]types.Type) - } else { - // First time seeing this interface. - // Update the 'implements' relation. - r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) { - if types.Implements(C, I) { - ifaces, _ := ifaces.([]*types.Interface) - r.concreteTypes.Set(C, append(ifaces, I)) - concs = append(concs, C) - } - }) - r.interfaceTypes.Set(I, concs) - } - return concs -} - -// addRuntimeType is called for each concrete type that can be the -// dynamic type of some interface or reflect.Value. -// Adapted from needMethods in go/ssa/builder.go -// -func (r *rta) addRuntimeType(T types.Type, skip bool) { - if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok { - if skip && !prev { - r.result.RuntimeTypes.Set(T, skip) - } - return - } - r.result.RuntimeTypes.Set(T, skip) - - mset := r.prog.MethodSets.MethodSet(T) - - if _, ok := T.Underlying().(*types.Interface); !ok { - // T is a new concrete type. - for i, n := 0, mset.Len(); i < n; i++ { - sel := mset.At(i) - m := sel.Obj() - - if m.Exported() { - // Exported methods are always potentially callable via reflection. - r.addReachable(r.prog.MethodValue(sel), true) - } - } - - // Add callgraph edge for each existing dynamic - // "invoke"-mode call via that interface. - for _, I := range r.interfaces(T) { - sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction) - for _, site := range sites { - r.addInvokeEdge(site, T) - } - } - } - - // Precondition: T is not a method signature (*Signature with Recv()!=nil). - // Recursive case: skip => don't call makeMethods(T). - // Each package maintains its own set of types it has visited. - - var n *types.Named - switch T := T.(type) { - case *types.Named: - n = T - case *types.Pointer: - n, _ = T.Elem().(*types.Named) - } - if n != nil { - owner := n.Obj().Pkg() - if owner == nil { - return // built-in error type - } - } - - // Recursion over signatures of each exported method. - for i := 0; i < mset.Len(); i++ { - if mset.At(i).Obj().Exported() { - sig := mset.At(i).Type().(*types.Signature) - r.addRuntimeType(sig.Params(), true) // skip the Tuple itself - r.addRuntimeType(sig.Results(), true) // skip the Tuple itself - } - } - - switch t := T.(type) { - case *types.Basic: - // nop - - case *types.Interface: - // nop---handled by recursion over method set. - - case *types.Pointer: - r.addRuntimeType(t.Elem(), false) - - case *types.Slice: - r.addRuntimeType(t.Elem(), false) - - case *types.Chan: - r.addRuntimeType(t.Elem(), false) - - case *types.Map: - r.addRuntimeType(t.Key(), false) - r.addRuntimeType(t.Elem(), false) - - case *types.Signature: - if t.Recv() != nil { - panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv())) - } - r.addRuntimeType(t.Params(), true) // skip the Tuple itself - r.addRuntimeType(t.Results(), true) // skip the Tuple itself - - case *types.Named: - // A pointer-to-named type can be derived from a named - // type via reflection. It may have methods too. - r.addRuntimeType(types.NewPointer(T), false) - - // Consider 'type T struct{S}' where S has methods. - // Reflection provides no way to get from T to struct{S}, - // only to S, so the method set of struct{S} is unwanted, - // so set 'skip' flag during recursion. - r.addRuntimeType(t.Underlying(), true) - - case *types.Array: - r.addRuntimeType(t.Elem(), false) - - case *types.Struct: - for i, n := 0, t.NumFields(); i < n; i++ { - r.addRuntimeType(t.Field(i).Type(), false) - } - - case *types.Tuple: - for i, n := 0, t.Len(); i < n; i++ { - r.addRuntimeType(t.At(i).Type(), false) - } - - default: - panic(T) - } -} diff --git a/go/callgraph/rta/rta14_test.go b/go/callgraph/rta/rta14_test.go deleted file mode 100644 index fd0c71d1fd..0000000000 --- a/go/callgraph/rta/rta14_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// No testdata on Android. - -// +build !android - -package rta_test - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "sort" - "strings" - "testing" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/callgraph/rta" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -var inputs = []string{ - "testdata/func.go", - "testdata/rtype.go", - "testdata/iface.go", -} - -func expectation(f *ast.File) (string, token.Pos) { - for _, c := range f.Comments { - text := strings.TrimSpace(c.Text()) - if t := strings.TrimPrefix(text, "WANT:\n"); t != text { - return t, c.Pos() - } - } - return "", token.NoPos -} - -// TestRTA runs RTA on each file in inputs, prints the results, and -// compares it with the golden results embedded in the WANT comment at -// the end of the file. -// -// The results string consists of two parts: the set of dynamic call -// edges, "f --> g", one per line, and the set of reachable functions, -// one per line. Each set is sorted. -// -func TestRTA(t *testing.T) { - for _, filename := range inputs { - content, err := ioutil.ReadFile(filename) - if err != nil { - t.Errorf("couldn't read file '%s': %s", filename, err) - continue - } - - conf := loader.Config{ - ParserMode: parser.ParseComments, - } - f, err := conf.ParseFile(filename, content) - if err != nil { - t.Error(err) - continue - } - - want, pos := expectation(f) - if pos == token.NoPos { - t.Errorf("No WANT: comment in %s", filename) - continue - } - - conf.CreateFromFiles("main", f) - iprog, err := conf.Load() - if err != nil { - t.Error(err) - continue - } - - prog := ssautil.CreateProgram(iprog, 0) - mainPkg := prog.Package(iprog.Created[0].Pkg) - prog.Build() - - res := rta.Analyze([]*ssa.Function{ - mainPkg.Func("main"), - mainPkg.Func("init"), - }, true) - - if got := printResult(res, mainPkg.Pkg); got != want { - t.Errorf("%s: got:\n%s\nwant:\n%s", - prog.Fset.Position(pos), got, want) - } - } -} - -func printResult(res *rta.Result, from *types.Package) string { - var buf bytes.Buffer - - writeSorted := func(ss []string) { - sort.Strings(ss) - for _, s := range ss { - fmt.Fprintf(&buf, " %s\n", s) - } - } - - buf.WriteString("Dynamic calls\n") - var edges []string - callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error { - if strings.Contains(e.Description(), "dynamic") { - edges = append(edges, fmt.Sprintf("%s --> %s", - e.Caller.Func.RelString(from), - e.Callee.Func.RelString(from))) - } - return nil - }) - writeSorted(edges) - - buf.WriteString("Reachable functions\n") - var reachable []string - for f := range res.Reachable { - reachable = append(reachable, f.RelString(from)) - } - writeSorted(reachable) - - buf.WriteString("Reflect types\n") - var rtypes []string - res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) { - if value == false { // accessible to reflection - rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from))) - } - }) - writeSorted(rtypes) - - return strings.TrimSpace(buf.String()) -} diff --git a/go/exact/exact.go b/go/exact/exact.go deleted file mode 100644 index 51c490624a..0000000000 --- a/go/exact/exact.go +++ /dev/null @@ -1,920 +0,0 @@ -// 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 exact implements Values representing untyped -// Go constants and the corresponding operations. Values -// and operations have unlimited precision. -// -// A special Unknown value may be used when a value -// is unknown due to an error. Operations on unknown -// values produce unknown values unless specified -// otherwise. -// -package exact // import "golang.org/x/tools/go/exact" - -import ( - "fmt" - "go/token" - "math/big" - "strconv" -) - -// Kind specifies the kind of value represented by a Value. -type Kind int - -// Implementation note: Kinds must be enumerated in -// order of increasing "complexity" (used by match). - -const ( - // unknown values - Unknown Kind = iota - - // non-numeric values - Bool - String - - // numeric values - Int - Float - Complex -) - -// A Value represents a mathematically exact value of a given Kind. -type Value interface { - // Kind returns the value kind; it is always the smallest - // kind in which the value can be represented exactly. - Kind() Kind - - // String returns a human-readable form of the value. - String() string - - // Prevent external implementations. - implementsValue() -} - -// ---------------------------------------------------------------------------- -// Implementations - -type ( - unknownVal struct{} - boolVal bool - stringVal string - int64Val int64 - intVal struct{ val *big.Int } - floatVal struct{ val *big.Rat } - complexVal struct{ re, im *big.Rat } -) - -func (unknownVal) Kind() Kind { return Unknown } -func (boolVal) Kind() Kind { return Bool } -func (stringVal) Kind() Kind { return String } -func (int64Val) Kind() Kind { return Int } -func (intVal) Kind() Kind { return Int } -func (floatVal) Kind() Kind { return Float } -func (complexVal) Kind() Kind { return Complex } - -func (unknownVal) String() string { return "unknown" } -func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) } -func (x stringVal) String() string { return strconv.Quote(string(x)) } -func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) } -func (x intVal) String() string { return x.val.String() } -func (x floatVal) String() string { return x.val.String() } -func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) } - -func (unknownVal) implementsValue() {} -func (boolVal) implementsValue() {} -func (stringVal) implementsValue() {} -func (int64Val) implementsValue() {} -func (intVal) implementsValue() {} -func (floatVal) implementsValue() {} -func (complexVal) implementsValue() {} - -// int64 bounds -var ( - minInt64 = big.NewInt(-1 << 63) - maxInt64 = big.NewInt(1<<63 - 1) -) - -func normInt(x *big.Int) Value { - if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 { - return int64Val(x.Int64()) - } - return intVal{x} -} - -func normFloat(x *big.Rat) Value { - if x.IsInt() { - return normInt(x.Num()) - } - return floatVal{x} -} - -func normComplex(re, im *big.Rat) Value { - if im.Sign() == 0 { - return normFloat(re) - } - return complexVal{re, im} -} - -// ---------------------------------------------------------------------------- -// Factories - -// MakeUnknown returns the Unknown value. -func MakeUnknown() Value { return unknownVal{} } - -// MakeBool returns the Bool value for x. -func MakeBool(b bool) Value { return boolVal(b) } - -// MakeString returns the String value for x. -func MakeString(s string) Value { return stringVal(s) } - -// MakeInt64 returns the Int value for x. -func MakeInt64(x int64) Value { return int64Val(x) } - -// MakeUint64 returns the Int value for x. -func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) } - -// MakeFloat64 returns the numeric value for x. -// If x is not finite, the result is unknown. -func MakeFloat64(x float64) Value { - if f := new(big.Rat).SetFloat64(x); f != nil { - return normFloat(f) - } - return unknownVal{} -} - -// MakeFromLiteral returns the corresponding integer, floating-point, -// imaginary, character, or string value for a Go literal string. The -// result is nil if the literal string is invalid. -func MakeFromLiteral(lit string, tok token.Token) Value { - switch tok { - case token.INT: - if x, err := strconv.ParseInt(lit, 0, 64); err == nil { - return int64Val(x) - } - if x, ok := new(big.Int).SetString(lit, 0); ok { - return intVal{x} - } - - case token.FLOAT: - if x, ok := new(big.Rat).SetString(lit); ok { - return normFloat(x) - } - - case token.IMAG: - if n := len(lit); n > 0 && lit[n-1] == 'i' { - if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok { - return normComplex(big.NewRat(0, 1), im) - } - } - - case token.CHAR: - if n := len(lit); n >= 2 { - if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil { - return int64Val(code) - } - } - - case token.STRING: - if s, err := strconv.Unquote(lit); err == nil { - return stringVal(s) - } - } - - return nil -} - -// ---------------------------------------------------------------------------- -// Accessors -// -// For unknown arguments the result is the zero value for the respective -// accessor type, except for Sign, where the result is 1. - -// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown. -// If x is Unknown, the result is false. -func BoolVal(x Value) bool { - switch x := x.(type) { - case boolVal: - return bool(x) - case unknownVal: - return false - } - panic(fmt.Sprintf("%v not a Bool", x)) -} - -// StringVal returns the Go string value of x, which must be a String or an Unknown. -// If x is Unknown, the result is "". -func StringVal(x Value) string { - switch x := x.(type) { - case stringVal: - return string(x) - case unknownVal: - return "" - } - panic(fmt.Sprintf("%v not a String", x)) -} - -// Int64Val returns the Go int64 value of x and whether the result is exact; -// x must be an Int or an Unknown. If the result is not exact, its value is undefined. -// If x is Unknown, the result is (0, false). -func Int64Val(x Value) (int64, bool) { - switch x := x.(type) { - case int64Val: - return int64(x), true - case intVal: - return x.val.Int64(), x.val.BitLen() <= 63 - case unknownVal: - return 0, false - } - panic(fmt.Sprintf("%v not an Int", x)) -} - -// Uint64Val returns the Go uint64 value of x and whether the result is exact; -// x must be an Int or an Unknown. If the result is not exact, its value is undefined. -// If x is Unknown, the result is (0, false). -func Uint64Val(x Value) (uint64, bool) { - switch x := x.(type) { - case int64Val: - return uint64(x), x >= 0 - case intVal: - return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64 - case unknownVal: - return 0, false - } - panic(fmt.Sprintf("%v not an Int", x)) -} - -// Float32Val is like Float64Val but for float32 instead of float64. -func Float32Val(x Value) (float32, bool) { - switch x := x.(type) { - case int64Val: - f := float32(x) - return f, int64Val(f) == x - case intVal: - return ratToFloat32(new(big.Rat).SetFrac(x.val, int1)) - case floatVal: - return ratToFloat32(x.val) - case unknownVal: - return 0, false - } - panic(fmt.Sprintf("%v not a Float", x)) -} - -// Float64Val returns the nearest Go float64 value of x and whether the result is exact; -// x must be numeric but not Complex, or Unknown. For values too small (too close to 0) -// to represent as float64, Float64Val silently underflows to 0. The result sign always -// matches the sign of x, even for 0. -// If x is Unknown, the result is (0, false). -func Float64Val(x Value) (float64, bool) { - switch x := x.(type) { - case int64Val: - f := float64(int64(x)) - return f, int64Val(f) == x - case intVal: - return new(big.Rat).SetFrac(x.val, int1).Float64() - case floatVal: - return x.val.Float64() - case unknownVal: - return 0, false - } - panic(fmt.Sprintf("%v not a Float", x)) -} - -// BitLen returns the number of bits required to represent -// the absolute value x in binary representation; x must be an Int or an Unknown. -// If x is Unknown, the result is 0. -func BitLen(x Value) int { - switch x := x.(type) { - case int64Val: - return new(big.Int).SetInt64(int64(x)).BitLen() - case intVal: - return x.val.BitLen() - case unknownVal: - return 0 - } - panic(fmt.Sprintf("%v not an Int", x)) -} - -// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0; -// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0, -// otherwise it is != 0. If x is Unknown, the result is 1. -func Sign(x Value) int { - switch x := x.(type) { - case int64Val: - switch { - case x < 0: - return -1 - case x > 0: - return 1 - } - return 0 - case intVal: - return x.val.Sign() - case floatVal: - return x.val.Sign() - case complexVal: - return x.re.Sign() | x.im.Sign() - case unknownVal: - return 1 // avoid spurious division by zero errors - } - panic(fmt.Sprintf("%v not numeric", x)) -} - -// ---------------------------------------------------------------------------- -// Support for serializing/deserializing integers - -const ( - // Compute the size of a Word in bytes. - _m = ^big.Word(0) - _log = _m>>8&1 + _m>>16&1 + _m>>32&1 - wordSize = 1 << _log -) - -// Bytes returns the bytes for the absolute value of x in little- -// endian binary representation; x must be an Int. -func Bytes(x Value) []byte { - var val *big.Int - switch x := x.(type) { - case int64Val: - val = new(big.Int).SetInt64(int64(x)) - case intVal: - val = x.val - default: - panic(fmt.Sprintf("%v not an Int", x)) - } - - words := val.Bits() - bytes := make([]byte, len(words)*wordSize) - - i := 0 - for _, w := range words { - for j := 0; j < wordSize; j++ { - bytes[i] = byte(w) - w >>= 8 - i++ - } - } - // remove leading 0's - for i > 0 && bytes[i-1] == 0 { - i-- - } - - return bytes[:i] -} - -// MakeFromBytes returns the Int value given the bytes of its little-endian -// binary representation. An empty byte slice argument represents 0. -func MakeFromBytes(bytes []byte) Value { - words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize) - - i := 0 - var w big.Word - var s uint - for _, b := range bytes { - w |= big.Word(b) << s - if s += 8; s == wordSize*8 { - words[i] = w - i++ - w = 0 - s = 0 - } - } - // store last word - if i < len(words) { - words[i] = w - i++ - } - // remove leading 0's - for i > 0 && words[i-1] == 0 { - i-- - } - - return normInt(new(big.Int).SetBits(words[:i])) -} - -// ---------------------------------------------------------------------------- -// Support for disassembling fractions - -// Num returns the numerator of x; x must be Int, Float, or Unknown. -// If x is Unknown, the result is Unknown, otherwise it is an Int -// with the same sign as x. -func Num(x Value) Value { - switch x := x.(type) { - case unknownVal, int64Val, intVal: - return x - case floatVal: - return normInt(x.val.Num()) - } - panic(fmt.Sprintf("%v not Int or Float", x)) -} - -// Denom returns the denominator of x; x must be Int, Float, or Unknown. -// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1. -func Denom(x Value) Value { - switch x := x.(type) { - case unknownVal: - return x - case int64Val, intVal: - return int64Val(1) - case floatVal: - return normInt(x.val.Denom()) - } - panic(fmt.Sprintf("%v not Int or Float", x)) -} - -// ---------------------------------------------------------------------------- -// Support for assembling/disassembling complex numbers - -// MakeImag returns the numeric value x*i (possibly 0); -// x must be Int, Float, or Unknown. -// If x is Unknown, the result is Unknown. -func MakeImag(x Value) Value { - var im *big.Rat - switch x := x.(type) { - case unknownVal: - return x - case int64Val: - im = big.NewRat(int64(x), 1) - case intVal: - im = new(big.Rat).SetFrac(x.val, int1) - case floatVal: - im = x.val - default: - panic(fmt.Sprintf("%v not Int or Float", x)) - } - return normComplex(rat0, im) -} - -// Real returns the real part of x, which must be a numeric or unknown value. -// If x is Unknown, the result is Unknown. -func Real(x Value) Value { - switch x := x.(type) { - case unknownVal, int64Val, intVal, floatVal: - return x - case complexVal: - return normFloat(x.re) - } - panic(fmt.Sprintf("%v not numeric", x)) -} - -// Imag returns the imaginary part of x, which must be a numeric or unknown value. -// If x is Unknown, the result is Unknown. -func Imag(x Value) Value { - switch x := x.(type) { - case unknownVal: - return x - case int64Val, intVal, floatVal: - return int64Val(0) - case complexVal: - return normFloat(x.im) - } - panic(fmt.Sprintf("%v not numeric", x)) -} - -// ---------------------------------------------------------------------------- -// Operations - -// is32bit reports whether x can be represented using 32 bits. -func is32bit(x int64) bool { - const s = 32 - return -1<<(s-1) <= x && x <= 1<<(s-1)-1 -} - -// is63bit reports whether x can be represented using 63 bits. -func is63bit(x int64) bool { - const s = 63 - return -1<<(s-1) <= x && x <= 1<<(s-1)-1 -} - -// UnaryOp returns the result of the unary expression op y. -// The operation must be defined for the operand. -// If size >= 0 it specifies the ^ (xor) result size in bytes. -// If y is Unknown, the result is Unknown. -// -func UnaryOp(op token.Token, y Value, size int) Value { - switch op { - case token.ADD: - switch y.(type) { - case unknownVal, int64Val, intVal, floatVal, complexVal: - return y - } - - case token.SUB: - switch y := y.(type) { - case unknownVal: - return y - case int64Val: - if z := -y; z != y { - return z // no overflow - } - return normInt(new(big.Int).Neg(big.NewInt(int64(y)))) - case intVal: - return normInt(new(big.Int).Neg(y.val)) - case floatVal: - return normFloat(new(big.Rat).Neg(y.val)) - case complexVal: - return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im)) - } - - case token.XOR: - var z big.Int - switch y := y.(type) { - case unknownVal: - return y - case int64Val: - z.Not(big.NewInt(int64(y))) - case intVal: - z.Not(y.val) - default: - goto Error - } - // For unsigned types, the result will be negative and - // thus "too large": We must limit the result size to - // the type's size. - if size >= 0 { - s := uint(size) * 8 - z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)< ord(y) { - y, x = match(y, x) - return x, y - } - // ord(x) <= ord(y) - - switch x := x.(type) { - case unknownVal: - return x, x - - case boolVal, stringVal, complexVal: - return x, y - - case int64Val: - switch y := y.(type) { - case int64Val: - return x, y - case intVal: - return intVal{big.NewInt(int64(x))}, y - case floatVal: - return floatVal{big.NewRat(int64(x), 1)}, y - case complexVal: - return complexVal{big.NewRat(int64(x), 1), rat0}, y - } - - case intVal: - switch y := y.(type) { - case intVal: - return x, y - case floatVal: - return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y - case complexVal: - return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y - } - - case floatVal: - switch y := y.(type) { - case floatVal: - return x, y - case complexVal: - return complexVal{x.val, rat0}, y - } - } - - panic("unreachable") -} - -// BinaryOp returns the result of the binary expression x op y. -// The operation must be defined for the operands. If one of the -// operands is Unknown, the result is Unknown. -// To force integer division of Int operands, use op == token.QUO_ASSIGN -// instead of token.QUO; the result is guaranteed to be Int in this case. -// Division by zero leads to a run-time panic. -// -func BinaryOp(x Value, op token.Token, y Value) Value { - x, y = match(x, y) - - switch x := x.(type) { - case unknownVal: - return x - - case boolVal: - y := y.(boolVal) - switch op { - case token.LAND: - return x && y - case token.LOR: - return x || y - } - - case int64Val: - a := int64(x) - b := int64(y.(int64Val)) - var c int64 - switch op { - case token.ADD: - if !is63bit(a) || !is63bit(b) { - return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b))) - } - c = a + b - case token.SUB: - if !is63bit(a) || !is63bit(b) { - return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b))) - } - c = a - b - case token.MUL: - if !is32bit(a) || !is32bit(b) { - return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b))) - } - c = a * b - case token.QUO: - return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b))) - case token.QUO_ASSIGN: // force integer division - c = a / b - case token.REM: - c = a % b - case token.AND: - c = a & b - case token.OR: - c = a | b - case token.XOR: - c = a ^ b - case token.AND_NOT: - c = a &^ b - default: - goto Error - } - return int64Val(c) - - case intVal: - a := x.val - b := y.(intVal).val - var c big.Int - switch op { - case token.ADD: - c.Add(a, b) - case token.SUB: - c.Sub(a, b) - case token.MUL: - c.Mul(a, b) - case token.QUO: - return normFloat(new(big.Rat).SetFrac(a, b)) - case token.QUO_ASSIGN: // force integer division - c.Quo(a, b) - case token.REM: - c.Rem(a, b) - case token.AND: - c.And(a, b) - case token.OR: - c.Or(a, b) - case token.XOR: - c.Xor(a, b) - case token.AND_NOT: - c.AndNot(a, b) - default: - goto Error - } - return normInt(&c) - - case floatVal: - a := x.val - b := y.(floatVal).val - var c big.Rat - switch op { - case token.ADD: - c.Add(a, b) - case token.SUB: - c.Sub(a, b) - case token.MUL: - c.Mul(a, b) - case token.QUO: - c.Quo(a, b) - default: - goto Error - } - return normFloat(&c) - - case complexVal: - y := y.(complexVal) - a, b := x.re, x.im - c, d := y.re, y.im - var re, im big.Rat - switch op { - case token.ADD: - // (a+c) + i(b+d) - re.Add(a, c) - im.Add(b, d) - case token.SUB: - // (a-c) + i(b-d) - re.Sub(a, c) - im.Sub(b, d) - case token.MUL: - // (ac-bd) + i(bc+ad) - var ac, bd, bc, ad big.Rat - ac.Mul(a, c) - bd.Mul(b, d) - bc.Mul(b, c) - ad.Mul(a, d) - re.Sub(&ac, &bd) - im.Add(&bc, &ad) - case token.QUO: - // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd - var ac, bd, bc, ad, s, cc, dd big.Rat - ac.Mul(a, c) - bd.Mul(b, d) - bc.Mul(b, c) - ad.Mul(a, d) - cc.Mul(c, c) - dd.Mul(d, d) - s.Add(&cc, &dd) - re.Add(&ac, &bd) - re.Quo(&re, &s) - im.Sub(&bc, &ad) - im.Quo(&im, &s) - default: - goto Error - } - return normComplex(&re, &im) - - case stringVal: - if op == token.ADD { - return x + y.(stringVal) - } - } - -Error: - panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y)) -} - -// Shift returns the result of the shift expression x op s -// with op == token.SHL or token.SHR (<< or >>). x must be -// an Int or an Unknown. If x is Unknown, the result is x. -// -func Shift(x Value, op token.Token, s uint) Value { - switch x := x.(type) { - case unknownVal: - return x - - case int64Val: - if s == 0 { - return x - } - switch op { - case token.SHL: - z := big.NewInt(int64(x)) - return normInt(z.Lsh(z, s)) - case token.SHR: - return x >> s - } - - case intVal: - if s == 0 { - return x - } - var z big.Int - switch op { - case token.SHL: - return normInt(z.Lsh(x.val, s)) - case token.SHR: - return normInt(z.Rsh(x.val, s)) - } - } - - panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s)) -} - -func cmpZero(x int, op token.Token) bool { - switch op { - case token.EQL: - return x == 0 - case token.NEQ: - return x != 0 - case token.LSS: - return x < 0 - case token.LEQ: - return x <= 0 - case token.GTR: - return x > 0 - case token.GEQ: - return x >= 0 - } - panic("unreachable") -} - -// Compare returns the result of the comparison x op y. -// The comparison must be defined for the operands. -// If one of the operands is Unknown, the result is -// false. -// -func Compare(x Value, op token.Token, y Value) bool { - x, y = match(x, y) - - switch x := x.(type) { - case unknownVal: - return false - - case boolVal: - y := y.(boolVal) - switch op { - case token.EQL: - return x == y - case token.NEQ: - return x != y - } - - case int64Val: - y := y.(int64Val) - switch op { - case token.EQL: - return x == y - case token.NEQ: - return x != y - case token.LSS: - return x < y - case token.LEQ: - return x <= y - case token.GTR: - return x > y - case token.GEQ: - return x >= y - } - - case intVal: - return cmpZero(x.val.Cmp(y.(intVal).val), op) - - case floatVal: - return cmpZero(x.val.Cmp(y.(floatVal).val), op) - - case complexVal: - y := y.(complexVal) - re := x.re.Cmp(y.re) - im := x.im.Cmp(y.im) - switch op { - case token.EQL: - return re == 0 && im == 0 - case token.NEQ: - return re != 0 || im != 0 - } - - case stringVal: - y := y.(stringVal) - switch op { - case token.EQL: - return x == y - case token.NEQ: - return x != y - case token.LSS: - return x < y - case token.LEQ: - return x <= y - case token.GTR: - return x > y - case token.GEQ: - return x >= y - } - } - - panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y)) -} diff --git a/go/exact/exact_test.go b/go/exact/exact_test.go deleted file mode 100644 index aa38a896c6..0000000000 --- a/go/exact/exact_test.go +++ /dev/null @@ -1,375 +0,0 @@ -// 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 exact - -import ( - "go/token" - "strings" - "testing" -) - -// TODO(gri) expand this test framework - -var opTests = []string{ - // unary operations - `+ 0 = 0`, - `+ ? = ?`, - `- 1 = -1`, - `- ? = ?`, - `^ 0 = -1`, - `^ ? = ?`, - - `! true = false`, - `! false = true`, - `! ? = ?`, - - // etc. - - // binary operations - `"" + "" = ""`, - `"foo" + "" = "foo"`, - `"" + "bar" = "bar"`, - `"foo" + "bar" = "foobar"`, - - `0 + 0 = 0`, - `0 + 0.1 = 0.1`, - `0 + 0.1i = 0.1i`, - `0.1 + 0.9 = 1`, - `1e100 + 1e100 = 2e100`, - `? + 0 = ?`, - `0 + ? = ?`, - - `0 - 0 = 0`, - `0 - 0.1 = -0.1`, - `0 - 0.1i = -0.1i`, - `1e100 - 1e100 = 0`, - `? - 0 = ?`, - `0 - ? = ?`, - - `0 * 0 = 0`, - `1 * 0.1 = 0.1`, - `1 * 0.1i = 0.1i`, - `1i * 1i = -1`, - `? * 0 = ?`, - `0 * ? = ?`, - - `0 / 0 = "division_by_zero"`, - `10 / 2 = 5`, - `5 / 3 = 5/3`, - `5i / 3i = 5/3`, - `? / 0 = ?`, - `0 / ? = ?`, - - `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for / - `10 % 3 = 1`, - `? % 0 = ?`, - `0 % ? = ?`, - - `0 & 0 = 0`, - `12345 & 0 = 0`, - `0xff & 0xf = 0xf`, - `? & 0 = ?`, - `0 & ? = ?`, - - `0 | 0 = 0`, - `12345 | 0 = 12345`, - `0xb | 0xa0 = 0xab`, - `? | 0 = ?`, - `0 | ? = ?`, - - `0 ^ 0 = 0`, - `1 ^ -1 = -2`, - `? ^ 0 = ?`, - `0 ^ ? = ?`, - - `0 &^ 0 = 0`, - `0xf &^ 1 = 0xe`, - `1 &^ 0xf = 0`, - // etc. - - // shifts - `0 << 0 = 0`, - `1 << 10 = 1024`, - `0 >> 0 = 0`, - `1024 >> 10 == 1`, - `? << 0 == ?`, - `? >> 10 == ?`, - // etc. - - // comparisons - `false == false = true`, - `false == true = false`, - `true == false = false`, - `true == true = true`, - - `false != false = false`, - `false != true = true`, - `true != false = true`, - `true != true = false`, - - `"foo" == "bar" = false`, - `"foo" != "bar" = true`, - `"foo" < "bar" = false`, - `"foo" <= "bar" = false`, - `"foo" > "bar" = true`, - `"foo" >= "bar" = true`, - - `0 == 0 = true`, - `0 != 0 = false`, - `0 < 10 = true`, - `10 <= 10 = true`, - `0 > 10 = false`, - `10 >= 10 = true`, - - `1/123456789 == 1/123456789 == true`, - `1/123456789 != 1/123456789 == false`, - `1/123456789 < 1/123456788 == true`, - `1/123456788 <= 1/123456789 == false`, - `0.11 > 0.11 = false`, - `0.11 >= 0.11 = true`, - - `? == 0 = false`, - `? != 0 = false`, - `? < 10 = false`, - `? <= 10 = false`, - `? > 10 = false`, - `? >= 10 = false`, - - `0 == ? = false`, - `0 != ? = false`, - `0 < ? = false`, - `10 <= ? = false`, - `0 > ? = false`, - `10 >= ? = false`, - - // etc. -} - -func TestOps(t *testing.T) { - for _, test := range opTests { - a := strings.Split(test, " ") - i := 0 // operator index - - var x, x0 Value - switch len(a) { - case 4: - // unary operation - case 5: - // binary operation - x, x0 = val(a[0]), val(a[0]) - i = 1 - default: - t.Errorf("invalid test case: %s", test) - continue - } - - op, ok := optab[a[i]] - if !ok { - panic("missing optab entry for " + a[i]) - } - - y, y0 := val(a[i+1]), val(a[i+1]) - - got := doOp(x, op, y) - want := val(a[i+3]) - if !eql(got, want) { - t.Errorf("%s: got %s; want %s", test, got, want) - } - if x0 != nil && !eql(x, x0) { - t.Errorf("%s: x changed to %s", test, x) - } - if !eql(y, y0) { - t.Errorf("%s: y changed to %s", test, y) - } - } -} - -func eql(x, y Value) bool { - _, ux := x.(unknownVal) - _, uy := y.(unknownVal) - if ux || uy { - return ux == uy - } - return Compare(x, token.EQL, y) -} - -// ---------------------------------------------------------------------------- -// Support functions - -func val(lit string) Value { - if len(lit) == 0 { - return MakeUnknown() - } - - switch lit { - case "?": - return MakeUnknown() - case "true": - return MakeBool(true) - case "false": - return MakeBool(false) - } - - tok := token.INT - switch first, last := lit[0], lit[len(lit)-1]; { - case first == '"' || first == '`': - tok = token.STRING - lit = strings.Replace(lit, "_", " ", -1) - case first == '\'': - tok = token.CHAR - case last == 'i': - tok = token.IMAG - default: - if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") { - tok = token.FLOAT - } - } - - return MakeFromLiteral(lit, tok) -} - -var optab = map[string]token.Token{ - "!": token.NOT, - - "+": token.ADD, - "-": token.SUB, - "*": token.MUL, - "/": token.QUO, - "%": token.REM, - - "<<": token.SHL, - ">>": token.SHR, - - "&": token.AND, - "|": token.OR, - "^": token.XOR, - "&^": token.AND_NOT, - - "==": token.EQL, - "!=": token.NEQ, - "<": token.LSS, - "<=": token.LEQ, - ">": token.GTR, - ">=": token.GEQ, -} - -func panicHandler(v *Value) { - switch p := recover().(type) { - case nil: - // nothing to do - case string: - *v = MakeString(p) - case error: - *v = MakeString(p.Error()) - default: - panic(p) - } -} - -func doOp(x Value, op token.Token, y Value) (z Value) { - defer panicHandler(&z) - - if x == nil { - return UnaryOp(op, y, -1) - } - - switch op { - case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: - return MakeBool(Compare(x, op, y)) - case token.SHL, token.SHR: - s, _ := Int64Val(y) - return Shift(x, op, uint(s)) - default: - return BinaryOp(x, op, y) - } -} - -// ---------------------------------------------------------------------------- -// Other tests - -var fracTests = []string{ - "0 0 1", - "1 1 1", - "-1 -1 1", - "1.2 6 5", - "-0.991 -991 1000", - "1e100 1e100 1", -} - -func TestFractions(t *testing.T) { - for _, test := range fracTests { - a := strings.Split(test, " ") - if len(a) != 3 { - t.Errorf("invalid test case: %s", test) - continue - } - - x := val(a[0]) - n := val(a[1]) - d := val(a[2]) - - if got := Num(x); !eql(got, n) { - t.Errorf("%s: got num = %s; want %s", test, got, n) - } - - if got := Denom(x); !eql(got, d) { - t.Errorf("%s: got denom = %s; want %s", test, got, d) - } - } -} - -var bytesTests = []string{ - "0", - "1", - "123456789", - "123456789012345678901234567890123456789012345678901234567890", -} - -func TestBytes(t *testing.T) { - for _, test := range bytesTests { - x := val(test) - bytes := Bytes(x) - - // special case 0 - if Sign(x) == 0 && len(bytes) != 0 { - t.Errorf("%s: got %v; want empty byte slice", test, bytes) - } - - if n := len(bytes); n > 0 && bytes[n-1] == 0 { - t.Errorf("%s: got %v; want no leading 0 byte", test, bytes) - } - - if got := MakeFromBytes(bytes); !eql(got, x) { - t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes) - } - } -} - -func TestUnknown(t *testing.T) { - u := MakeUnknown() - var values = []Value{ - u, - MakeBool(false), // token.ADD ok below, operation is never considered - MakeString(""), - MakeInt64(1), - MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT), - MakeFloat64(1.2), - MakeImag(MakeFloat64(1.2)), - } - for _, val := range values { - x, y := val, u - for i := range [2]int{} { - if i == 1 { - x, y = y, x - } - if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown { - t.Errorf("%s + %s: got %s; want %s", x, y, got, u) - } - if got := Compare(x, token.EQL, y); got { - t.Errorf("%s == %s: got true; want false", x, y) - } - } - } -} diff --git a/go/exact/go13.go b/go/exact/go13.go deleted file mode 100644 index 1016c14150..0000000000 --- a/go/exact/go13.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.4 - -package exact - -import ( - "math" - "math/big" -) - -func ratToFloat32(x *big.Rat) (float32, bool) { - // Before 1.4, there's no Rat.Float32. - // Emulate it, albeit at the cost of - // imprecision in corner cases. - x64, exact := x.Float64() - x32 := float32(x64) - if math.IsInf(float64(x32), 0) { - exact = false - } - return x32, exact -} diff --git a/go/exact/go14.go b/go/exact/go14.go deleted file mode 100644 index b86e5d2609..0000000000 --- a/go/exact/go14.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 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. - -// +build go1.4 - -package exact - -import "math/big" - -func ratToFloat32(x *big.Rat) (float32, bool) { - return x.Float32() -} diff --git a/go/gccgoimporter/gccgoinstallation.go b/go/gccgoimporter/gccgoinstallation.go deleted file mode 100644 index 1c56cf5a5b..0000000000 --- a/go/gccgoimporter/gccgoinstallation.go +++ /dev/null @@ -1,95 +0,0 @@ -// 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 gccgoimporter - -import ( - "bufio" - "os" - "os/exec" - "path/filepath" - "strings" - - "golang.org/x/tools/go/types" -) - -// Information about a specific installation of gccgo. -type GccgoInstallation struct { - // Version of gcc (e.g. 4.8.0). - GccVersion string - - // Target triple (e.g. x86_64-unknown-linux-gnu). - TargetTriple string - - // Built-in library paths used by this installation. - LibPaths []string -} - -// Ask the driver at the given path for information for this GccgoInstallation. -func (inst *GccgoInstallation) InitFromDriver(gccgoPath string) (err error) { - cmd := exec.Command(gccgoPath, "-###", "-S", "-x", "go", "-") - stderr, err := cmd.StderrPipe() - if err != nil { - return - } - - err = cmd.Start() - if err != nil { - return - } - - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - line := scanner.Text() - switch { - case strings.HasPrefix(line, "Target: "): - inst.TargetTriple = line[8:] - - case line[0] == ' ': - args := strings.Fields(line) - for _, arg := range args[1:] { - if strings.HasPrefix(arg, "-L") { - inst.LibPaths = append(inst.LibPaths, arg[2:]) - } - } - } - } - - stdout, err := exec.Command(gccgoPath, "-dumpversion").Output() - if err != nil { - return - } - inst.GccVersion = strings.TrimSpace(string(stdout)) - - return -} - -// Return the list of export search paths for this GccgoInstallation. -func (inst *GccgoInstallation) SearchPaths() (paths []string) { - for _, lpath := range inst.LibPaths { - spath := filepath.Join(lpath, "go", inst.GccVersion) - fi, err := os.Stat(spath) - if err != nil || !fi.IsDir() { - continue - } - paths = append(paths, spath) - - spath = filepath.Join(spath, inst.TargetTriple) - fi, err = os.Stat(spath) - if err != nil || !fi.IsDir() { - continue - } - paths = append(paths, spath) - } - - paths = append(paths, inst.LibPaths...) - - return -} - -// Return an importer that searches incpaths followed by the gcc installation's -// built-in search paths and the current directory. -func (inst *GccgoInstallation) GetImporter(incpaths []string, initmap map[*types.Package]InitData) types.Importer { - return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap) -} diff --git a/go/gccgoimporter/gccgoinstallation_test.go b/go/gccgoimporter/gccgoinstallation_test.go deleted file mode 100644 index 9ab928dce3..0000000000 --- a/go/gccgoimporter/gccgoinstallation_test.go +++ /dev/null @@ -1,194 +0,0 @@ -// 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 gccgoimporter - -import ( - "runtime" - "testing" - - "golang.org/x/tools/go/types" -) - -var importablePackages = [...]string{ - "archive/tar", - "archive/zip", - "bufio", - "bytes", - "compress/bzip2", - "compress/flate", - "compress/gzip", - "compress/lzw", - "compress/zlib", - "container/heap", - "container/list", - "container/ring", - "crypto/aes", - "crypto/cipher", - "crypto/des", - "crypto/dsa", - "crypto/ecdsa", - "crypto/elliptic", - "crypto", - "crypto/hmac", - "crypto/md5", - "crypto/rand", - "crypto/rc4", - "crypto/rsa", - "crypto/sha1", - "crypto/sha256", - "crypto/sha512", - "crypto/subtle", - "crypto/tls", - "crypto/x509", - "crypto/x509/pkix", - "database/sql/driver", - "database/sql", - "debug/dwarf", - "debug/elf", - "debug/gosym", - "debug/macho", - "debug/pe", - "encoding/ascii85", - "encoding/asn1", - "encoding/base32", - "encoding/base64", - "encoding/binary", - "encoding/csv", - "encoding/gob", - "encoding", - "encoding/hex", - "encoding/json", - "encoding/pem", - "encoding/xml", - "errors", - "exp/proxy", - "exp/terminal", - "expvar", - "flag", - "fmt", - "go/ast", - "go/build", - "go/doc", - "go/format", - "go/parser", - "go/printer", - "go/scanner", - "go/token", - "hash/adler32", - "hash/crc32", - "hash/crc64", - "hash/fnv", - "hash", - "html", - "html/template", - "image/color", - "image/color/palette", - "image/draw", - "image/gif", - "image", - "image/jpeg", - "image/png", - "index/suffixarray", - "io", - "io/ioutil", - "log", - "log/syslog", - "math/big", - "math/cmplx", - "math", - "math/rand", - "mime", - "mime/multipart", - "net", - "net/http/cgi", - "net/http/cookiejar", - "net/http/fcgi", - "net/http", - "net/http/httptest", - "net/http/httputil", - "net/http/pprof", - "net/mail", - "net/rpc", - "net/rpc/jsonrpc", - "net/smtp", - "net/textproto", - "net/url", - "old/regexp", - "old/template", - "os/exec", - "os", - "os/signal", - "os/user", - "path/filepath", - "path", - "reflect", - "regexp", - "regexp/syntax", - "runtime/debug", - "runtime", - "runtime/pprof", - "sort", - "strconv", - "strings", - "sync/atomic", - "sync", - "syscall", - "testing", - "testing/iotest", - "testing/quick", - "text/scanner", - "text/tabwriter", - "text/template", - "text/template/parse", - "time", - "unicode", - "unicode/utf16", - "unicode/utf8", -} - -func TestInstallationImporter(t *testing.T) { - // This test relies on gccgo being around, which it most likely will be if we - // were compiled with gccgo. - if runtime.Compiler != "gccgo" { - t.Skip("This test needs gccgo") - return - } - - var inst GccgoInstallation - err := inst.InitFromDriver("gccgo") - if err != nil { - t.Fatal(err) - } - imp := inst.GetImporter(nil, nil) - - // Ensure we don't regress the number of packages we can parse. First import - // all packages into the same map and then each individually. - pkgMap := make(map[string]*types.Package) - for _, pkg := range importablePackages { - _, err = imp(pkgMap, pkg) - if err != nil { - t.Error(err) - } - } - - for _, pkg := range importablePackages { - _, err = imp(make(map[string]*types.Package), pkg) - if err != nil { - t.Error(err) - } - } - - // Test for certain specific entities in the imported data. - for _, test := range [...]importerTest{ - {pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []uint8) (n int, err error)}"}, - {pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"}, - {pkgpath: "math", name: "Pi", want: "const Pi untyped float"}, - {pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"}, - {pkgpath: "sort", name: "Ints", want: "func Ints(a []int)"}, - {pkgpath: "unsafe", name: "Pointer", want: "type Pointer unsafe.Pointer"}, - } { - runImporterTest(t, imp, nil, &test) - } -} diff --git a/go/gccgoimporter/importer.go b/go/gccgoimporter/importer.go deleted file mode 100644 index ac484af9be..0000000000 --- a/go/gccgoimporter/importer.go +++ /dev/null @@ -1,205 +0,0 @@ -// 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 gccgoimporter implements Import for gccgo-generated object files. -package gccgoimporter // import "golang.org/x/tools/go/gccgoimporter" - -import ( - "bytes" - "debug/elf" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" - - "golang.org/x/tools/go/importer" - "golang.org/x/tools/go/types" -) - -// A PackageInit describes an imported package that needs initialization. -type PackageInit struct { - Name string // short package name - InitFunc string // name of init function - Priority int // priority of init function, see InitData.Priority -} - -// The gccgo-specific init data for a package. -type InitData struct { - // Initialization priority of this package relative to other packages. - // This is based on the maximum depth of the package's dependency graph; - // it is guaranteed to be greater than that of its dependencies. - Priority int - - // The list of packages which this package depends on to be initialized, - // including itself if needed. This is the subset of the transitive closure of - // the package's dependencies that need initialization. - Inits []PackageInit -} - -// Locate the file from which to read export data. -// This is intended to replicate the logic in gofrontend. -func findExportFile(searchpaths []string, pkgpath string) (string, error) { - for _, spath := range searchpaths { - pkgfullpath := filepath.Join(spath, pkgpath) - pkgdir, name := filepath.Split(pkgfullpath) - - for _, filepath := range [...]string{ - pkgfullpath, - pkgfullpath + ".gox", - pkgdir + "lib" + name + ".so", - pkgdir + "lib" + name + ".a", - pkgfullpath + ".o", - } { - fi, err := os.Stat(filepath) - if err == nil && !fi.IsDir() { - return filepath, nil - } - } - } - - return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) -} - -const ( - gccgov1Magic = "v1;\n" - goimporterMagic = "\n$$ " - archiveMagic = "! 0 { - initdata := initmap[pkg] - found := false - // Check that the package's own init function has the package's priority - for _, pkginit := range initdata.Inits { - if pkginit.InitFunc == test.wantinits[0] { - if initdata.Priority != pkginit.Priority { - t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority) - } - found = true - break - } - } - - if !found { - t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0]) - } - - // Each init function in the list other than the first one is a - // dependency of the function immediately before it. Check that - // the init functions appear in descending priority order. - priority := initdata.Priority - for _, wantdepinit := range test.wantinits[1:] { - found = false - for _, pkginit := range initdata.Inits { - if pkginit.InitFunc == wantdepinit { - if priority <= pkginit.Priority { - t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority) - } - found = true - priority = pkginit.Priority - break - } - } - - if !found { - t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit) - } - } - } -} - -var importerTests = [...]importerTest{ - {pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"}, - {pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1/1 + -1/1i)"}, - {pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1/1 + 1/1i)"}, - {pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1/1 + -1/1i)"}, - {pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1/1 + 1/1i)"}, - // TODO: enable this entry once bug has been tracked down - //{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}}, -} - -func TestGoxImporter(t *testing.T) { - if runtime.GOOS == "android" { - t.Skipf("no testdata directory on %s", runtime.GOOS) - } - initmap := make(map[*types.Package]InitData) - imp := GetImporter([]string{"testdata"}, initmap) - - for _, test := range importerTests { - runImporterTest(t, imp, initmap, &test) - } -} - -func TestObjImporter(t *testing.T) { - // This test relies on gccgo being around, which it most likely will be if we - // were compiled with gccgo. - if runtime.Compiler != "gccgo" { - t.Skip("This test needs gccgo") - return - } - - tmpdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - initmap := make(map[*types.Package]InitData) - imp := GetImporter([]string{tmpdir}, initmap) - - artmpdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - arinitmap := make(map[*types.Package]InitData) - arimp := GetImporter([]string{artmpdir}, arinitmap) - - for _, test := range importerTests { - gofile := filepath.Join("testdata", test.pkgpath+".go") - ofile := filepath.Join(tmpdir, test.pkgpath+".o") - afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a") - - cmd := exec.Command("gccgo", "-fgo-pkgpath="+test.pkgpath, "-c", "-o", ofile, gofile) - out, err := cmd.CombinedOutput() - if err != nil { - t.Logf("%s", out) - t.Fatalf("gccgo %s failed: %s", gofile, err) - } - - runImporterTest(t, imp, initmap, &test) - - cmd = exec.Command("ar", "cr", afile, ofile) - out, err = cmd.CombinedOutput() - if err != nil { - t.Logf("%s", out) - t.Fatalf("ar cr %s %s failed: %s", afile, ofile, err) - } - - runImporterTest(t, arimp, arinitmap, &test) - - if err = os.Remove(ofile); err != nil { - t.Fatal(err) - } - if err = os.Remove(afile); err != nil { - t.Fatal(err) - } - } - - if err = os.Remove(tmpdir); err != nil { - t.Fatal(err) - } -} diff --git a/go/gccgoimporter/parser.go b/go/gccgoimporter/parser.go deleted file mode 100644 index d20a967313..0000000000 --- a/go/gccgoimporter/parser.go +++ /dev/null @@ -1,856 +0,0 @@ -// 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 gccgoimporter - -import ( - "bytes" - "errors" - "fmt" - "go/token" - "io" - "strconv" - "strings" - "text/scanner" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -type parser struct { - scanner scanner.Scanner - tok rune // current token - lit string // literal string; only valid for Ident, Int, String tokens - pkgpath string // package path of imported package - pkgname string // name of imported package - pkg *types.Package // reference to imported package - imports map[string]*types.Package // package path -> package object - typeMap map[int]types.Type // type number -> type - initdata InitData // package init priority data -} - -func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) { - p.scanner.Init(src) - p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } - p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments - p.scanner.Whitespace = 1<<'\t' | 1<<'\n' | 1<<' ' - p.scanner.Filename = filename // for good error messages - p.next() - p.imports = imports - p.typeMap = make(map[int]types.Type) -} - -type importError struct { - pos scanner.Position - err error -} - -func (e importError) Error() string { - return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) -} - -func (p *parser) error(err interface{}) { - if s, ok := err.(string); ok { - err = errors.New(s) - } - // panic with a runtime.Error if err is not an error - panic(importError{p.scanner.Pos(), err.(error)}) -} - -func (p *parser) errorf(format string, args ...interface{}) { - p.error(fmt.Errorf(format, args...)) -} - -func (p *parser) expect(tok rune) string { - lit := p.lit - if p.tok != tok { - p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) - } - p.next() - return lit -} - -func (p *parser) expectKeyword(keyword string) { - lit := p.expect(scanner.Ident) - if lit != keyword { - p.errorf("expected keyword %s, got %q", keyword, lit) - } -} - -func (p *parser) parseString() string { - str, err := strconv.Unquote(p.expect(scanner.String)) - if err != nil { - p.error(err) - } - return str -} - -// unquotedString = { unquotedStringChar } . -// unquotedStringChar = . -func (p *parser) parseUnquotedString() string { - if p.tok == scanner.EOF { - p.error("unexpected EOF") - } - var buf bytes.Buffer - buf.WriteString(p.scanner.TokenText()) - // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon, - // we need to let it be consumed by p.next(). - for ch := p.scanner.Peek(); ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1< 0 { - p.expect(',') - } - par, variadic := p.parseParam(pkg) - list = append(list, par) - if variadic { - if isVariadic { - p.error("... not on final argument") - } - isVariadic = true - } - } - p.expect(')') - - return types.NewTuple(list...), isVariadic -} - -// ResultList = Type | ParamList . -func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { - switch p.tok { - case '<': - return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg))) - - case '(': - params, _ := p.parseParamList(pkg) - return params - - default: - return nil - } -} - -// FunctionType = ParamList ResultList . -func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature { - params, isVariadic := p.parseParamList(pkg) - results := p.parseResultList(pkg) - return types.NewSignature(nil, params, results, isVariadic) -} - -// Func = Name FunctionType . -func (p *parser) parseFunc(pkg *types.Package) *types.Func { - name := p.parseName() - if strings.ContainsRune(name, '$') { - // This is a Type$equal or Type$hash function, which we don't want to parse, - // except for the types. - p.discardDirectiveWhileParsingTypes(pkg) - return nil - } - return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg)) -} - -// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . -func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { - p.expectKeyword("interface") - - var methods []*types.Func - var typs []*types.Named - - p.expect('{') - for p.tok != '}' && p.tok != scanner.EOF { - if p.tok == '?' { - p.next() - typs = append(typs, p.parseType(pkg).(*types.Named)) - } else { - method := p.parseFunc(pkg) - methods = append(methods, method) - } - p.expect(';') - } - p.expect('}') - - return types.NewInterface(methods, typs) -} - -// PointerType = "*" ("any" | Type) . -func (p *parser) parsePointerType(pkg *types.Package) types.Type { - p.expect('*') - if p.tok == scanner.Ident { - p.expectKeyword("any") - return types.Typ[types.UnsafePointer] - } - return types.NewPointer(p.parseType(pkg)) -} - -// TypeDefinition = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . -func (p *parser) parseTypeDefinition(pkg *types.Package, n int) types.Type { - var t types.Type - switch p.tok { - case scanner.String: - t = p.parseNamedType(n) - - case scanner.Ident: - switch p.lit { - case "map": - t = p.parseMapType(pkg) - - case "chan": - t = p.parseChanType(pkg) - - case "struct": - t = p.parseStructType(pkg) - - case "interface": - t = p.parseInterfaceType(pkg) - } - - case '*': - t = p.parsePointerType(pkg) - - case '[': - t = p.parseArrayOrSliceType(pkg) - - case '(': - t = p.parseFunctionType(pkg) - } - - p.typeMap[n] = t - return t -} - -const ( - // From gofrontend/go/export.h - // Note that these values are negative in the gofrontend and have been made positive - // in the gccgoimporter. - gccgoBuiltinINT8 = 1 - gccgoBuiltinINT16 = 2 - gccgoBuiltinINT32 = 3 - gccgoBuiltinINT64 = 4 - gccgoBuiltinUINT8 = 5 - gccgoBuiltinUINT16 = 6 - gccgoBuiltinUINT32 = 7 - gccgoBuiltinUINT64 = 8 - gccgoBuiltinFLOAT32 = 9 - gccgoBuiltinFLOAT64 = 10 - gccgoBuiltinINT = 11 - gccgoBuiltinUINT = 12 - gccgoBuiltinUINTPTR = 13 - gccgoBuiltinBOOL = 15 - gccgoBuiltinSTRING = 16 - gccgoBuiltinCOMPLEX64 = 17 - gccgoBuiltinCOMPLEX128 = 18 - gccgoBuiltinERROR = 19 - gccgoBuiltinBYTE = 20 - gccgoBuiltinRUNE = 21 -) - -func lookupBuiltinType(typ int) types.Type { - return [...]types.Type{ - gccgoBuiltinINT8: types.Typ[types.Int8], - gccgoBuiltinINT16: types.Typ[types.Int16], - gccgoBuiltinINT32: types.Typ[types.Int32], - gccgoBuiltinINT64: types.Typ[types.Int64], - gccgoBuiltinUINT8: types.Typ[types.Uint8], - gccgoBuiltinUINT16: types.Typ[types.Uint16], - gccgoBuiltinUINT32: types.Typ[types.Uint32], - gccgoBuiltinUINT64: types.Typ[types.Uint64], - gccgoBuiltinFLOAT32: types.Typ[types.Float32], - gccgoBuiltinFLOAT64: types.Typ[types.Float64], - gccgoBuiltinINT: types.Typ[types.Int], - gccgoBuiltinUINT: types.Typ[types.Uint], - gccgoBuiltinUINTPTR: types.Typ[types.Uintptr], - gccgoBuiltinBOOL: types.Typ[types.Bool], - gccgoBuiltinSTRING: types.Typ[types.String], - gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64], - gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128], - gccgoBuiltinERROR: types.Universe.Lookup("error").Type(), - gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(), - gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(), - }[typ] -} - -// Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" . -func (p *parser) parseType(pkg *types.Package) (t types.Type) { - p.expect('<') - p.expectKeyword("type") - - switch p.tok { - case scanner.Int: - n := p.parseInt() - - if p.tok == '>' { - t = p.typeMap[int(n)] - } else { - t = p.parseTypeDefinition(pkg, int(n)) - } - - case '-': - p.next() - n := p.parseInt() - t = lookupBuiltinType(int(n)) - - default: - p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit) - return nil - } - - p.expect('>') - return -} - -// PackageInit = unquotedString unquotedString int . -func (p *parser) parsePackageInit() PackageInit { - name := p.parseUnquotedString() - initfunc := p.parseUnquotedString() - priority := int(p.parseInt()) - return PackageInit{Name: name, InitFunc: initfunc, Priority: priority} -} - -// Throw away tokens until we see a ';'. If we see a '<', attempt to parse as a type. -func (p *parser) discardDirectiveWhileParsingTypes(pkg *types.Package) { - for { - switch p.tok { - case ';': - return - case '<': - p.parseType(p.pkg) - case scanner.EOF: - p.error("unexpected EOF") - default: - p.next() - } - } -} - -// Create the package if we have parsed both the package path and package name. -func (p *parser) maybeCreatePackage() { - if p.pkgname != "" && p.pkgpath != "" { - p.pkg = p.getPkg(p.pkgpath, p.pkgname) - } -} - -// InitDataDirective = "v1" ";" | -// "priority" int ";" | -// "init" { PackageInit } ";" | -// "checksum" unquotedString ";" . -func (p *parser) parseInitDataDirective() { - if p.tok != scanner.Ident { - // unexpected token kind; panic - p.expect(scanner.Ident) - } - - switch p.lit { - case "v1": - p.next() - p.expect(';') - - case "priority": - p.next() - p.initdata.Priority = int(p.parseInt()) - p.expect(';') - - case "init": - p.next() - for p.tok != ';' && p.tok != scanner.EOF { - p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit()) - } - p.expect(';') - - case "checksum": - // Don't let the scanner try to parse the checksum as a number. - defer func(mode uint) { - p.scanner.Mode = mode - }(p.scanner.Mode) - p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats - p.next() - p.parseUnquotedString() - p.expect(';') - - default: - p.errorf("unexpected identifier: %q", p.lit) - } -} - -// Directive = InitDataDirective | -// "package" unquotedString ";" | -// "pkgpath" unquotedString ";" | -// "import" unquotedString unquotedString string ";" | -// "func" Func ";" | -// "type" Type ";" | -// "var" Var ";" | -// "const" Const ";" . -func (p *parser) parseDirective() { - if p.tok != scanner.Ident { - // unexpected token kind; panic - p.expect(scanner.Ident) - } - - switch p.lit { - case "v1", "priority", "init", "checksum": - p.parseInitDataDirective() - - case "package": - p.next() - p.pkgname = p.parseUnquotedString() - p.maybeCreatePackage() - p.expect(';') - - case "pkgpath": - p.next() - p.pkgpath = p.parseUnquotedString() - p.maybeCreatePackage() - p.expect(';') - - case "import": - p.next() - pkgname := p.parseUnquotedString() - pkgpath := p.parseUnquotedString() - p.getPkg(pkgpath, pkgname) - p.parseString() - p.expect(';') - - case "func": - p.next() - fun := p.parseFunc(p.pkg) - if fun != nil { - p.pkg.Scope().Insert(fun) - } - p.expect(';') - - case "type": - p.next() - p.parseType(p.pkg) - p.expect(';') - - case "var": - p.next() - v := p.parseVar(p.pkg) - p.pkg.Scope().Insert(v) - p.expect(';') - - case "const": - p.next() - c := p.parseConst(p.pkg) - p.pkg.Scope().Insert(c) - p.expect(';') - - default: - p.errorf("unexpected identifier: %q", p.lit) - } -} - -// Package = { Directive } . -func (p *parser) parsePackage() *types.Package { - for p.tok != scanner.EOF { - p.parseDirective() - } - for _, typ := range p.typeMap { - if it, ok := typ.(*types.Interface); ok { - it.Complete() - } - } - p.pkg.MarkComplete() - return p.pkg -} - -// InitData = { InitDataDirective } . -func (p *parser) parseInitData() { - for p.tok != scanner.EOF { - p.parseInitDataDirective() - } -} diff --git a/go/gccgoimporter/parser_test.go b/go/gccgoimporter/parser_test.go deleted file mode 100644 index 1f0f12a2f8..0000000000 --- a/go/gccgoimporter/parser_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 gccgoimporter - -import ( - "bytes" - "strings" - "testing" - "text/scanner" - - "golang.org/x/tools/go/types" -) - -var typeParserTests = []struct { - id, typ, want, underlying, methods string -}{ - {id: "foo", typ: "", want: "int8"}, - {id: "foo", typ: ">", want: "*error"}, - {id: "foo", typ: "", want: "unsafe.Pointer"}, - {id: "foo", typ: ">>", want: "foo.Bar", underlying: "*foo.Bar"}, - {id: "foo", typ: " func (? ) M (); >", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"}, - {id: "foo", typ: ">", want: "bar.foo", underlying: "int8"}, - {id: "foo", typ: ">", want: "[]int8"}, - {id: "foo", typ: ">", want: "[42]int8"}, - {id: "foo", typ: "] >", want: "map[int8]int16"}, - {id: "foo", typ: ">", want: "chan int8"}, - {id: "foo", typ: ">", want: "<-chan int8"}, - {id: "foo", typ: ">", want: "chan<- int8"}, - {id: "foo", typ: "; I16 \"i16\"; }>", want: "struct{I8 int8; I16 int16 \"i16\"}"}, - {id: "foo", typ: ", b ) ; Bar (? , ? ...) (? , ? ); Baz (); }>", want: "interface{Bar(int16, ...int8) (int16, int8); Baz(); Foo(a int8, b int16) int8}"}, - {id: "foo", typ: ") >", want: "func(int8) int16"}, -} - -func TestTypeParser(t *testing.T) { - for _, test := range typeParserTests { - var p parser - p.init("test.gox", strings.NewReader(test.typ), make(map[string]*types.Package)) - p.pkgname = test.id - p.pkgpath = test.id - p.maybeCreatePackage() - typ := p.parseType(p.pkg) - - if p.tok != scanner.EOF { - t.Errorf("expected full parse, stopped at %q", p.lit) - } - - got := typ.String() - if got != test.want { - t.Errorf("got type %q, expected %q", got, test.want) - } - - if test.underlying != "" { - underlying := typ.Underlying().String() - if underlying != test.underlying { - t.Errorf("got underlying type %q, expected %q", underlying, test.underlying) - } - } - - if test.methods != "" { - nt := typ.(*types.Named) - var buf bytes.Buffer - for i := 0; i != nt.NumMethods(); i++ { - buf.WriteString(nt.Method(i).String()) - } - methods := buf.String() - if methods != test.methods { - t.Errorf("got methods %q, expected %q", methods, test.methods) - } - } - } -} diff --git a/go/gccgoimporter/testdata/complexnums.go b/go/gccgoimporter/testdata/complexnums.go deleted file mode 100644 index a51b6b01c0..0000000000 --- a/go/gccgoimporter/testdata/complexnums.go +++ /dev/null @@ -1,6 +0,0 @@ -package complexnums - -const NN = -1 - 1i -const NP = -1 + 1i -const PN = 1 - 1i -const PP = 1 + 1i diff --git a/go/gccgoimporter/testdata/complexnums.gox b/go/gccgoimporter/testdata/complexnums.gox deleted file mode 100644 index b66524f80e..0000000000 --- a/go/gccgoimporter/testdata/complexnums.gox +++ /dev/null @@ -1,8 +0,0 @@ -v1; -package complexnums; -pkgpath complexnums; -priority 1; -const NN = -0.1E1-0.1E1i ; -const NP = -0.1E1+0.1E1i ; -const PN = 0.1E1-0.1E1i ; -const PP = 0.1E1+0.1E1i ; diff --git a/go/gccgoimporter/testdata/imports.go b/go/gccgoimporter/testdata/imports.go deleted file mode 100644 index 7907316a60..0000000000 --- a/go/gccgoimporter/testdata/imports.go +++ /dev/null @@ -1,5 +0,0 @@ -package imports - -import "fmt" - -var Hello = fmt.Sprintf("Hello, world") diff --git a/go/gccgoimporter/testdata/imports.gox b/go/gccgoimporter/testdata/imports.gox deleted file mode 100644 index 958a4f5b82..0000000000 --- a/go/gccgoimporter/testdata/imports.gox +++ /dev/null @@ -1,7 +0,0 @@ -v1; -package imports; -pkgpath imports; -priority 7; -import fmt fmt "fmt"; -init imports imports..import 7 math math..import 1 runtime runtime..import 1 strconv strconv..import 2 io io..import 3 reflect reflect..import 3 syscall syscall..import 3 time time..import 4 os os..import 5 fmt fmt..import 6; -var Hello ; diff --git a/go/gccgoimporter/testdata/pointer.go b/go/gccgoimporter/testdata/pointer.go deleted file mode 100644 index 4ebc67137d..0000000000 --- a/go/gccgoimporter/testdata/pointer.go +++ /dev/null @@ -1,3 +0,0 @@ -package pointer - -type Int8Ptr *int8 diff --git a/go/gccgoimporter/testdata/pointer.gox b/go/gccgoimporter/testdata/pointer.gox deleted file mode 100644 index d96ebbdd14..0000000000 --- a/go/gccgoimporter/testdata/pointer.gox +++ /dev/null @@ -1,4 +0,0 @@ -v1; -package pointer; -pkgpath pointer; -type >>; diff --git a/go/gcimporter/exportdata.go b/go/gcimporter/exportdata.go deleted file mode 100644 index 657742bb6d..0000000000 --- a/go/gcimporter/exportdata.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2011 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. - -// This file implements FindExportData. - -package gcimporter - -import ( - "bufio" - "errors" - "fmt" - "io" - "strconv" - "strings" -) - -func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { - // See $GOROOT/include/ar.h. - hdr := make([]byte, 16+12+6+6+8+10+2) - _, err = io.ReadFull(r, hdr) - if err != nil { - return - } - // leave for debugging - if false { - fmt.Printf("header: %s", hdr) - } - s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) - size, err = strconv.Atoi(s) - if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { - err = errors.New("invalid archive header") - return - } - name = strings.TrimSpace(string(hdr[:16])) - return -} - -// FindExportData positions the reader r at the beginning of the -// export data section of an underlying GC-created object/archive -// file by reading from it. The reader must be positioned at the -// start of the file before calling this function. -// -func FindExportData(r *bufio.Reader) (err error) { - // Read first line to make sure this is an object file. - line, err := r.ReadSlice('\n') - if err != nil { - return - } - if string(line) == "!\n" { - // Archive file. Scan to __.PKGDEF. - var name string - var size int - if name, size, err = readGopackHeader(r); err != nil { - return - } - - // Optional leading __.GOSYMDEF or __.SYMDEF. - // Read and discard. - if name == "__.SYMDEF" || name == "__.GOSYMDEF" { - const block = 4096 - tmp := make([]byte, block) - for size > 0 { - n := size - if n > block { - n = block - } - if _, err = io.ReadFull(r, tmp[:n]); err != nil { - return - } - size -= n - } - - if name, size, err = readGopackHeader(r); err != nil { - return - } - } - - // First real entry should be __.PKGDEF. - if name != "__.PKGDEF" { - err = errors.New("go archive is missing __.PKGDEF") - return - } - - // Read first line of __.PKGDEF data, so that line - // is once again the first line of the input. - if line, err = r.ReadSlice('\n'); err != nil { - return - } - } - - // Now at __.PKGDEF in archive or still at beginning of file. - // Either way, line should begin with "go object ". - if !strings.HasPrefix(string(line), "go object ") { - err = errors.New("not a go object file") - return - } - - // Skip over object header to export data. - // Begins after first line with $$. - for line[0] != '$' { - if line, err = r.ReadSlice('\n'); err != nil { - return - } - } - - return -} diff --git a/go/gcimporter/gcimporter.go b/go/gcimporter/gcimporter.go deleted file mode 100644 index 031e8707cf..0000000000 --- a/go/gcimporter/gcimporter.go +++ /dev/null @@ -1,995 +0,0 @@ -// Copyright 2011 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 gcimporter implements Import for gc-generated object files. -// Importing this package installs Import as go/types.DefaultImport. -package gcimporter // import "golang.org/x/tools/go/gcimporter" - -import ( - "bufio" - "errors" - "fmt" - "go/build" - "go/token" - "io" - "os" - "path/filepath" - "sort" - "strconv" - "strings" - "text/scanner" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// debugging/development support -const debug = false - -func init() { - types.DefaultImport = Import -} - -var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"} - -// FindPkg returns the filename and unique package id for an import -// path based on package information provided by build.Import (using -// the build.Default build.Context). -// If no file was found, an empty filename is returned. -// -func FindPkg(path, srcDir string) (filename, id string) { - if len(path) == 0 { - return - } - - id = path - var noext string - switch { - default: - // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" - // Don't require the source files to be present. - bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) - if bp.PkgObj == "" { - return - } - noext = strings.TrimSuffix(bp.PkgObj, ".a") - - case build.IsLocalImport(path): - // "./x" -> "/this/directory/x.ext", "/this/directory/x" - noext = filepath.Join(srcDir, path) - id = noext - - case filepath.IsAbs(path): - // for completeness only - go/build.Import - // does not support absolute imports - // "/x" -> "/x.ext", "/x" - noext = path - } - - // try extensions - for _, ext := range pkgExts { - filename = noext + ext - if f, err := os.Stat(filename); err == nil && !f.IsDir() { - return - } - } - - filename = "" // not found - return -} - -// ImportData imports a package by reading the gc-generated export data, -// adds the corresponding package object to the packages map indexed by id, -// and returns the object. -// -// The packages map must contains all packages already imported. The data -// reader position must be the beginning of the export data section. The -// filename is only used in error messages. -// -// If packages[id] contains the completely imported package, that package -// can be used directly, and there is no need to call this function (but -// there is also no harm but for extra time used). -// -func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) { - // support for parser error handling - defer func() { - switch r := recover().(type) { - case nil: - // nothing to do - case importError: - err = r - default: - panic(r) // internal error - } - }() - - var p parser - p.init(filename, id, data, packages) - pkg = p.parseExport() - - return -} - -// Import imports a gc-generated package given its import path, adds the -// corresponding package object to the packages map, and returns the object. -// Local import paths are interpreted relative to the current working directory. -// The packages map must contains all packages already imported. -// -func Import(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { - if path == "unsafe" { - return types.Unsafe, nil - } - - srcDir := "." - if build.IsLocalImport(path) { - srcDir, err = os.Getwd() - if err != nil { - return - } - } - - filename, id := FindPkg(path, srcDir) - if filename == "" { - err = fmt.Errorf("can't find import: %s", id) - return - } - - // no need to re-import if the package was imported completely before - if pkg = packages[id]; pkg != nil && pkg.Complete() { - return - } - - // open file - f, err := os.Open(filename) - if err != nil { - return - } - defer func() { - f.Close() - if err != nil { - // add file name to error - err = fmt.Errorf("reading export data: %s: %v", filename, err) - } - }() - - buf := bufio.NewReader(f) - if err = FindExportData(buf); err != nil { - return - } - - pkg, err = ImportData(packages, filename, id, buf) - - return -} - -// ---------------------------------------------------------------------------- -// Parser - -// TODO(gri) Imported objects don't have position information. -// Ideally use the debug table line info; alternatively -// create some fake position (or the position of the -// import). That way error messages referring to imported -// objects can print meaningful information. - -// parser parses the exports inside a gc compiler-produced -// object/archive file and populates its scope with the results. -type parser struct { - scanner scanner.Scanner - tok rune // current token - lit string // literal string; only valid for Ident, Int, String tokens - id string // package id of imported package - sharedPkgs map[string]*types.Package // package id -> package object (across importer) - localPkgs map[string]*types.Package // package id -> package object (just this package) -} - -func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) { - p.scanner.Init(src) - p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } - p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments - p.scanner.Whitespace = 1<<'\t' | 1<<' ' - p.scanner.Filename = filename // for good error messages - p.next() - p.id = id - p.sharedPkgs = packages - if debug { - // check consistency of packages map - for _, pkg := range packages { - if pkg.Name() == "" { - fmt.Printf("no package name for %s\n", pkg.Path()) - } - } - } -} - -func (p *parser) next() { - p.tok = p.scanner.Scan() - switch p.tok { - case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': - p.lit = p.scanner.TokenText() - default: - p.lit = "" - } - if debug { - fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) - } -} - -func declTypeName(pkg *types.Package, name string) *types.TypeName { - scope := pkg.Scope() - if obj := scope.Lookup(name); obj != nil { - return obj.(*types.TypeName) - } - obj := types.NewTypeName(token.NoPos, pkg, name, nil) - // a named type may be referred to before the underlying type - // is known - set it up - types.NewNamed(obj, nil, nil) - scope.Insert(obj) - return obj -} - -// ---------------------------------------------------------------------------- -// Error handling - -// Internal errors are boxed as importErrors. -type importError struct { - pos scanner.Position - err error -} - -func (e importError) Error() string { - return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) -} - -func (p *parser) error(err interface{}) { - if s, ok := err.(string); ok { - err = errors.New(s) - } - // panic with a runtime.Error if err is not an error - panic(importError{p.scanner.Pos(), err.(error)}) -} - -func (p *parser) errorf(format string, args ...interface{}) { - p.error(fmt.Sprintf(format, args...)) -} - -func (p *parser) expect(tok rune) string { - lit := p.lit - if p.tok != tok { - p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) - } - p.next() - return lit -} - -func (p *parser) expectSpecial(tok string) { - sep := 'x' // not white space - i := 0 - for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { - sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token - p.next() - i++ - } - if i < len(tok) { - p.errorf("expected %q, got %q", tok, tok[0:i]) - } -} - -func (p *parser) expectKeyword(keyword string) { - lit := p.expect(scanner.Ident) - if lit != keyword { - p.errorf("expected keyword %s, got %q", keyword, lit) - } -} - -// ---------------------------------------------------------------------------- -// Qualified and unqualified names - -// PackageId = string_lit . -// -func (p *parser) parsePackageId() string { - id, err := strconv.Unquote(p.expect(scanner.String)) - if err != nil { - p.error(err) - } - // id == "" stands for the imported package id - // (only known at time of package installation) - if id == "" { - id = p.id - } - return id -} - -// PackageName = ident . -// -func (p *parser) parsePackageName() string { - return p.expect(scanner.Ident) -} - -// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . -func (p *parser) parseDotIdent() string { - ident := "" - if p.tok != scanner.Int { - sep := 'x' // not white space - for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { - ident += p.lit - sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token - p.next() - } - } - if ident == "" { - p.expect(scanner.Ident) // use expect() for error handling - } - return ident -} - -// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . -// -func (p *parser) parseQualifiedName() (id, name string) { - p.expect('@') - id = p.parsePackageId() - p.expect('.') - // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. - if p.tok == '?' { - p.next() - } else { - name = p.parseDotIdent() - } - return -} - -// getPkg returns the package for a given id. If the package is -// not found but we have a package name, create the package and -// add it to the p.localPkgs and p.sharedPkgs maps. -// -// id identifies a package, usually by a canonical package path like -// "encoding/json" but possibly by a non-canonical import path like -// "./json". -// -func (p *parser) getPkg(id, name string) *types.Package { - // package unsafe is not in the packages maps - handle explicitly - if id == "unsafe" { - return types.Unsafe - } - - pkg := p.localPkgs[id] - if pkg == nil && name != "" { - // first import of id from this package - pkg = p.sharedPkgs[id] - if pkg == nil { - // first import of id by this importer - pkg = types.NewPackage(id, name) - p.sharedPkgs[id] = pkg - } - - if p.localPkgs == nil { - p.localPkgs = make(map[string]*types.Package) - } - p.localPkgs[id] = pkg - } - return pkg -} - -// parseExportedName is like parseQualifiedName, but -// the package id is resolved to an imported *types.Package. -// -func (p *parser) parseExportedName() (pkg *types.Package, name string) { - id, name := p.parseQualifiedName() - pkg = p.getPkg(id, "") - if pkg == nil { - p.errorf("%s package not found", id) - } - return -} - -// ---------------------------------------------------------------------------- -// Types - -// BasicType = identifier . -// -func (p *parser) parseBasicType() types.Type { - id := p.expect(scanner.Ident) - obj := types.Universe.Lookup(id) - if obj, ok := obj.(*types.TypeName); ok { - return obj.Type() - } - p.errorf("not a basic type: %s", id) - return nil -} - -// ArrayType = "[" int_lit "]" Type . -// -func (p *parser) parseArrayType() types.Type { - // "[" already consumed and lookahead known not to be "]" - lit := p.expect(scanner.Int) - p.expect(']') - elem := p.parseType() - n, err := strconv.ParseInt(lit, 10, 64) - if err != nil { - p.error(err) - } - return types.NewArray(elem, n) -} - -// MapType = "map" "[" Type "]" Type . -// -func (p *parser) parseMapType() types.Type { - p.expectKeyword("map") - p.expect('[') - key := p.parseType() - p.expect(']') - elem := p.parseType() - return types.NewMap(key, elem) -} - -// Name = identifier | "?" | QualifiedName . -// -// If materializePkg is set, the returned package is guaranteed to be set. -// For fully qualified names, the returned package may be a fake package -// (without name, scope, and not in the p.imports map), created for the -// sole purpose of providing a package path. Fake packages are created -// when the package id is not found in the p.imports map; in that case -// we cannot create a real package because we don't have a package name. -// For non-qualified names, the returned package is the imported package. -// -func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) { - switch p.tok { - case scanner.Ident: - pkg = p.sharedPkgs[p.id] - name = p.lit - p.next() - case '?': - // anonymous - pkg = p.sharedPkgs[p.id] - p.next() - case '@': - // exported name prefixed with package path - var id string - id, name = p.parseQualifiedName() - if materializePkg { - // we don't have a package name - if the package - // doesn't exist yet, create a fake package instead - pkg = p.getPkg(id, "") - if pkg == nil { - pkg = types.NewPackage(id, "") - } - } - default: - p.error("name expected") - } - return -} - -func deref(typ types.Type) types.Type { - if p, _ := typ.(*types.Pointer); p != nil { - return p.Elem() - } - return typ -} - -// Field = Name Type [ string_lit ] . -// -func (p *parser) parseField() (*types.Var, string) { - pkg, name := p.parseName(true) - typ := p.parseType() - anonymous := false - if name == "" { - // anonymous field - typ must be T or *T and T must be a type name - switch typ := deref(typ).(type) { - case *types.Basic: // basic types are named types - pkg = nil - name = typ.Name() - case *types.Named: - name = typ.Obj().Name() - default: - p.errorf("anonymous field expected") - } - anonymous = true - } - tag := "" - if p.tok == scanner.String { - s := p.expect(scanner.String) - var err error - tag, err = strconv.Unquote(s) - if err != nil { - p.errorf("invalid struct tag %s: %s", s, err) - } - } - return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag -} - -// StructType = "struct" "{" [ FieldList ] "}" . -// FieldList = Field { ";" Field } . -// -func (p *parser) parseStructType() types.Type { - var fields []*types.Var - var tags []string - - p.expectKeyword("struct") - p.expect('{') - for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { - if i > 0 { - p.expect(';') - } - fld, tag := p.parseField() - if tag != "" && tags == nil { - tags = make([]string, i) - } - if tags != nil { - tags = append(tags, tag) - } - fields = append(fields, fld) - } - p.expect('}') - - return types.NewStruct(fields, tags) -} - -// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . -// -func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { - _, name := p.parseName(false) - // remove gc-specific parameter numbering - if i := strings.Index(name, "·"); i >= 0 { - name = name[:i] - } - if p.tok == '.' { - p.expectSpecial("...") - isVariadic = true - } - typ := p.parseType() - if isVariadic { - typ = types.NewSlice(typ) - } - // ignore argument tag (e.g. "noescape") - if p.tok == scanner.String { - p.next() - } - // TODO(gri) should we provide a package? - par = types.NewVar(token.NoPos, nil, name, typ) - return -} - -// Parameters = "(" [ ParameterList ] ")" . -// ParameterList = { Parameter "," } Parameter . -// -func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { - p.expect('(') - for p.tok != ')' && p.tok != scanner.EOF { - if len(list) > 0 { - p.expect(',') - } - par, variadic := p.parseParameter() - list = append(list, par) - if variadic { - if isVariadic { - p.error("... not on final argument") - } - isVariadic = true - } - } - p.expect(')') - - return -} - -// Signature = Parameters [ Result ] . -// Result = Type | Parameters . -// -func (p *parser) parseSignature(recv *types.Var) *types.Signature { - params, isVariadic := p.parseParameters() - - // optional result type - var results []*types.Var - if p.tok == '(' { - var variadic bool - results, variadic = p.parseParameters() - if variadic { - p.error("... not permitted on result type") - } - } - - return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) -} - -// InterfaceType = "interface" "{" [ MethodList ] "}" . -// MethodList = Method { ";" Method } . -// Method = Name Signature . -// -// The methods of embedded interfaces are always "inlined" -// by the compiler and thus embedded interfaces are never -// visible in the export data. -// -func (p *parser) parseInterfaceType() types.Type { - var methods []*types.Func - - p.expectKeyword("interface") - p.expect('{') - for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { - if i > 0 { - p.expect(';') - } - pkg, name := p.parseName(true) - sig := p.parseSignature(nil) - methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) - } - p.expect('}') - - // Complete requires the type's embedded interfaces to be fully defined, - // but we do not define any - return types.NewInterface(methods, nil).Complete() -} - -// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . -// -func (p *parser) parseChanType() types.Type { - dir := types.SendRecv - if p.tok == scanner.Ident { - p.expectKeyword("chan") - if p.tok == '<' { - p.expectSpecial("<-") - dir = types.SendOnly - } - } else { - p.expectSpecial("<-") - p.expectKeyword("chan") - dir = types.RecvOnly - } - elem := p.parseType() - return types.NewChan(dir, elem) -} - -// Type = -// BasicType | TypeName | ArrayType | SliceType | StructType | -// PointerType | FuncType | InterfaceType | MapType | ChanType | -// "(" Type ")" . -// -// BasicType = ident . -// TypeName = ExportedName . -// SliceType = "[" "]" Type . -// PointerType = "*" Type . -// FuncType = "func" Signature . -// -func (p *parser) parseType() types.Type { - switch p.tok { - case scanner.Ident: - switch p.lit { - default: - return p.parseBasicType() - case "struct": - return p.parseStructType() - case "func": - // FuncType - p.next() - return p.parseSignature(nil) - case "interface": - return p.parseInterfaceType() - case "map": - return p.parseMapType() - case "chan": - return p.parseChanType() - } - case '@': - // TypeName - pkg, name := p.parseExportedName() - return declTypeName(pkg, name).Type() - case '[': - p.next() // look ahead - if p.tok == ']' { - // SliceType - p.next() - return types.NewSlice(p.parseType()) - } - return p.parseArrayType() - case '*': - // PointerType - p.next() - return types.NewPointer(p.parseType()) - case '<': - return p.parseChanType() - case '(': - // "(" Type ")" - p.next() - typ := p.parseType() - p.expect(')') - return typ - } - p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) - return nil -} - -// ---------------------------------------------------------------------------- -// Declarations - -// ImportDecl = "import" PackageName PackageId . -// -func (p *parser) parseImportDecl() { - p.expectKeyword("import") - name := p.parsePackageName() - p.getPkg(p.parsePackageId(), name) -} - -// int_lit = [ "+" | "-" ] { "0" ... "9" } . -// -func (p *parser) parseInt() string { - s := "" - switch p.tok { - case '-': - s = "-" - p.next() - case '+': - p.next() - } - return s + p.expect(scanner.Int) -} - -// number = int_lit [ "p" int_lit ] . -// -func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { - // mantissa - mant := exact.MakeFromLiteral(p.parseInt(), token.INT) - if mant == nil { - panic("invalid mantissa") - } - - if p.lit == "p" { - // exponent (base 2) - p.next() - exp, err := strconv.ParseInt(p.parseInt(), 10, 0) - if err != nil { - p.error(err) - } - if exp < 0 { - denom := exact.MakeInt64(1) - denom = exact.Shift(denom, token.SHL, uint(-exp)) - typ = types.Typ[types.UntypedFloat] - val = exact.BinaryOp(mant, token.QUO, denom) - return - } - if exp > 0 { - mant = exact.Shift(mant, token.SHL, uint(exp)) - } - typ = types.Typ[types.UntypedFloat] - val = mant - return - } - - typ = types.Typ[types.UntypedInt] - val = mant - return -} - -// ConstDecl = "const" ExportedName [ Type ] "=" Literal . -// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . -// bool_lit = "true" | "false" . -// complex_lit = "(" float_lit "+" float_lit "i" ")" . -// rune_lit = "(" int_lit "+" int_lit ")" . -// string_lit = `"` { unicode_char } `"` . -// -func (p *parser) parseConstDecl() { - p.expectKeyword("const") - pkg, name := p.parseExportedName() - - var typ0 types.Type - if p.tok != '=' { - typ0 = p.parseType() - } - - p.expect('=') - var typ types.Type - var val exact.Value - switch p.tok { - case scanner.Ident: - // bool_lit - if p.lit != "true" && p.lit != "false" { - p.error("expected true or false") - } - typ = types.Typ[types.UntypedBool] - val = exact.MakeBool(p.lit == "true") - p.next() - - case '-', scanner.Int: - // int_lit - typ, val = p.parseNumber() - - case '(': - // complex_lit or rune_lit - p.next() - if p.tok == scanner.Char { - p.next() - p.expect('+') - typ = types.Typ[types.UntypedRune] - _, val = p.parseNumber() - p.expect(')') - break - } - _, re := p.parseNumber() - p.expect('+') - _, im := p.parseNumber() - p.expectKeyword("i") - p.expect(')') - typ = types.Typ[types.UntypedComplex] - val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) - - case scanner.Char: - // rune_lit - typ = types.Typ[types.UntypedRune] - val = exact.MakeFromLiteral(p.lit, token.CHAR) - p.next() - - case scanner.String: - // string_lit - typ = types.Typ[types.UntypedString] - val = exact.MakeFromLiteral(p.lit, token.STRING) - p.next() - - default: - p.errorf("expected literal got %s", scanner.TokenString(p.tok)) - } - - if typ0 == nil { - typ0 = typ - } - - pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) -} - -// TypeDecl = "type" ExportedName Type . -// -func (p *parser) parseTypeDecl() { - p.expectKeyword("type") - pkg, name := p.parseExportedName() - obj := declTypeName(pkg, name) - - // The type object may have been imported before and thus already - // have a type associated with it. We still need to parse the type - // structure, but throw it away if the object already has a type. - // This ensures that all imports refer to the same type object for - // a given type declaration. - typ := p.parseType() - - if name := obj.Type().(*types.Named); name.Underlying() == nil { - name.SetUnderlying(typ) - } -} - -// VarDecl = "var" ExportedName Type . -// -func (p *parser) parseVarDecl() { - p.expectKeyword("var") - pkg, name := p.parseExportedName() - typ := p.parseType() - pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) -} - -// Func = Signature [ Body ] . -// Body = "{" ... "}" . -// -func (p *parser) parseFunc(recv *types.Var) *types.Signature { - sig := p.parseSignature(recv) - if p.tok == '{' { - p.next() - for i := 1; i > 0; p.next() { - switch p.tok { - case '{': - i++ - case '}': - i-- - } - } - } - return sig -} - -// MethodDecl = "func" Receiver Name Func . -// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . -// -func (p *parser) parseMethodDecl() { - // "func" already consumed - p.expect('(') - recv, _ := p.parseParameter() // receiver - p.expect(')') - - // determine receiver base type object - base := deref(recv.Type()).(*types.Named) - - // parse method name, signature, and possibly inlined body - _, name := p.parseName(true) - sig := p.parseFunc(recv) - - // methods always belong to the same package as the base type object - pkg := base.Obj().Pkg() - - // add method to type unless type was imported before - // and method exists already - // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small. - base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) -} - -// FuncDecl = "func" ExportedName Func . -// -func (p *parser) parseFuncDecl() { - // "func" already consumed - pkg, name := p.parseExportedName() - typ := p.parseFunc(nil) - pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) -} - -// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . -// -func (p *parser) parseDecl() { - if p.tok == scanner.Ident { - switch p.lit { - case "import": - p.parseImportDecl() - case "const": - p.parseConstDecl() - case "type": - p.parseTypeDecl() - case "var": - p.parseVarDecl() - case "func": - p.next() // look ahead - if p.tok == '(' { - p.parseMethodDecl() - } else { - p.parseFuncDecl() - } - } - } - p.expect('\n') -} - -// ---------------------------------------------------------------------------- -// Export - -// Export = "PackageClause { Decl } "$$" . -// PackageClause = "package" PackageName [ "safe" ] "\n" . -// -func (p *parser) parseExport() *types.Package { - p.expectKeyword("package") - name := p.parsePackageName() - if p.tok == scanner.Ident && p.lit == "safe" { - // package was compiled with -u option - ignore - p.next() - } - p.expect('\n') - - pkg := p.getPkg(p.id, name) - - for p.tok != '$' && p.tok != scanner.EOF { - p.parseDecl() - } - - if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { - // don't call next()/expect() since reading past the - // export data may cause scanner errors (e.g. NUL chars) - p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) - } - - if n := p.scanner.ErrorCount; n != 0 { - p.errorf("expected no scanner errors, got %d", n) - } - - // Record all referenced packages as imports. - var imports []*types.Package - for id, pkg2 := range p.localPkgs { - if id == p.id { - continue // avoid self-edge - } - imports = append(imports, pkg2) - } - sort.Sort(byPath(imports)) - pkg.SetImports(imports) - - // package was imported completely and without errors - pkg.MarkComplete() - - return pkg -} - -type byPath []*types.Package - -func (a byPath) Len() int { return len(a) } -func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() } diff --git a/go/gcimporter/gcimporter_test.go b/go/gcimporter/gcimporter_test.go deleted file mode 100644 index 73a4747fc7..0000000000 --- a/go/gcimporter/gcimporter_test.go +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2011 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 gcimporter - -import ( - "fmt" - "go/build" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - "golang.org/x/tools/go/types" -) - -// skipSpecialPlatforms causes the test to be skipped for platforms where -// builders (build.golang.org) don't have access to compiled packages for -// import. -func skipSpecialPlatforms(t *testing.T) { - switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform { - case "nacl-amd64p32", - "nacl-386", - "nacl-arm", - "darwin-arm", - "darwin-arm64": - t.Skipf("no compiled packages available for import on %s", platform) - } -} - -var gcPath string // Go compiler path - -func init() { - if char, err := build.ArchChar(runtime.GOARCH); err == nil { - gcPath = filepath.Join(build.ToolDir, char+"g") - return - } - gcPath = "unknown-GOARCH-compiler" -} - -func compile(t *testing.T, dirname, filename string) string { - cmd := exec.Command(gcPath, filename) - cmd.Dir = dirname - out, err := cmd.CombinedOutput() - if err != nil { - t.Logf("%s", out) - t.Fatalf("%s %s failed: %s", gcPath, filename, err) - } - archCh, _ := build.ArchChar(runtime.GOARCH) - // filename should end with ".go" - return filepath.Join(dirname, filename[:len(filename)-2]+archCh) -} - -// Use the same global imports map for all tests. The effect is -// as if all tested packages were imported into a single package. -var imports = make(map[string]*types.Package) - -func testPath(t *testing.T, path string) *types.Package { - t0 := time.Now() - pkg, err := Import(imports, path) - if err != nil { - t.Errorf("testPath(%s): %s", path, err) - return nil - } - t.Logf("testPath(%s): %v", path, time.Since(t0)) - return pkg -} - -const maxTime = 30 * time.Second - -func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { - dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) - list, err := ioutil.ReadDir(dirname) - if err != nil { - t.Fatalf("testDir(%s): %s", dirname, err) - } - for _, f := range list { - if time.Now().After(endTime) { - t.Log("testing time used up") - return - } - switch { - case !f.IsDir(): - // try extensions - for _, ext := range pkgExts { - if strings.HasSuffix(f.Name(), ext) { - name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension - if testPath(t, filepath.Join(dir, name)) != nil { - nimports++ - } - } - } - case f.IsDir(): - nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) - } - } - return -} - -func TestImport(t *testing.T) { - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - return - } - - // On cross-compile builds, the path will not exist. - // Need to use GOHOSTOS, which is not available. - if _, err := os.Stat(gcPath); err != nil { - t.Skipf("skipping test: %v", err) - } - - if outFn := compile(t, "testdata", "exports.go"); outFn != "" { - defer os.Remove(outFn) - } - - nimports := 0 - if pkg := testPath(t, "./testdata/exports"); pkg != nil { - nimports++ - // The package's Imports should include all the types - // referenced by the exportdata, which may be more than - // the import statements in the package's source, but - // fewer than the transitive closure of dependencies. - want := `[package ast ("go/ast") package token ("go/token") package runtime ("runtime")]` - got := fmt.Sprint(pkg.Imports()) - if got != want { - t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want) - } - } - nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages - t.Logf("tested %d imports", nimports) -} - -var importedObjectTests = []struct { - name string - want string -}{ - {"unsafe.Pointer", "type Pointer unsafe.Pointer"}, - {"math.Pi", "const Pi untyped float"}, - {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, - {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"}, - {"math.Sin", "func Sin(x float64) float64"}, - // TODO(gri) add more tests -} - -func TestImportedTypes(t *testing.T) { - skipSpecialPlatforms(t) - - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - return - } - - for _, test := range importedObjectTests { - s := strings.Split(test.name, ".") - if len(s) != 2 { - t.Fatal("inconsistent test data") - } - importPath := s[0] - objName := s[1] - - pkg, err := Import(imports, importPath) - if err != nil { - t.Error(err) - continue - } - - obj := pkg.Scope().Lookup(objName) - if obj == nil { - t.Errorf("%s: object not found", test.name) - continue - } - - got := types.ObjectString(obj, types.RelativeTo(pkg)) - if got != test.want { - t.Errorf("%s: got %q; want %q", test.name, got, test.want) - } - } -} - -func TestIssue5815(t *testing.T) { - skipSpecialPlatforms(t) - - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - return - } - - pkg, err := Import(make(map[string]*types.Package), "strings") - if err != nil { - t.Fatal(err) - } - - scope := pkg.Scope() - for _, name := range scope.Names() { - obj := scope.Lookup(name) - if obj.Pkg() == nil { - t.Errorf("no pkg for %s", obj) - } - if tname, _ := obj.(*types.TypeName); tname != nil { - named := tname.Type().(*types.Named) - for i := 0; i < named.NumMethods(); i++ { - m := named.Method(i) - if m.Pkg() == nil { - t.Errorf("no pkg for %s", m) - } - } - } - } -} - -// Smoke test to ensure that imported methods get the correct package. -func TestCorrectMethodPackage(t *testing.T) { - skipSpecialPlatforms(t) - - // This package only handles gc export data. - if runtime.Compiler != "gc" { - t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) - return - } - - imports := make(map[string]*types.Package) - _, err := Import(imports, "net/http") - if err != nil { - t.Fatal(err) - } - - mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() - mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex - sel := mset.Lookup(nil, "Lock") - lock := sel.Obj().(*types.Func) - if got, want := lock.Pkg().Path(), "sync"; got != want { - t.Errorf("got package path %q; want %q", got, want) - } -} diff --git a/go/gcimporter/testdata/exports.go b/go/gcimporter/testdata/exports.go deleted file mode 100644 index 8ee28b0942..0000000000 --- a/go/gcimporter/testdata/exports.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2011 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. - -// This file is used to generate an object file which -// serves as test file for gcimporter_test.go. - -package exports - -import ( - "go/ast" -) - -// Issue 3682: Correctly read dotted identifiers from export data. -const init1 = 0 - -func init() {} - -const ( - C0 int = 0 - C1 = 3.14159265 - C2 = 2.718281828i - C3 = -123.456e-789 - C4 = +123.456E+789 - C5 = 1234i - C6 = "foo\n" - C7 = `bar\n` -) - -type ( - T1 int - T2 [10]int - T3 []int - T4 *int - T5 chan int - T6a chan<- int - T6b chan (<-chan int) - T6c chan<- (chan int) - T7 <-chan *ast.File - T8 struct{} - T9 struct { - a int - b, c float32 - d []string `go:"tag"` - } - T10 struct { - T8 - T9 - _ *T10 - } - T11 map[int]string - T12 interface{} - T13 interface { - m1() - m2(int) float32 - } - T14 interface { - T12 - T13 - m3(x ...struct{}) []T9 - } - T15 func() - T16 func(int) - T17 func(x int) - T18 func() float32 - T19 func() (x float32) - T20 func(...interface{}) - T21 struct{ next *T21 } - T22 struct{ link *T23 } - T23 struct{ link *T22 } - T24 *T24 - T25 *T26 - T26 *T27 - T27 *T25 - T28 func(T28) T28 -) - -var ( - V0 int - V1 = -991.0 -) - -func F1() {} -func F2(x int) {} -func F3() int { return 0 } -func F4() float32 { return 0 } -func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10) - -func (p *T1) M1() diff --git a/go/importer/export.go b/go/importer/export.go deleted file mode 100644 index 5930eafc05..0000000000 --- a/go/importer/export.go +++ /dev/null @@ -1,462 +0,0 @@ -// 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 importer - -import ( - "bytes" - "encoding/binary" - "fmt" - "go/ast" - "strings" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// debugging support -const ( - debug = false // emit debugging data - trace = false // print emitted data -) - -// format returns a byte indicating the low-level encoding/decoding format -// (debug vs product). -func format() byte { - if debug { - return 'd' - } - return 'p' -} - -// ExportData serializes the interface (exported package objects) -// of package pkg and returns the corresponding data. The export -// format is described elsewhere (TODO). -func ExportData(pkg *types.Package) []byte { - p := exporter{ - data: append([]byte(magic), format()), - pkgIndex: make(map[*types.Package]int), - typIndex: make(map[types.Type]int), - } - - // populate typIndex with predeclared types - for _, t := range predeclared { - p.typIndex[t] = len(p.typIndex) - } - - if trace { - p.tracef("export %s\n", pkg.Name()) - defer p.tracef("\n") - } - - p.string(version) - - p.pkg(pkg) - - // collect exported objects from package scope - var list []types.Object - scope := pkg.Scope() - for _, name := range scope.Names() { - if exported(name) { - list = append(list, scope.Lookup(name)) - } - } - - // write objects - p.int(len(list)) - for _, obj := range list { - p.obj(obj) - } - - return p.data -} - -type exporter struct { - data []byte - pkgIndex map[*types.Package]int - typIndex map[types.Type]int - - // tracing support - indent string -} - -func (p *exporter) pkg(pkg *types.Package) { - if trace { - p.tracef("package { ") - defer p.tracef("} ") - } - - if pkg == nil { - panic("unexpected nil pkg") - } - - // if the package was seen before, write its index (>= 0) - if i, ok := p.pkgIndex[pkg]; ok { - p.int(i) - return - } - p.pkgIndex[pkg] = len(p.pkgIndex) - - // otherwise, write the package tag (< 0) and package data - p.int(packageTag) - p.string(pkg.Name()) - p.string(pkg.Path()) -} - -func (p *exporter) obj(obj types.Object) { - if trace { - p.tracef("object %s {\n", obj.Name()) - defer p.tracef("}\n") - } - - switch obj := obj.(type) { - case *types.Const: - p.int(constTag) - p.string(obj.Name()) - p.typ(obj.Type()) - p.value(obj.Val()) - case *types.TypeName: - p.int(typeTag) - // name is written by corresponding named type - p.typ(obj.Type().(*types.Named)) - case *types.Var: - p.int(varTag) - p.string(obj.Name()) - p.typ(obj.Type()) - case *types.Func: - p.int(funcTag) - p.string(obj.Name()) - p.typ(obj.Type()) - default: - panic(fmt.Sprintf("unexpected object type %T", obj)) - } -} - -func (p *exporter) value(x exact.Value) { - if trace { - p.tracef("value { ") - defer p.tracef("} ") - } - - switch kind := x.Kind(); kind { - case exact.Bool: - tag := falseTag - if exact.BoolVal(x) { - tag = trueTag - } - p.int(tag) - case exact.Int: - if i, ok := exact.Int64Val(x); ok { - p.int(int64Tag) - p.int64(i) - return - } - p.int(floatTag) - p.float(x) - case exact.Float: - p.int(fractionTag) - p.fraction(x) - case exact.Complex: - p.int(complexTag) - p.fraction(exact.Real(x)) - p.fraction(exact.Imag(x)) - case exact.String: - p.int(stringTag) - p.string(exact.StringVal(x)) - default: - panic(fmt.Sprintf("unexpected value kind %d", kind)) - } -} - -func (p *exporter) float(x exact.Value) { - sign := exact.Sign(x) - p.int(sign) - if sign == 0 { - return - } - - p.ufloat(x) -} - -func (p *exporter) fraction(x exact.Value) { - sign := exact.Sign(x) - p.int(sign) - if sign == 0 { - return - } - - p.ufloat(exact.Num(x)) - p.ufloat(exact.Denom(x)) -} - -// ufloat writes abs(x) in form of a binary exponent -// followed by its mantissa bytes; x must be != 0. -func (p *exporter) ufloat(x exact.Value) { - mant := exact.Bytes(x) - exp8 := -1 - for i, b := range mant { - if b != 0 { - exp8 = i - break - } - } - if exp8 < 0 { - panic(fmt.Sprintf("%s has no mantissa", x)) - } - p.int(exp8 * 8) - p.bytes(mant[exp8:]) -} - -func (p *exporter) typ(typ types.Type) { - if trace { - p.tracef("type {\n") - defer p.tracef("}\n") - } - - // if the type was seen before, write its index (>= 0) - if i, ok := p.typIndex[typ]; ok { - p.int(i) - return - } - p.typIndex[typ] = len(p.typIndex) - - // otherwise, write the type tag (< 0) and type data - switch t := typ.(type) { - case *types.Array: - p.int(arrayTag) - p.int64(t.Len()) - p.typ(t.Elem()) - - case *types.Slice: - p.int(sliceTag) - p.typ(t.Elem()) - - case *types.Struct: - p.int(structTag) - n := t.NumFields() - p.int(n) - for i := 0; i < n; i++ { - p.field(t.Field(i)) - p.string(t.Tag(i)) - } - - case *types.Pointer: - p.int(pointerTag) - p.typ(t.Elem()) - - case *types.Signature: - p.int(signatureTag) - p.signature(t) - - case *types.Interface: - p.int(interfaceTag) - - // write embedded interfaces - m := t.NumEmbeddeds() - p.int(m) - for i := 0; i < m; i++ { - p.typ(t.Embedded(i)) - } - - // write methods - n := t.NumExplicitMethods() - p.int(n) - for i := 0; i < n; i++ { - m := t.ExplicitMethod(i) - p.qualifiedName(m.Pkg(), m.Name()) - p.typ(m.Type()) - } - - case *types.Map: - p.int(mapTag) - p.typ(t.Key()) - p.typ(t.Elem()) - - case *types.Chan: - p.int(chanTag) - p.int(int(t.Dir())) - p.typ(t.Elem()) - - case *types.Named: - p.int(namedTag) - - // write type object - obj := t.Obj() - p.string(obj.Name()) - p.pkg(obj.Pkg()) - - // write underlying type - p.typ(t.Underlying()) - - // write associated methods - n := t.NumMethods() - p.int(n) - for i := 0; i < n; i++ { - m := t.Method(i) - p.string(m.Name()) - p.typ(m.Type()) - } - - default: - panic("unreachable") - } -} - -func (p *exporter) field(f *types.Var) { - // anonymous fields have "" name - name := "" - if !f.Anonymous() { - name = f.Name() - } - - // qualifiedName will always emit the field package for - // anonymous fields because "" is not an exported name. - p.qualifiedName(f.Pkg(), name) - p.typ(f.Type()) -} - -func (p *exporter) qualifiedName(pkg *types.Package, name string) { - p.string(name) - // exported names don't need package - if !exported(name) { - if pkg == nil { - panic(fmt.Sprintf("nil package for unexported qualified name %s", name)) - } - p.pkg(pkg) - } -} - -func (p *exporter) signature(sig *types.Signature) { - // We need the receiver information (T vs *T) - // for methods associated with named types. - // We do not record interface receiver types in the - // export data because 1) the importer can derive them - // from the interface type and 2) they create cycles - // in the type graph. - if recv := sig.Recv(); recv != nil { - if _, ok := recv.Type().Underlying().(*types.Interface); !ok { - // 1-element tuple - p.int(1) - p.param(recv) - } else { - // 0-element tuple - p.int(0) - } - } else { - // 0-element tuple - p.int(0) - } - p.tuple(sig.Params()) - p.tuple(sig.Results()) - if sig.Variadic() { - p.int(1) - } else { - p.int(0) - } -} - -func (p *exporter) param(v *types.Var) { - p.string(v.Name()) - p.typ(v.Type()) -} - -func (p *exporter) tuple(t *types.Tuple) { - n := t.Len() - p.int(n) - for i := 0; i < n; i++ { - p.param(t.At(i)) - } -} - -// ---------------------------------------------------------------------------- -// encoders - -func (p *exporter) string(s string) { - p.bytes([]byte(s)) // (could be inlined if extra allocation matters) -} - -func (p *exporter) int(x int) { - p.int64(int64(x)) -} - -func (p *exporter) int64(x int64) { - if debug { - p.marker('i') - } - - if trace { - p.tracef("%d ", x) - } - - p.rawInt64(x) -} - -func (p *exporter) bytes(b []byte) { - if debug { - p.marker('b') - } - - if trace { - p.tracef("%q ", b) - } - - p.rawInt64(int64(len(b))) - if len(b) > 0 { - p.data = append(p.data, b...) - } -} - -// marker emits a marker byte and position information which makes -// it easy for a reader to detect if it is "out of sync". Used for -// debug format only. -func (p *exporter) marker(m byte) { - if debug { - p.data = append(p.data, m) - p.rawInt64(int64(len(p.data))) - } -} - -// rawInt64 should only be used by low-level encoders -func (p *exporter) rawInt64(x int64) { - var tmp [binary.MaxVarintLen64]byte - n := binary.PutVarint(tmp[:], x) - p.data = append(p.data, tmp[:n]...) -} - -// utility functions - -func (p *exporter) tracef(format string, args ...interface{}) { - // rewrite format string to take care of indentation - const indent = ". " - if strings.IndexAny(format, "{}\n") >= 0 { - var buf bytes.Buffer - for i := 0; i < len(format); i++ { - // no need to deal with runes - ch := format[i] - switch ch { - case '{': - p.indent += indent - case '}': - p.indent = p.indent[:len(p.indent)-len(indent)] - if i+1 < len(format) && format[i+1] == '\n' { - buf.WriteByte('\n') - buf.WriteString(p.indent) - buf.WriteString("} ") - i++ - continue - } - } - buf.WriteByte(ch) - if ch == '\n' { - buf.WriteString(p.indent) - } - } - format = buf.String() - } - fmt.Printf(format, args...) -} - -func exported(name string) bool { - return ast.IsExported(name) -} diff --git a/go/importer/import.go b/go/importer/import.go deleted file mode 100644 index 2de2054f66..0000000000 --- a/go/importer/import.go +++ /dev/null @@ -1,456 +0,0 @@ -// 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. - -// This implementation is loosely based on the algorithm described -// in: "On the linearization of graphs and writing symbol files", -// by R. Griesemer, Technical Report 156, ETH Zürich, 1991. - -// Package importer implements an exporter and importer for Go export data. -package importer // import "golang.org/x/tools/go/importer" - -import ( - "encoding/binary" - "fmt" - "go/token" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// ImportData imports a package from the serialized package data -// and returns the number of bytes consumed and a reference to the package. -// If data is obviously malformed, an error is returned but in -// general it is not recommended to call ImportData on untrusted -// data. -func ImportData(imports map[string]*types.Package, data []byte) (int, *types.Package, error) { - datalen := len(data) - - // check magic string - var s string - if len(data) >= len(magic) { - s = string(data[:len(magic)]) - data = data[len(magic):] - } - if s != magic { - return 0, nil, fmt.Errorf("incorrect magic string: got %q; want %q", s, magic) - } - - // check low-level encoding format - var m byte = 'm' // missing format - if len(data) > 0 { - m = data[0] - data = data[1:] - } - if m != format() { - return 0, nil, fmt.Errorf("incorrect low-level encoding format: got %c; want %c", m, format()) - } - - p := importer{ - data: data, - datalen: datalen, - imports: imports, - } - - // populate typList with predeclared types - for _, t := range predeclared { - p.typList = append(p.typList, t) - } - - if v := p.string(); v != version { - return 0, nil, fmt.Errorf("unknown version: got %s; want %s", v, version) - } - - pkg := p.pkg() - if debug && p.pkgList[0] != pkg { - panic("imported packaged not found in pkgList[0]") - } - - // read objects - n := p.int() - for i := 0; i < n; i++ { - p.obj(pkg) - } - - // complete interfaces - for _, typ := range p.typList { - if it, ok := typ.(*types.Interface); ok { - it.Complete() - } - } - - // package was imported completely and without errors - pkg.MarkComplete() - - return p.consumed(), pkg, nil -} - -type importer struct { - data []byte - datalen int - imports map[string]*types.Package - pkgList []*types.Package - typList []types.Type -} - -func (p *importer) pkg() *types.Package { - // if the package was seen before, i is its index (>= 0) - i := p.int() - if i >= 0 { - return p.pkgList[i] - } - - // otherwise, i is the package tag (< 0) - if i != packageTag { - panic(fmt.Sprintf("unexpected package tag %d", i)) - } - - // read package data - name := p.string() - path := p.string() - - // if the package was imported before, use that one; otherwise create a new one - pkg := p.imports[path] - if pkg == nil { - pkg = types.NewPackage(path, name) - p.imports[path] = pkg - } - p.pkgList = append(p.pkgList, pkg) - - return pkg -} - -func (p *importer) obj(pkg *types.Package) { - var obj types.Object - switch tag := p.int(); tag { - case constTag: - obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.value()) - case typeTag: - // type object is added to scope via respective named type - _ = p.typ().(*types.Named) - return - case varTag: - obj = types.NewVar(token.NoPos, pkg, p.string(), p.typ()) - case funcTag: - obj = types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature)) - default: - panic(fmt.Sprintf("unexpected object tag %d", tag)) - } - - if alt := pkg.Scope().Insert(obj); alt != nil { - panic(fmt.Sprintf("%s already declared", alt.Name())) - } -} - -func (p *importer) value() exact.Value { - switch kind := exact.Kind(p.int()); kind { - case falseTag: - return exact.MakeBool(false) - case trueTag: - return exact.MakeBool(true) - case int64Tag: - return exact.MakeInt64(p.int64()) - case floatTag: - return p.float() - case fractionTag: - return p.fraction() - case complexTag: - re := p.fraction() - im := p.fraction() - return exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) - case stringTag: - return exact.MakeString(p.string()) - default: - panic(fmt.Sprintf("unexpected value kind %d", kind)) - } -} - -func (p *importer) float() exact.Value { - sign := p.int() - if sign == 0 { - return exact.MakeInt64(0) - } - - x := p.ufloat() - if sign < 0 { - x = exact.UnaryOp(token.SUB, x, 0) - } - return x -} - -func (p *importer) fraction() exact.Value { - sign := p.int() - if sign == 0 { - return exact.MakeInt64(0) - } - - x := exact.BinaryOp(p.ufloat(), token.QUO, p.ufloat()) - if sign < 0 { - x = exact.UnaryOp(token.SUB, x, 0) - } - return x -} - -func (p *importer) ufloat() exact.Value { - exp := p.int() - x := exact.MakeFromBytes(p.bytes()) - switch { - case exp < 0: - d := exact.Shift(exact.MakeInt64(1), token.SHL, uint(-exp)) - x = exact.BinaryOp(x, token.QUO, d) - case exp > 0: - x = exact.Shift(x, token.SHL, uint(exp)) - } - return x -} - -func (p *importer) record(t types.Type) { - p.typList = append(p.typList, t) -} - -func (p *importer) typ() types.Type { - // if the type was seen before, i is its index (>= 0) - i := p.int() - if i >= 0 { - return p.typList[i] - } - - // otherwise, i is the type tag (< 0) - switch i { - case arrayTag: - t := new(types.Array) - p.record(t) - - n := p.int64() - *t = *types.NewArray(p.typ(), n) - return t - - case sliceTag: - t := new(types.Slice) - p.record(t) - - *t = *types.NewSlice(p.typ()) - return t - - case structTag: - t := new(types.Struct) - p.record(t) - - n := p.int() - fields := make([]*types.Var, n) - tags := make([]string, n) - for i := range fields { - fields[i] = p.field() - tags[i] = p.string() - } - *t = *types.NewStruct(fields, tags) - return t - - case pointerTag: - t := new(types.Pointer) - p.record(t) - - *t = *types.NewPointer(p.typ()) - return t - - case signatureTag: - t := new(types.Signature) - p.record(t) - - *t = *p.signature() - return t - - case interfaceTag: - // Create a dummy entry in the type list. This is safe because we - // cannot expect the interface type to appear in a cycle, as any - // such cycle must contain a named type which would have been - // first defined earlier. - n := len(p.typList) - p.record(nil) - - // read embedded interfaces - embeddeds := make([]*types.Named, p.int()) - for i := range embeddeds { - embeddeds[i] = p.typ().(*types.Named) - } - - // read methods - methods := make([]*types.Func, p.int()) - for i := range methods { - pkg, name := p.qualifiedName() - methods[i] = types.NewFunc(token.NoPos, pkg, name, p.typ().(*types.Signature)) - } - - t := types.NewInterface(methods, embeddeds) - p.typList[n] = t - return t - - case mapTag: - t := new(types.Map) - p.record(t) - - *t = *types.NewMap(p.typ(), p.typ()) - return t - - case chanTag: - t := new(types.Chan) - p.record(t) - - *t = *types.NewChan(types.ChanDir(p.int()), p.typ()) - return t - - case namedTag: - // read type object - name := p.string() - pkg := p.pkg() - scope := pkg.Scope() - obj := scope.Lookup(name) - - // if the object doesn't exist yet, create and insert it - if obj == nil { - obj = types.NewTypeName(token.NoPos, pkg, name, nil) - scope.Insert(obj) - } - - // associate new named type with obj if it doesn't exist yet - t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) - - // but record the existing type, if any - t := obj.Type().(*types.Named) - p.record(t) - - // read underlying type - t0.SetUnderlying(p.typ()) - - // read associated methods - for i, n := 0, p.int(); i < n; i++ { - t0.AddMethod(types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature))) - } - - return t - - default: - panic(fmt.Sprintf("unexpected type tag %d", i)) - } -} - -func deref(typ types.Type) types.Type { - if p, _ := typ.(*types.Pointer); p != nil { - return p.Elem() - } - return typ -} - -func (p *importer) field() *types.Var { - pkg, name := p.qualifiedName() - typ := p.typ() - - anonymous := false - if name == "" { - // anonymous field - typ must be T or *T and T must be a type name - switch typ := deref(typ).(type) { - case *types.Basic: // basic types are named types - pkg = nil - name = typ.Name() - case *types.Named: - obj := typ.Obj() - name = obj.Name() - // correct the field package for anonymous fields - if exported(name) { - pkg = p.pkgList[0] - } - default: - panic("anonymous field expected") - } - anonymous = true - } - - return types.NewField(token.NoPos, pkg, name, typ, anonymous) -} - -func (p *importer) qualifiedName() (*types.Package, string) { - name := p.string() - pkg := p.pkgList[0] // exported names assume current package - if !exported(name) { - pkg = p.pkg() - } - return pkg, name -} - -func (p *importer) signature() *types.Signature { - var recv *types.Var - if p.int() != 0 { - recv = p.param() - } - return types.NewSignature(recv, p.tuple(), p.tuple(), p.int() != 0) -} - -func (p *importer) param() *types.Var { - return types.NewVar(token.NoPos, nil, p.string(), p.typ()) -} - -func (p *importer) tuple() *types.Tuple { - vars := make([]*types.Var, p.int()) - for i := range vars { - vars[i] = p.param() - } - return types.NewTuple(vars...) -} - -// ---------------------------------------------------------------------------- -// decoders - -func (p *importer) string() string { - return string(p.bytes()) -} - -func (p *importer) int() int { - return int(p.int64()) -} - -func (p *importer) int64() int64 { - if debug { - p.marker('i') - } - - return p.rawInt64() -} - -// Note: bytes() returns the respective byte slice w/o copy. -func (p *importer) bytes() []byte { - if debug { - p.marker('b') - } - - var b []byte - if n := int(p.rawInt64()); n > 0 { - b = p.data[:n] - p.data = p.data[n:] - } - return b -} - -func (p *importer) marker(want byte) { - if debug { - if got := p.data[0]; got != want { - panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.consumed())) - } - p.data = p.data[1:] - - pos := p.consumed() - if n := int(p.rawInt64()); n != pos { - panic(fmt.Sprintf("incorrect position: got %d; want %d", n, pos)) - } - } -} - -// rawInt64 should only be used by low-level decoders -func (p *importer) rawInt64() int64 { - i, n := binary.Varint(p.data) - p.data = p.data[n:] - return i -} - -func (p *importer) consumed() int { - return p.datalen - len(p.data) -} diff --git a/go/importer/import_test.go b/go/importer/import_test.go deleted file mode 100644 index 95c373d804..0000000000 --- a/go/importer/import_test.go +++ /dev/null @@ -1,387 +0,0 @@ -// 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 importer - -import ( - "bufio" - "bytes" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "os" - "path/filepath" - "runtime" - "sort" - "strconv" - "testing" - "time" - - "golang.org/x/tools/go/gcimporter" - "golang.org/x/tools/go/types" -) - -var fset = token.NewFileSet() - -var tests = []string{ - `package p`, - - // consts - `package p; const X = true`, - `package p; const X, y, Z = true, false, 0 != 0`, - `package p; const ( A float32 = 1<= 750*time.Millisecond { - return - } - if lib == "cmd/internal/objfile" || lib == "net/http" { - // gcimporter doesn't support vendored imports. - // TODO(gri): fix. - continue - } - - pkg, err := pkgForPath(lib) - switch err := err.(type) { - case nil: - // ok - case *build.NoGoError: - // no Go files - ignore - continue - default: - t.Errorf("typecheck failed: %s", err) - continue - } - - size, gcsize := testExportImport(t, pkg, lib) - if gcsize == 0 { - // if gc import didn't happen, assume same size - // (and avoid division by zero below) - gcsize = size - } - - if testing.Verbose() { - fmt.Printf("%s\t%d\t%d\t%d%%\n", lib, size, gcsize, int(float64(size)*100/float64(gcsize))) - } - totSize += size - totGcSize += gcsize - } - - if testing.Verbose() { - fmt.Printf("\n%d\t%d\t%d%%\n", totSize, totGcSize, int(float64(totSize)*100/float64(totGcSize))) - } - - types.GcCompatibilityMode = false -} - -func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) { - data := ExportData(pkg0) - size = len(data) - - imports := make(map[string]*types.Package) - n, pkg1, err := ImportData(imports, data) - if err != nil { - t.Errorf("package %s: import failed: %s", pkg0.Name(), err) - return - } - if n != size { - t.Errorf("package %s: not all input data consumed", pkg0.Name()) - return - } - - s0 := pkgString(pkg0) - s1 := pkgString(pkg1) - if s1 != s0 { - t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0) - } - - // If we have a standard library, compare also against the gcimported package. - if path == "" { - return // not std library - } - - gcdata, err := gcExportData(path) - if err != nil { - if pkg0.Name() == "main" { - return // no export data present for main package - } - t.Errorf("package %s: couldn't get export data: %s", pkg0.Name(), err) - } - gcsize = len(gcdata) - - imports = make(map[string]*types.Package) - pkg2, err := gcImportData(imports, gcdata, path) - if err != nil { - t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err) - return - } - - s2 := pkgString(pkg2) - if s2 != s0 { - t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0) - } - - return -} - -func pkgForSource(src string) (*types.Package, error) { - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - return nil, err - } - return typecheck("import-test", f) -} - -func pkgForPath(path string) (*types.Package, error) { - // collect filenames - ctxt := build.Default - pkginfo, err := ctxt.Import(path, "", 0) - if err != nil { - return nil, err - } - filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...) - - // parse files - files := make([]*ast.File, len(filenames)) - for i, filename := range filenames { - var err error - files[i], err = parser.ParseFile(fset, filepath.Join(pkginfo.Dir, filename), nil, 0) - if err != nil { - return nil, err - } - } - - return typecheck(path, files...) -} - -var defaultConf = types.Config{ - // we only care about exports and thus can ignore function bodies - IgnoreFuncBodies: true, - // work around C imports if possible - FakeImportC: true, - // strconv exports IntSize as a constant. The type-checker must - // use the same word size otherwise the result of the type-checker - // and gc imports is different. We don't care about alignment - // since none of the tests have exported constants depending - // on alignment (see also issue 8366). - Sizes: &types.StdSizes{WordSize: strconv.IntSize / 8, MaxAlign: 8}, -} - -func typecheck(path string, files ...*ast.File) (*types.Package, error) { - return defaultConf.Check(path, fset, files, nil) -} - -// pkgString returns a string representation of a package's exported interface. -func pkgString(pkg *types.Package) string { - var buf bytes.Buffer - - fmt.Fprintf(&buf, "package %s\n", pkg.Name()) - - scope := pkg.Scope() - for _, name := range scope.Names() { - if exported(name) { - obj := scope.Lookup(name) - buf.WriteString(obj.String()) - - switch obj := obj.(type) { - case *types.Const: - // For now only print constant values if they are not float - // or complex. This permits comparing go/types results with - // gc-generated gcimported package interfaces. - info := obj.Type().Underlying().(*types.Basic).Info() - if info&types.IsFloat == 0 && info&types.IsComplex == 0 { - fmt.Fprintf(&buf, " = %s", obj.Val()) - } - - case *types.TypeName: - // Print associated methods. - // Basic types (e.g., unsafe.Pointer) have *types.Basic - // type rather than *types.Named; so we need to check. - if typ, _ := obj.Type().(*types.Named); typ != nil { - if n := typ.NumMethods(); n > 0 { - // Sort methods by name so that we get the - // same order independent of whether the - // methods got imported or coming directly - // for the source. - // TODO(gri) This should probably be done - // in go/types. - list := make([]*types.Func, n) - for i := 0; i < n; i++ { - list[i] = typ.Method(i) - } - sort.Sort(byName(list)) - - buf.WriteString("\nmethods (\n") - for _, m := range list { - fmt.Fprintf(&buf, "\t%s\n", m) - } - buf.WriteString(")") - } - } - } - buf.WriteByte('\n') - } - } - - return buf.String() -} - -var stdLibRoot = filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator) - -// The following std libraries are excluded from the stdLibs list. -var excluded = map[string]bool{ - "builtin": true, // contains type declarations with cycles - "unsafe": true, // contains fake declarations -} - -// stdLibs returns the list of standard library package paths. -func stdLibs() (list []string, err error) { - err = filepath.Walk(stdLibRoot, func(path string, info os.FileInfo, err error) error { - if err == nil && info.IsDir() { - // testdata directories don't contain importable libraries - if info.Name() == "testdata" { - return filepath.SkipDir - } - pkgPath := path[len(stdLibRoot):] // remove stdLibRoot - if len(pkgPath) > 0 && !excluded[pkgPath] { - list = append(list, pkgPath) - } - } - return nil - }) - return -} - -type byName []*types.Func - -func (a byName) Len() int { return len(a) } -func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byName) Less(i, j int) bool { return a[i].Name() < a[j].Name() } - -// gcExportData returns the gc-generated export data for the given path. -// It is based on a trimmed-down version of gcimporter.Import which does -// not do the actual import, does not handle package unsafe, and assumes -// that path is a correct standard library package path (no canonicalization, -// or handling of local import paths). -func gcExportData(path string) ([]byte, error) { - filename, id := gcimporter.FindPkg(path, "") - if filename == "" { - return nil, fmt.Errorf("can't find import: %s", path) - } - if id != path { - panic("path should be canonicalized") - } - - f, err := os.Open(filename) - if err != nil { - return nil, err - } - defer f.Close() - - buf := bufio.NewReader(f) - if err = gcimporter.FindExportData(buf); err != nil { - return nil, err - } - - var data []byte - for { - line, err := buf.ReadBytes('\n') - if err != nil { - return nil, err - } - data = append(data, line...) - // export data ends in "$$\n" - if len(line) == 3 && line[0] == '$' && line[1] == '$' { - return data, nil - } - } -} - -func gcImportData(imports map[string]*types.Package, data []byte, path string) (*types.Package, error) { - filename := fmt.Sprintf("", path) // so we have a decent error message if necessary - return gcimporter.ImportData(imports, filename, path, bufio.NewReader(bytes.NewBuffer(data))) -} diff --git a/go/importer/predefined.go b/go/importer/predefined.go deleted file mode 100644 index b23dfcb4c2..0000000000 --- a/go/importer/predefined.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2014 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 importer - -import "golang.org/x/tools/go/types" - -const ( - magic = "\n$$ exports $$\n" - version = "v0" -) - -// Tags. Must be < 0. -const ( - // Packages - packageTag = -(iota + 1) - - // Objects - constTag - typeTag - varTag - funcTag - - // Types - arrayTag - sliceTag - structTag - pointerTag - signatureTag - interfaceTag - mapTag - chanTag - namedTag - - // Values - falseTag - trueTag - int64Tag - floatTag - fractionTag - complexTag - stringTag -) - -var predeclared = []types.Type{ - // basic types - types.Typ[types.Bool], - types.Typ[types.Int], - types.Typ[types.Int8], - types.Typ[types.Int16], - types.Typ[types.Int32], - types.Typ[types.Int64], - types.Typ[types.Uint], - types.Typ[types.Uint8], - types.Typ[types.Uint16], - types.Typ[types.Uint32], - types.Typ[types.Uint64], - types.Typ[types.Uintptr], - types.Typ[types.Float32], - types.Typ[types.Float64], - types.Typ[types.Complex64], - types.Typ[types.Complex128], - types.Typ[types.String], - - // untyped types - types.Typ[types.UntypedBool], - types.Typ[types.UntypedInt], - types.Typ[types.UntypedRune], - types.Typ[types.UntypedFloat], - types.Typ[types.UntypedComplex], - types.Typ[types.UntypedString], - types.Typ[types.UntypedNil], - - // package unsafe - types.Typ[types.UnsafePointer], - - // aliases - types.Universe.Lookup("byte").Type(), - types.Universe.Lookup("rune").Type(), - - // error - types.Universe.Lookup("error").Type(), -} diff --git a/go/loader/cgo14.go b/go/loader/cgo14.go deleted file mode 100644 index d484c7db1f..0000000000 --- a/go/loader/cgo14.go +++ /dev/null @@ -1,210 +0,0 @@ -// 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. - -// +build !go1.5 - -package loader - -// This file handles cgo preprocessing of files containing `import "C"`. -// -// DESIGN -// -// The approach taken is to run the cgo processor on the package's -// CgoFiles and parse the output, faking the filenames of the -// resulting ASTs so that the synthetic file containing the C types is -// called "C" (e.g. "~/go/src/net/C") and the preprocessed files -// have their original names (e.g. "~/go/src/net/cgo_unix.go"), -// not the names of the actual temporary files. -// -// The advantage of this approach is its fidelity to 'go build'. The -// downside is that the token.Position.Offset for each AST node is -// incorrect, being an offset within the temporary file. Line numbers -// should still be correct because of the //line comments. -// -// The logic of this file is mostly plundered from the 'go build' -// tool, which also invokes the cgo preprocessor. -// -// -// REJECTED ALTERNATIVE -// -// An alternative approach that we explored is to extend go/types' -// Importer mechanism to provide the identity of the importing package -// so that each time `import "C"` appears it resolves to a different -// synthetic package containing just the objects needed in that case. -// The loader would invoke cgo but parse only the cgo_types.go file -// defining the package-level objects, discarding the other files -// resulting from preprocessing. -// -// The benefit of this approach would have been that source-level -// syntax information would correspond exactly to the original cgo -// file, with no preprocessing involved, making source tools like -// godoc, oracle, and eg happy. However, the approach was rejected -// due to the additional complexity it would impose on go/types. (It -// made for a beautiful demo, though.) -// -// cgo files, despite their *.go extension, are not legal Go source -// files per the specification since they may refer to unexported -// members of package "C" such as C.int. Also, a function such as -// C.getpwent has in effect two types, one matching its C type and one -// which additionally returns (errno C.int). The cgo preprocessor -// uses name mangling to distinguish these two functions in the -// processed code, but go/types would need to duplicate this logic in -// its handling of function calls, analogous to the treatment of map -// lookups in which y=m[k] and y,ok=m[k] are both legal. - -import ( - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" -) - -// processCgoFiles invokes the cgo preprocessor on bp.CgoFiles, parses -// the output and returns the resulting ASTs. -// -func processCgoFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) { - tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C") - if err != nil { - return nil, err - } - defer os.RemoveAll(tmpdir) - - pkgdir := bp.Dir - if DisplayPath != nil { - pkgdir = DisplayPath(pkgdir) - } - - cgoFiles, cgoDisplayFiles, err := runCgo(bp, pkgdir, tmpdir) - if err != nil { - return nil, err - } - var files []*ast.File - for i := range cgoFiles { - rd, err := os.Open(cgoFiles[i]) - if err != nil { - return nil, err - } - display := filepath.Join(bp.Dir, cgoDisplayFiles[i]) - f, err := parser.ParseFile(fset, display, rd, mode) - rd.Close() - if err != nil { - return nil, err - } - files = append(files, f) - } - return files, nil -} - -var cgoRe = regexp.MustCompile(`[/\\:]`) - -// runCgo invokes the cgo preprocessor on bp.CgoFiles and returns two -// lists of files: the resulting processed files (in temporary -// directory tmpdir) and the corresponding names of the unprocessed files. -// -// runCgo is adapted from (*builder).cgo in -// $GOROOT/src/cmd/go/build.go, but these features are unsupported: -// Objective C, CGOPKGPATH, CGO_FLAGS. -// -func runCgo(bp *build.Package, pkgdir, tmpdir string) (files, displayFiles []string, err error) { - cgoCPPFLAGS, _, _, cgoLDFLAGS := cflags(bp, true) - _, cgoexeCFLAGS, _, _ := cflags(bp, false) - - if len(bp.CgoPkgConfig) > 0 { - pcCFLAGS, pcLDFLAGS, err := pkgConfigFlags(bp) - if err != nil { - return nil, nil, err - } - cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) - cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) - } - - // Allows including _cgo_export.h from .[ch] files in the package. - cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir) - - // _cgo_gotypes.go (displayed "C") contains the type definitions. - files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go")) - displayFiles = append(displayFiles, "C") - for _, fn := range bp.CgoFiles { - // "foo.cgo1.go" (displayed "foo.go") is the processed Go source. - f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_") - files = append(files, filepath.Join(tmpdir, f+"cgo1.go")) - displayFiles = append(displayFiles, fn) - } - - var cgoflags []string - if bp.Goroot && bp.ImportPath == "runtime/cgo" { - cgoflags = append(cgoflags, "-import_runtime_cgo=false") - } - if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" { - cgoflags = append(cgoflags, "-import_syscall=false") - } - - args := stringList( - "go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--", - cgoCPPFLAGS, cgoLDFLAGS, cgoexeCFLAGS, bp.CgoFiles, - ) - if false { - log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir) - } - cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = pkgdir - cmd.Stdout = os.Stderr - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err) - } - - return files, displayFiles, nil -} - -// -- unmodified from 'go build' --------------------------------------- - -// Return the flags to use when invoking the C or C++ compilers, or cgo. -func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) { - var defaults string - if def { - defaults = "-g -O2" - } - - cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) - cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) - cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) - ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) - return -} - -// envList returns the value of the given environment variable broken -// into fields, using the default value when the variable is empty. -func envList(key, def string) []string { - v := os.Getenv(key) - if v == "" { - v = def - } - return strings.Fields(v) -} - -// stringList's arguments should be a sequence of string or []string values. -// stringList flattens them into a single []string. -func stringList(args ...interface{}) []string { - var x []string - for _, arg := range args { - switch arg := arg.(type) { - case []string: - x = append(x, arg...) - case string: - x = append(x, arg) - default: - panic("stringList: invalid argument") - } - } - return x -} diff --git a/go/loader/example14_test.go b/go/loader/example14_test.go deleted file mode 100644 index 67dbefa538..0000000000 --- a/go/loader/example14_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2015 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. - -// +build !go1.5 -// +build !windows - -package loader_test - -import ( - "fmt" - "go/token" - "log" - "path/filepath" - "runtime" - "sort" - - "golang.org/x/tools/go/loader" -) - -func printProgram(prog *loader.Program) { - // Created packages are the initial packages specified by a call - // to CreateFromFilenames or CreateFromFiles. - var names []string - for _, info := range prog.Created { - names = append(names, info.Pkg.Path()) - } - fmt.Printf("created: %s\n", names) - - // Imported packages are the initial packages specified by a - // call to Import or ImportWithTests. - names = nil - for _, info := range prog.Imported { - names = append(names, info.Pkg.Path()) - } - sort.Strings(names) - fmt.Printf("imported: %s\n", names) - - // InitialPackages contains the union of created and imported. - names = nil - for _, info := range prog.InitialPackages() { - names = append(names, info.Pkg.Path()) - } - sort.Strings(names) - fmt.Printf("initial: %s\n", names) - - // AllPackages contains all initial packages and their dependencies. - names = nil - for pkg := range prog.AllPackages { - names = append(names, pkg.Path()) - } - sort.Strings(names) - fmt.Printf("all: %s\n", names) -} - -func printFilenames(fset *token.FileSet, info *loader.PackageInfo) { - var names []string - for _, f := range info.Files { - names = append(names, filepath.Base(fset.File(f.Pos()).Name())) - } - fmt.Printf("%s.Files: %s\n", info.Pkg.Path(), names) -} - -// This example loads a set of packages and all of their dependencies -// from a typical command-line. FromArgs parses a command line and -// makes calls to the other methods of Config shown in the examples that -// follow. -func ExampleConfig_FromArgs() { - args := []string{"mytool", "unicode/utf8", "errors", "runtime", "--", "foo", "bar"} - const wantTests = false - - var conf loader.Config - rest, err := conf.FromArgs(args[1:], wantTests) - prog, err := conf.Load() - if err != nil { - log.Fatal(err) - } - - fmt.Printf("rest: %s\n", rest) - printProgram(prog) - // Output: - // rest: [foo bar] - // created: [] - // imported: [errors runtime unicode/utf8] - // initial: [errors runtime unicode/utf8] - // all: [errors runtime unicode/utf8 unsafe] -} - -// This example creates and type-checks a single package (without tests) -// from a list of filenames, and loads all of its dependencies. -func ExampleConfig_CreateFromFilenames() { - var conf loader.Config - filename := filepath.Join(runtime.GOROOT(), "src/container/heap/heap.go") - conf.CreateFromFilenames("container/heap", filename) - prog, err := conf.Load() - if err != nil { - log.Fatal(err) - } - - printProgram(prog) - // Output: - // created: [container/heap] - // imported: [] - // initial: [container/heap] - // all: [container/heap sort] -} - -// In the examples below, for stability, the chosen packages are -// relatively small, platform-independent, and low-level (and thus -// infrequently changing). -// The strconv package has internal and external tests. - -const hello = `package main - -import "fmt" - -func main() { - fmt.Println("Hello, world.") -} -` - -// This example creates and type-checks a package from a list of -// already-parsed files, and loads all its dependencies. -func ExampleConfig_CreateFromFiles() { - var conf loader.Config - f, err := conf.ParseFile("hello.go", hello) - if err != nil { - log.Fatal(err) - } - conf.CreateFromFiles("hello", f) - prog, err := conf.Load() - if err != nil { - log.Fatal(err) - } - - printProgram(prog) - printFilenames(prog.Fset, prog.Package("strconv")) - // Output: - // created: [hello] - // imported: [] - // initial: [hello] - // all: [errors fmt hello io math os reflect runtime strconv sync sync/atomic syscall time unicode/utf8 unsafe] - // strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go] -} - -// This example imports three packages, including the tests for one of -// them, and loads all their dependencies. -func ExampleConfig_Import() { - // ImportWithTest("strconv") causes strconv to include - // internal_test.go, and creates an external test package, - // strconv_test. - // (Compare with the example of CreateFromFiles.) - - var conf loader.Config - conf.Import("unicode/utf8") - conf.Import("errors") - conf.ImportWithTests("strconv") - prog, err := conf.Load() - if err != nil { - log.Fatal(err) - } - - printProgram(prog) - printFilenames(prog.Fset, prog.Package("strconv")) - printFilenames(prog.Fset, prog.Package("strconv_test")) - // Output: - // created: [strconv_test] - // imported: [errors strconv unicode/utf8] - // initial: [errors strconv strconv_test unicode/utf8] - // all: [bufio bytes errors flag fmt io math math/rand os reflect runtime runtime/pprof sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8 unsafe] - // strconv.Files: [atob.go atof.go atoi.go decimal.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go] - // strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go fp_test.go ftoa_test.go itoa_test.go quote_example_test.go quote_test.go strconv_test.go] -} diff --git a/go/loader/loader14.go b/go/loader/loader14.go deleted file mode 100644 index 4fd4b2a69b..0000000000 --- a/go/loader/loader14.go +++ /dev/null @@ -1,1019 +0,0 @@ -// 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. - -// +build !go1.5 - -package loader - -// See doc.go for package documentation and implementation notes. - -import ( - "errors" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "os" - "sort" - "strings" - "sync" - "time" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" -) - -const trace = false // show timing info for type-checking - -// Config specifies the configuration for loading a whole program from -// Go source code. -// The zero value for Config is a ready-to-use default configuration. -type Config struct { - // Fset is the file set for the parser to use when loading the - // program. If nil, it may be lazily initialized by any - // method of Config. - Fset *token.FileSet - - // ParserMode specifies the mode to be used by the parser when - // loading source packages. - ParserMode parser.Mode - - // TypeChecker contains options relating to the type checker. - // - // The supplied IgnoreFuncBodies is not used; the effective - // value comes from the TypeCheckFuncBodies func below. - // The supplied Import function is not used either. - TypeChecker types.Config - - // TypeCheckFuncBodies is a predicate over package paths. - // A package for which the predicate is false will - // have its package-level declarations type checked, but not - // its function bodies; this can be used to quickly load - // dependencies from source. If nil, all func bodies are type - // checked. - TypeCheckFuncBodies func(path string) bool - - // If Build is non-nil, it is used to locate source packages. - // Otherwise &build.Default is used. - // - // By default, cgo is invoked to preprocess Go files that - // import the fake package "C". This behaviour can be - // disabled by setting CGO_ENABLED=0 in the environment prior - // to startup, or by setting Build.CgoEnabled=false. - Build *build.Context - - // The current directory, used for resolving relative package - // references such as "./go/loader". If empty, os.Getwd will be - // used instead. - Cwd string - - // If DisplayPath is non-nil, it is used to transform each - // file name obtained from Build.Import(). This can be used - // to prevent a virtualized build.Config's file names from - // leaking into the user interface. - DisplayPath func(path string) string - - // If AllowErrors is true, Load will return a Program even - // if some of the its packages contained I/O, parser or type - // errors; such errors are accessible via PackageInfo.Errors. If - // false, Load will fail if any package had an error. - AllowErrors bool - - // CreatePkgs specifies a list of non-importable initial - // packages to create. The resulting packages will appear in - // the corresponding elements of the Program.Created slice. - CreatePkgs []PkgSpec - - // ImportPkgs specifies a set of initial packages to load. - // The map keys are package paths. - // - // The map value indicates whether to load tests. If true, Load - // will add and type-check two lists of files to the package: - // non-test files followed by in-package *_test.go files. In - // addition, it will append the external test package (if any) - // to Program.Created. - ImportPkgs map[string]bool - - // FindPackage is called during Load to create the build.Package - // for a given import path from a given directory. - // If FindPackage is nil, a default implementation - // based on ctxt.Import is used. A client may use this hook to - // adapt to a proprietary build system that does not follow the - // "go build" layout conventions, for example. - // - // It must be safe to call concurrently from multiple goroutines. - FindPackage func(ctxt *build.Context, fromDir, importPath string, mode build.ImportMode) (*build.Package, error) -} - -// A PkgSpec specifies a non-importable package to be created by Load. -// Files are processed first, but typically only one of Files and -// Filenames is provided. The path needn't be globally unique. -// -type PkgSpec struct { - Path string // package path ("" => use package declaration) - Files []*ast.File // ASTs of already-parsed files - Filenames []string // names of files to be parsed -} - -// A Program is a Go program loaded from source as specified by a Config. -type Program struct { - Fset *token.FileSet // the file set for this program - - // Created[i] contains the initial package whose ASTs or - // filenames were supplied by Config.CreatePkgs[i], followed by - // the external test package, if any, of each package in - // Config.ImportPkgs ordered by ImportPath. - // - // NOTE: these files must not import "C". Cgo preprocessing is - // only performed on imported packages, not ad hoc packages. - // - // TODO(adonovan): we need to copy and adapt the logic of - // goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make - // Config.Import and Config.Create methods return the same kind - // of entity, essentially a build.Package. - // Perhaps we can even reuse that type directly. - Created []*PackageInfo - - // Imported contains the initially imported packages, - // as specified by Config.ImportPkgs. - Imported map[string]*PackageInfo - - // AllPackages contains the PackageInfo of every package - // encountered by Load: all initial packages and all - // dependencies, including incomplete ones. - AllPackages map[*types.Package]*PackageInfo - - // importMap is the canonical mapping of package paths to - // packages. It contains all Imported initial packages, but not - // Created ones, and all imported dependencies. - importMap map[string]*types.Package -} - -// PackageInfo holds the ASTs and facts derived by the type-checker -// for a single package. -// -// Not mutated once exposed via the API. -// -type PackageInfo struct { - Pkg *types.Package - Importable bool // true if 'import "Pkg.Path()"' would resolve to this - TransitivelyErrorFree bool // true if Pkg and all its dependencies are free of errors - Files []*ast.File // syntax trees for the package's files - Errors []error // non-nil if the package had errors - types.Info // type-checker deductions. - dir string // package directory - - checker *types.Checker // transient type-checker state - errorFunc func(error) -} - -func (info *PackageInfo) String() string { return info.Pkg.Path() } - -func (info *PackageInfo) appendError(err error) { - if info.errorFunc != nil { - info.errorFunc(err) - } else { - fmt.Fprintln(os.Stderr, err) - } - info.Errors = append(info.Errors, err) -} - -func (conf *Config) fset() *token.FileSet { - if conf.Fset == nil { - conf.Fset = token.NewFileSet() - } - return conf.Fset -} - -// ParseFile is a convenience function (intended for testing) that invokes -// the parser using the Config's FileSet, which is initialized if nil. -// -// src specifies the parser input as a string, []byte, or io.Reader, and -// filename is its apparent name. If src is nil, the contents of -// filename are read from the file system. -// -func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) { - // TODO(adonovan): use conf.build() etc like parseFiles does. - return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode) -} - -// FromArgsUsage is a partial usage message that applications calling -// FromArgs may wish to include in their -help output. -const FromArgsUsage = ` - is a list of arguments denoting a set of initial packages. -It may take one of two forms: - -1. A list of *.go source files. - - All of the specified files are loaded, parsed and type-checked - as a single package. All the files must belong to the same directory. - -2. A list of import paths, each denoting a package. - - The package's directory is found relative to the $GOROOT and - $GOPATH using similar logic to 'go build', and the *.go files in - that directory are loaded, parsed and type-checked as a single - package. - - In addition, all *_test.go files in the directory are then loaded - and parsed. Those files whose package declaration equals that of - the non-*_test.go files are included in the primary package. Test - files whose package declaration ends with "_test" are type-checked - as another package, the 'external' test package, so that a single - import path may denote two packages. (Whether this behaviour is - enabled is tool-specific, and may depend on additional flags.) - -A '--' argument terminates the list of packages. -` - -// FromArgs interprets args as a set of initial packages to load from -// source and updates the configuration. It returns the list of -// unconsumed arguments. -// -// It is intended for use in command-line interfaces that require a -// set of initial packages to be specified; see FromArgsUsage message -// for details. -// -// Only superficial errors are reported at this stage; errors dependent -// on I/O are detected during Load. -// -func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) { - var rest []string - for i, arg := range args { - if arg == "--" { - rest = args[i+1:] - args = args[:i] - break // consume "--" and return the remaining args - } - } - - if len(args) > 0 && strings.HasSuffix(args[0], ".go") { - // Assume args is a list of a *.go files - // denoting a single ad hoc package. - for _, arg := range args { - if !strings.HasSuffix(arg, ".go") { - return nil, fmt.Errorf("named files must be .go files: %s", arg) - } - } - conf.CreateFromFilenames("", args...) - } else { - // Assume args are directories each denoting a - // package and (perhaps) an external test, iff xtest. - for _, arg := range args { - if xtest { - conf.ImportWithTests(arg) - } else { - conf.Import(arg) - } - } - } - - return rest, nil -} - -// CreateFromFilenames is a convenience function that adds -// a conf.CreatePkgs entry to create a package of the specified *.go -// files. -// -func (conf *Config) CreateFromFilenames(path string, filenames ...string) { - conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames}) -} - -// CreateFromFiles is a convenience function that adds a conf.CreatePkgs -// entry to create package of the specified path and parsed files. -// -func (conf *Config) CreateFromFiles(path string, files ...*ast.File) { - conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files}) -} - -// ImportWithTests is a convenience function that adds path to -// ImportPkgs, the set of initial source packages located relative to -// $GOPATH. The package will be augmented by any *_test.go files in -// its directory that contain a "package x" (not "package x_test") -// declaration. -// -// In addition, if any *_test.go files contain a "package x_test" -// declaration, an additional package comprising just those files will -// be added to CreatePkgs. -// -func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) } - -// Import is a convenience function that adds path to ImportPkgs, the -// set of initial packages that will be imported from source. -// -func (conf *Config) Import(path string) { conf.addImport(path, false) } - -func (conf *Config) addImport(path string, tests bool) { - if path == "C" { - return // ignore; not a real package - } - if conf.ImportPkgs == nil { - conf.ImportPkgs = make(map[string]bool) - } - conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests -} - -// PathEnclosingInterval returns the PackageInfo and ast.Node that -// contain source interval [start, end), and all the node's ancestors -// up to the AST root. It searches all ast.Files of all packages in prog. -// exact is defined as for astutil.PathEnclosingInterval. -// -// The zero value is returned if not found. -// -func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) { - for _, info := range prog.AllPackages { - for _, f := range info.Files { - if f.Pos() == token.NoPos { - // This can happen if the parser saw - // too many errors and bailed out. - // (Use parser.AllErrors to prevent that.) - continue - } - if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) { - continue - } - if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil { - return info, path, exact - } - } - } - return nil, nil, false -} - -// InitialPackages returns a new slice containing the set of initial -// packages (Created + Imported) in unspecified order. -// -func (prog *Program) InitialPackages() []*PackageInfo { - infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported)) - infos = append(infos, prog.Created...) - for _, info := range prog.Imported { - infos = append(infos, info) - } - return infos -} - -// Package returns the ASTs and results of type checking for the -// specified package. -func (prog *Program) Package(path string) *PackageInfo { - if info, ok := prog.AllPackages[prog.importMap[path]]; ok { - return info - } - for _, info := range prog.Created { - if path == info.Pkg.Path() { - return info - } - } - return nil -} - -// ---------- Implementation ---------- - -// importer holds the working state of the algorithm. -type importer struct { - conf *Config // the client configuration - start time.Time // for logging - - progMu sync.Mutex // guards prog - prog *Program // the resulting program - - // findpkg is a memoization of FindPackage. - findpkgMu sync.Mutex // guards findpkg - findpkg map[findpkgKey]findpkgValue - - importedMu sync.Mutex // guards imported - imported map[string]*importInfo // all imported packages (incl. failures) by import path - - // import dependency graph: graph[x][y] => x imports y - // - // Since non-importable packages cannot be cyclic, we ignore - // their imports, thus we only need the subgraph over importable - // packages. Nodes are identified by their import paths. - graphMu sync.Mutex - graph map[string]map[string]bool -} - -type findpkgKey struct { - importPath string - fromDir string - mode build.ImportMode -} - -type findpkgValue struct { - bp *build.Package - err error -} - -// importInfo tracks the success or failure of a single import. -// -// Upon completion, exactly one of info and err is non-nil: -// info on successful creation of a package, err otherwise. -// A successful package may still contain type errors. -// -type importInfo struct { - path string // import path - info *PackageInfo // results of typechecking (including errors) - complete chan struct{} // closed to broadcast that info is set. -} - -// awaitCompletion blocks until ii is complete, -// i.e. the info field is safe to inspect. -func (ii *importInfo) awaitCompletion() { - <-ii.complete // wait for close -} - -// Complete marks ii as complete. -// Its info and err fields will not be subsequently updated. -func (ii *importInfo) Complete(info *PackageInfo) { - if info == nil { - panic("info == nil") - } - ii.info = info - close(ii.complete) -} - -type importError struct { - path string // import path - err error // reason for failure to create a package -} - -// Load creates the initial packages specified by conf.{Create,Import}Pkgs, -// loading their dependencies packages as needed. -// -// On success, Load returns a Program containing a PackageInfo for -// each package. On failure, it returns an error. -// -// If AllowErrors is true, Load will return a Program even if some -// packages contained I/O, parser or type errors, or if dependencies -// were missing. (Such errors are accessible via PackageInfo.Errors. If -// false, Load will fail if any package had an error. -// -// It is an error if no packages were loaded. -// -func (conf *Config) Load() (*Program, error) { - // Create a simple default error handler for parse/type errors. - if conf.TypeChecker.Error == nil { - conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) } - } - - // Set default working directory for relative package references. - if conf.Cwd == "" { - var err error - conf.Cwd, err = os.Getwd() - if err != nil { - return nil, err - } - } - - // Install default FindPackage hook using go/build logic. - if conf.FindPackage == nil { - conf.FindPackage = func(ctxt *build.Context, path, fromDir string, mode build.ImportMode) (*build.Package, error) { - ioLimit <- true - bp, err := ctxt.Import(path, fromDir, mode) - <-ioLimit - if _, ok := err.(*build.NoGoError); ok { - return bp, nil // empty directory is not an error - } - return bp, err - } - } - - prog := &Program{ - Fset: conf.fset(), - Imported: make(map[string]*PackageInfo), - importMap: make(map[string]*types.Package), - AllPackages: make(map[*types.Package]*PackageInfo), - } - - imp := importer{ - conf: conf, - prog: prog, - findpkg: make(map[findpkgKey]findpkgValue), - imported: make(map[string]*importInfo), - start: time.Now(), - graph: make(map[string]map[string]bool), - } - - // -- loading proper (concurrent phase) -------------------------------- - - var errpkgs []string // packages that contained errors - - // Load the initially imported packages and their dependencies, - // in parallel. - infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, 0) - for _, ie := range importErrors { - conf.TypeChecker.Error(ie.err) // failed to create package - errpkgs = append(errpkgs, ie.path) - } - for _, info := range infos { - prog.Imported[info.Pkg.Path()] = info - } - - // Augment the designated initial packages by their tests. - // Dependencies are loaded in parallel. - var xtestPkgs []*build.Package - for importPath, augment := range conf.ImportPkgs { - if !augment { - continue - } - - bp, err := imp.findPackage(importPath, conf.Cwd, 0) - if err != nil { - // Package not found, or can't even parse package declaration. - // Already reported by previous loop; ignore it. - continue - } - - // Needs external test package? - if len(bp.XTestGoFiles) > 0 { - xtestPkgs = append(xtestPkgs, bp) - } - - // Consult the cache using the canonical package path. - path := bp.ImportPath - imp.importedMu.Lock() // (unnecessary, we're sequential here) - ii, ok := imp.imported[path] - // Paranoid checks added due to issue #11012. - if !ok { - // Unreachable. - // The previous loop called importAll and thus - // startLoad for each path in ImportPkgs, which - // populates imp.imported[path] with a non-zero value. - panic(fmt.Sprintf("imported[%q] not found", path)) - } - if ii == nil { - // Unreachable. - // The ii values in this loop are the same as in - // the previous loop, which enforced the invariant - // that at least one of ii.err and ii.info is non-nil. - panic(fmt.Sprintf("imported[%q] == nil", path)) - } - if ii.info == nil { - // Unreachable. - // awaitCompletion has the postcondition - // ii.info != nil. - panic(fmt.Sprintf("imported[%q].info = nil", path)) - } - info := ii.info - imp.importedMu.Unlock() - - // Parse the in-package test files. - files, errs := imp.conf.parsePackageFiles(bp, 't') - for _, err := range errs { - info.appendError(err) - } - - // The test files augmenting package P cannot be imported, - // but may import packages that import P, - // so we must disable the cycle check. - imp.addFiles(info, files, false) - } - - createPkg := func(path string, files []*ast.File, errs []error) { - // TODO(adonovan): fix: use dirname of files, not cwd. - info := imp.newPackageInfo(path, conf.Cwd) - for _, err := range errs { - info.appendError(err) - } - - // Ad hoc packages are non-importable, - // so no cycle check is needed. - // addFiles loads dependencies in parallel. - imp.addFiles(info, files, false) - prog.Created = append(prog.Created, info) - } - - // Create packages specified by conf.CreatePkgs. - for _, cp := range conf.CreatePkgs { - files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode) - files = append(files, cp.Files...) - - path := cp.Path - if path == "" { - if len(files) > 0 { - path = files[0].Name.Name - } else { - path = "(unnamed)" - } - } - createPkg(path, files, errs) - } - - // Create external test packages. - sort.Sort(byImportPath(xtestPkgs)) - for _, bp := range xtestPkgs { - files, errs := imp.conf.parsePackageFiles(bp, 'x') - createPkg(bp.ImportPath+"_test", files, errs) - } - - // -- finishing up (sequential) ---------------------------------------- - - if len(prog.Imported)+len(prog.Created) == 0 { - return nil, errors.New("no initial packages were loaded") - } - - // Create infos for indirectly imported packages. - // e.g. incomplete packages without syntax, loaded from export data. - for _, obj := range prog.importMap { - info := prog.AllPackages[obj] - if info == nil { - prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true} - } else { - // finished - info.checker = nil - info.errorFunc = nil - } - } - - if !conf.AllowErrors { - // Report errors in indirectly imported packages. - for _, info := range prog.AllPackages { - if len(info.Errors) > 0 { - errpkgs = append(errpkgs, info.Pkg.Path()) - } - } - if errpkgs != nil { - var more string - if len(errpkgs) > 3 { - more = fmt.Sprintf(" and %d more", len(errpkgs)-3) - errpkgs = errpkgs[:3] - } - return nil, fmt.Errorf("couldn't load packages due to errors: %s%s", - strings.Join(errpkgs, ", "), more) - } - } - - markErrorFreePackages(prog.AllPackages) - - return prog, nil -} - -type byImportPath []*build.Package - -func (b byImportPath) Len() int { return len(b) } -func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath } -func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] } - -// markErrorFreePackages sets the TransitivelyErrorFree flag on all -// applicable packages. -func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) { - // Build the transpose of the import graph. - importedBy := make(map[*types.Package]map[*types.Package]bool) - for P := range allPackages { - for _, Q := range P.Imports() { - clients, ok := importedBy[Q] - if !ok { - clients = make(map[*types.Package]bool) - importedBy[Q] = clients - } - clients[P] = true - } - } - - // Find all packages reachable from some error package. - reachable := make(map[*types.Package]bool) - var visit func(*types.Package) - visit = func(p *types.Package) { - if !reachable[p] { - reachable[p] = true - for q := range importedBy[p] { - visit(q) - } - } - } - for _, info := range allPackages { - if len(info.Errors) > 0 { - visit(info.Pkg) - } - } - - // Mark the others as "transitively error-free". - for _, info := range allPackages { - if !reachable[info.Pkg] { - info.TransitivelyErrorFree = true - } - } -} - -// build returns the effective build context. -func (conf *Config) build() *build.Context { - if conf.Build != nil { - return conf.Build - } - return &build.Default -} - -// parsePackageFiles enumerates the files belonging to package path, -// then loads, parses and returns them, plus a list of I/O or parse -// errors that were encountered. -// -// 'which' indicates which files to include: -// 'g': include non-test *.go source files (GoFiles + processed CgoFiles) -// 't': include in-package *_test.go source files (TestGoFiles) -// 'x': include external *_test.go source files. (XTestGoFiles) -// -func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) { - if bp.ImportPath == "unsafe" { - return nil, nil - } - var filenames []string - switch which { - case 'g': - filenames = bp.GoFiles - case 't': - filenames = bp.TestGoFiles - case 'x': - filenames = bp.XTestGoFiles - default: - panic(which) - } - - files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode) - - // Preprocess CgoFiles and parse the outputs (sequentially). - if which == 'g' && bp.CgoFiles != nil { - cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode) - if err != nil { - errs = append(errs, err) - } else { - files = append(files, cgofiles...) - } - } - - return files, errs -} - -// doImport imports the package denoted by path. -// It implements the types.Importer signature. -// -// It returns an error if a package could not be created -// (e.g. go/build or parse error), but type errors are reported via -// the types.Config.Error callback (the first of which is also saved -// in the package's PackageInfo). -// -// Idempotent. -// -func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) { - if to == "C" { - // This should be unreachable, but ad hoc packages are - // not currently subject to cgo preprocessing. - // See https://github.com/golang/go/issues/11627. - return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`, - from.Pkg.Path()) - } - - bp, err := imp.findPackage(to, from.dir, 0) - if err != nil { - return nil, err - } - - // The standard unsafe package is handled specially, - // and has no PackageInfo. - if bp.ImportPath == "unsafe" { - return types.Unsafe, nil - } - - // Look for the package in the cache using its canonical path. - path := bp.ImportPath - imp.importedMu.Lock() - ii := imp.imported[path] - imp.importedMu.Unlock() - if ii == nil { - panic("internal error: unexpected import: " + path) - } - if ii.info != nil { - return ii.info.Pkg, nil - } - - // Import of incomplete package: this indicates a cycle. - fromPath := from.Pkg.Path() - if cycle := imp.findPath(path, fromPath); cycle != nil { - cycle = append([]string{fromPath}, cycle...) - return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> ")) - } - - panic("internal error: import of incomplete (yet acyclic) package: " + fromPath) -} - -// findPackage locates the package denoted by the importPath in the -// specified directory. -func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) { - // TODO(adonovan): opt: non-blocking duplicate-suppressing cache. - // i.e. don't hold the lock around FindPackage. - key := findpkgKey{importPath, fromDir, mode} - imp.findpkgMu.Lock() - defer imp.findpkgMu.Unlock() - v, ok := imp.findpkg[key] - if !ok { - bp, err := imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode) - v = findpkgValue{bp, err} - imp.findpkg[key] = v - } - return v.bp, v.err -} - -// importAll loads, parses, and type-checks the specified packages in -// parallel and returns their completed importInfos in unspecified order. -// -// fromPath is the package path of the importing package, if it is -// importable, "" otherwise. It is used for cycle detection. -// -// fromDir is the directory containing the import declaration that -// caused these imports. -// -func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) { - // TODO(adonovan): opt: do the loop in parallel once - // findPackage is non-blocking. - var pending []*importInfo - for importPath := range imports { - bp, err := imp.findPackage(importPath, fromDir, mode) - if err != nil { - errors = append(errors, importError{ - path: importPath, - err: err, - }) - continue - } - pending = append(pending, imp.startLoad(bp)) - } - - if fromPath != "" { - // We're loading a set of imports. - // - // We must record graph edges from the importing package - // to its dependencies, and check for cycles. - imp.graphMu.Lock() - deps, ok := imp.graph[fromPath] - if !ok { - deps = make(map[string]bool) - imp.graph[fromPath] = deps - } - for _, ii := range pending { - deps[ii.path] = true - } - imp.graphMu.Unlock() - } - - for _, ii := range pending { - if fromPath != "" { - if cycle := imp.findPath(ii.path, fromPath); cycle != nil { - // Cycle-forming import: we must not await its - // completion since it would deadlock. - // - // We don't record the error in ii since - // the error is really associated with the - // cycle-forming edge, not the package itself. - // (Also it would complicate the - // invariants of importPath completion.) - if trace { - fmt.Fprintln(os.Stderr, "import cycle: %q", cycle) - } - continue - } - } - ii.awaitCompletion() - infos = append(infos, ii.info) - } - - return infos, errors -} - -// findPath returns an arbitrary path from 'from' to 'to' in the import -// graph, or nil if there was none. -func (imp *importer) findPath(from, to string) []string { - imp.graphMu.Lock() - defer imp.graphMu.Unlock() - - seen := make(map[string]bool) - var search func(stack []string, importPath string) []string - search = func(stack []string, importPath string) []string { - if !seen[importPath] { - seen[importPath] = true - stack = append(stack, importPath) - if importPath == to { - return stack - } - for x := range imp.graph[importPath] { - if p := search(stack, x); p != nil { - return p - } - } - } - return nil - } - return search(make([]string, 0, 20), from) -} - -// startLoad initiates the loading, parsing and type-checking of the -// specified package and its dependencies, if it has not already begun. -// -// It returns an importInfo, not necessarily in a completed state. The -// caller must call awaitCompletion() before accessing its info field. -// -// startLoad is concurrency-safe and idempotent. -// -func (imp *importer) startLoad(bp *build.Package) *importInfo { - path := bp.ImportPath - imp.importedMu.Lock() - ii, ok := imp.imported[path] - if !ok { - ii = &importInfo{path: path, complete: make(chan struct{})} - imp.imported[path] = ii - go func() { - info := imp.load(bp) - ii.Complete(info) - }() - } - imp.importedMu.Unlock() - - return ii -} - -// load implements package loading by parsing Go source files -// located by go/build. -func (imp *importer) load(bp *build.Package) *PackageInfo { - info := imp.newPackageInfo(bp.ImportPath, bp.Dir) - info.Importable = true - files, errs := imp.conf.parsePackageFiles(bp, 'g') - for _, err := range errs { - info.appendError(err) - } - - imp.addFiles(info, files, true) - - imp.progMu.Lock() - imp.prog.importMap[bp.ImportPath] = info.Pkg - imp.progMu.Unlock() - - return info -} - -// addFiles adds and type-checks the specified files to info, loading -// their dependencies if needed. The order of files determines the -// package initialization order. It may be called multiple times on the -// same package. Errors are appended to the info.Errors field. -// -// cycleCheck determines whether the imports within files create -// dependency edges that should be checked for potential cycles. -// -func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) { - info.Files = append(info.Files, files...) - - // Ensure the dependencies are loaded, in parallel. - var fromPath string - if cycleCheck { - fromPath = info.Pkg.Path() - } - // TODO(adonovan): opt: make the caller do scanImports. - // Callers with a build.Package can skip it. - imp.importAll(fromPath, info.dir, scanImports(files), 0) - - if trace { - fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n", - time.Since(imp.start), info.Pkg.Path(), len(files)) - } - - // Ignore the returned (first) error since we - // already collect them all in the PackageInfo. - info.checker.Files(files) - - if trace { - fmt.Fprintf(os.Stderr, "%s: stop %q\n", - time.Since(imp.start), info.Pkg.Path()) - } -} - -func (imp *importer) newPackageInfo(path, dir string) *PackageInfo { - pkg := types.NewPackage(path, "") - info := &PackageInfo{ - Pkg: pkg, - Info: types.Info{ - Types: make(map[ast.Expr]types.TypeAndValue), - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Implicits: make(map[ast.Node]types.Object), - Scopes: make(map[ast.Node]*types.Scope), - Selections: make(map[*ast.SelectorExpr]*types.Selection), - }, - errorFunc: imp.conf.TypeChecker.Error, - dir: dir, - } - - // Copy the types.Config so we can vary it across PackageInfos. - tc := imp.conf.TypeChecker - tc.IgnoreFuncBodies = false - if f := imp.conf.TypeCheckFuncBodies; f != nil { - tc.IgnoreFuncBodies = !f(path) - } - tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) { - return imp.doImport(info, to) - } - tc.Error = info.appendError // appendError wraps the user's Error function - - info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) - imp.progMu.Lock() - imp.prog.AllPackages[pkg] = info - imp.progMu.Unlock() - return info -} diff --git a/go/loader/loader14_test.go b/go/loader/loader14_test.go deleted file mode 100644 index fab3170a0c..0000000000 --- a/go/loader/loader14_test.go +++ /dev/null @@ -1,676 +0,0 @@ -// 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. - -// +build !go1.5 - -// No testdata on Android. - -// +build !android - -package loader_test - -import ( - "fmt" - "go/build" - "path/filepath" - "reflect" - "sort" - "strings" - "sync" - "testing" - - "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/loader" -) - -// TestFromArgs checks that conf.FromArgs populates conf correctly. -// It does no I/O. -func TestFromArgs(t *testing.T) { - type result struct { - Err string - Rest []string - ImportPkgs map[string]bool - CreatePkgs []loader.PkgSpec - } - for _, test := range []struct { - args []string - tests bool - want result - }{ - // Mix of existing and non-existent packages. - { - args: []string{"nosuchpkg", "errors"}, - want: result{ - ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false}, - }, - }, - // Same, with -test flag. - { - args: []string{"nosuchpkg", "errors"}, - tests: true, - want: result{ - ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true}, - }, - }, - // Surplus arguments. - { - args: []string{"fmt", "errors", "--", "surplus"}, - want: result{ - Rest: []string{"surplus"}, - ImportPkgs: map[string]bool{"errors": false, "fmt": false}, - }, - }, - // Ad hoc package specified as *.go files. - { - args: []string{"foo.go", "bar.go"}, - want: result{CreatePkgs: []loader.PkgSpec{{ - Filenames: []string{"foo.go", "bar.go"}, - }}}, - }, - // Mixture of *.go and import paths. - { - args: []string{"foo.go", "fmt"}, - want: result{ - Err: "named files must be .go files: fmt", - }, - }, - } { - var conf loader.Config - rest, err := conf.FromArgs(test.args, test.tests) - got := result{ - Rest: rest, - ImportPkgs: conf.ImportPkgs, - CreatePkgs: conf.CreatePkgs, - } - if err != nil { - got.Err = err.Error() - } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want) - } - } -} - -func TestLoad_NoInitialPackages(t *testing.T) { - var conf loader.Config - - const wantErr = "no initial packages were loaded" - - prog, err := conf.Load() - if err == nil { - t.Errorf("Load succeeded unexpectedly, want %q", wantErr) - } else if err.Error() != wantErr { - t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) - } - if prog != nil { - t.Errorf("Load unexpectedly returned a Program") - } -} - -func TestLoad_MissingInitialPackage(t *testing.T) { - var conf loader.Config - conf.Import("nosuchpkg") - conf.Import("errors") - - const wantErr = "couldn't load packages due to errors: nosuchpkg" - - prog, err := conf.Load() - if err == nil { - t.Errorf("Load succeeded unexpectedly, want %q", wantErr) - } else if err.Error() != wantErr { - t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) - } - if prog != nil { - t.Errorf("Load unexpectedly returned a Program") - } -} - -func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) { - var conf loader.Config - conf.AllowErrors = true - conf.Import("nosuchpkg") - conf.ImportWithTests("errors") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed unexpectedly: %v", err) - } - if prog == nil { - t.Fatalf("Load returned a nil Program") - } - if got, want := created(prog), "errors_test"; got != want { - t.Errorf("Created = %s, want %s", got, want) - } - if got, want := imported(prog), "errors"; got != want { - t.Errorf("Imported = %s, want %s", got, want) - } -} - -func TestCreateUnnamedPackage(t *testing.T) { - var conf loader.Config - conf.CreateFromFilenames("") - prog, err := conf.Load() - if err != nil { - t.Fatalf("Load failed: %v", err) - } - if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { - t.Errorf("InitialPackages = %s, want %s", got, want) - } -} - -func TestLoad_MissingFileInCreatedPackage(t *testing.T) { - var conf loader.Config - conf.CreateFromFilenames("", "missing.go") - - const wantErr = "couldn't load packages due to errors: (unnamed)" - - prog, err := conf.Load() - if prog != nil { - t.Errorf("Load unexpectedly returned a Program") - } - if err == nil { - t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) - } - if err.Error() != wantErr { - t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) - } -} - -func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) { - conf := loader.Config{AllowErrors: true} - conf.CreateFromFilenames("", "missing.go") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed: %v", err) - } - if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { - t.Fatalf("InitialPackages = %s, want %s", got, want) - } -} - -func TestLoad_ParseError(t *testing.T) { - var conf loader.Config - conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") - - const wantErr = "couldn't load packages due to errors: badpkg" - - prog, err := conf.Load() - if prog != nil { - t.Errorf("Load unexpectedly returned a Program") - } - if err == nil { - t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) - } - if err.Error() != wantErr { - t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) - } -} - -func TestLoad_ParseError_AllowErrors(t *testing.T) { - var conf loader.Config - conf.AllowErrors = true - conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed unexpectedly: %v", err) - } - if prog == nil { - t.Fatalf("Load returned a nil Program") - } - if got, want := created(prog), "badpkg"; got != want { - t.Errorf("Created = %s, want %s", got, want) - } - - badpkg := prog.Created[0] - if len(badpkg.Files) != 1 { - t.Errorf("badpkg has %d files, want 1", len(badpkg.Files)) - } - wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'" - if !hasError(badpkg.Errors, wantErr) { - t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr) - } -} - -func TestLoad_FromSource_Success(t *testing.T) { - var conf loader.Config - conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed unexpectedly: %v", err) - } - if prog == nil { - t.Fatalf("Load returned a nil Program") - } - if got, want := created(prog), "P"; got != want { - t.Errorf("Created = %s, want %s", got, want) - } -} - -func TestLoad_FromImports_Success(t *testing.T) { - var conf loader.Config - conf.ImportWithTests("fmt") - conf.ImportWithTests("errors") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed unexpectedly: %v", err) - } - if prog == nil { - t.Fatalf("Load returned a nil Program") - } - if got, want := created(prog), "errors_test fmt_test"; got != want { - t.Errorf("Created = %q, want %s", got, want) - } - if got, want := imported(prog), "errors fmt"; got != want { - t.Errorf("Imported = %s, want %s", got, want) - } - // Check set of transitive packages. - // There are >30 and the set may grow over time, so only check a few. - want := map[string]bool{ - "strings": true, - "time": true, - "runtime": true, - "testing": true, - "unicode": true, - } - for _, path := range all(prog) { - delete(want, path) - } - if len(want) > 0 { - t.Errorf("AllPackages is missing these keys: %q", keys(want)) - } -} - -func TestLoad_MissingIndirectImport(t *testing.T) { - pkgs := map[string]string{ - "a": `package a; import _ "b"`, - "b": `package b; import _ "c"`, - } - conf := loader.Config{Build: fakeContext(pkgs)} - conf.Import("a") - - const wantErr = "couldn't load packages due to errors: b" - - prog, err := conf.Load() - if err == nil { - t.Errorf("Load succeeded unexpectedly, want %q", wantErr) - } else if err.Error() != wantErr { - t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) - } - if prog != nil { - t.Errorf("Load unexpectedly returned a Program") - } -} - -func TestLoad_BadDependency_AllowErrors(t *testing.T) { - for _, test := range []struct { - descr string - pkgs map[string]string - wantPkgs string - }{ - - { - descr: "missing dependency", - pkgs: map[string]string{ - "a": `package a; import _ "b"`, - "b": `package b; import _ "c"`, - }, - wantPkgs: "a b", - }, - { - descr: "bad package decl in dependency", - pkgs: map[string]string{ - "a": `package a; import _ "b"`, - "b": `package b; import _ "c"`, - "c": `package`, - }, - wantPkgs: "a b", - }, - { - descr: "parse error in dependency", - pkgs: map[string]string{ - "a": `package a; import _ "b"`, - "b": `package b; import _ "c"`, - "c": `package c; var x = `, - }, - wantPkgs: "a b c", - }, - } { - conf := loader.Config{ - AllowErrors: true, - Build: fakeContext(test.pkgs), - } - conf.Import("a") - - prog, err := conf.Load() - if err != nil { - t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err) - } - if prog == nil { - t.Fatalf("%s: Load returned a nil Program", test.descr) - } - - if got, want := imported(prog), "a"; got != want { - t.Errorf("%s: Imported = %s, want %s", test.descr, got, want) - } - if got := all(prog); strings.Join(got, " ") != test.wantPkgs { - t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs) - } - } -} - -func TestCwd(t *testing.T) { - ctxt := fakeContext(map[string]string{"one/two/three": `package three`}) - for _, test := range []struct { - cwd, arg, want string - }{ - {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"}, - {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"}, - {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"}, - {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"}, - {cwd: "/go/src/one", arg: "two/three", want: ""}, - } { - conf := loader.Config{ - Cwd: test.cwd, - Build: ctxt, - } - conf.Import(test.arg) - - var got string - prog, err := conf.Load() - if prog != nil { - got = imported(prog) - } - if got != test.want { - t.Errorf("Load(%s) from %s: Imported = %s, want %s", - test.arg, test.cwd, got, test.want) - if err != nil { - t.Errorf("Load failed: %v", err) - } - } - } -} - -// TODO(adonovan): more Load tests: -// -// failures: -// - to parse package decl of *_test.go files -// - to parse package decl of external *_test.go files -// - to parse whole of *_test.go files -// - to parse whole of external *_test.go files -// - to open a *.go file during import scanning -// - to import from binary - -// features: -// - InitialPackages -// - PackageCreated hook -// - TypeCheckFuncBodies hook - -func TestTransitivelyErrorFreeFlag(t *testing.T) { - // Create an minimal custom build.Context - // that fakes the following packages: - // - // a --> b --> c! c has an error - // \ d and e are transitively error-free. - // e --> d - // - // Each package [a-e] consists of one file, x.go. - pkgs := map[string]string{ - "a": `package a; import (_ "b"; _ "e")`, - "b": `package b; import _ "c"`, - "c": `package c; func f() { _ = int(false) }`, // type error within function body - "d": `package d;`, - "e": `package e; import _ "d"`, - } - conf := loader.Config{ - AllowErrors: true, - Build: fakeContext(pkgs), - } - conf.Import("a") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed: %s", err) - } - if prog == nil { - t.Fatalf("Load returned nil *Program") - } - - for pkg, info := range prog.AllPackages { - var wantErr, wantTEF bool - switch pkg.Path() { - case "a", "b": - case "c": - wantErr = true - case "d", "e": - wantTEF = true - default: - t.Errorf("unexpected package: %q", pkg.Path()) - continue - } - - if (info.Errors != nil) != wantErr { - if wantErr { - t.Errorf("Package %q.Error = nil, want error", pkg.Path()) - } else { - t.Errorf("Package %q has unexpected Errors: %v", - pkg.Path(), info.Errors) - } - } - - if info.TransitivelyErrorFree != wantTEF { - t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", - pkg.Path(), info.TransitivelyErrorFree, wantTEF) - } - } -} - -// Test that syntax (scan/parse), type, and loader errors are recorded -// (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). -func TestErrorReporting(t *testing.T) { - pkgs := map[string]string{ - "a": `package a; import (_ "b"; _ "c"); var x int = false`, - "b": `package b; 'syntax error!`, - } - conf := loader.Config{ - AllowErrors: true, - Build: fakeContext(pkgs), - } - var mu sync.Mutex - var allErrors []error - conf.TypeChecker.Error = func(err error) { - mu.Lock() - allErrors = append(allErrors, err) - mu.Unlock() - } - conf.Import("a") - - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed: %s", err) - } - if prog == nil { - t.Fatalf("Load returned nil *Program") - } - - // TODO(adonovan): test keys of ImportMap. - - // Check errors recorded in each PackageInfo. - for pkg, info := range prog.AllPackages { - switch pkg.Path() { - case "a": - if !hasError(info.Errors, "cannot convert false") { - t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) - } - if !hasError(info.Errors, "could not import c") { - t.Errorf("a.Errors = %v, want import (loader) error", info.Errors) - } - case "b": - if !hasError(info.Errors, "rune literal not terminated") { - t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) - } - } - } - - // Check errors reported via error handler. - if !hasError(allErrors, "cannot convert false") || - !hasError(allErrors, "rune literal not terminated") || - !hasError(allErrors, "could not import c") { - t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors) - } -} - -func TestCycles(t *testing.T) { - for _, test := range []struct { - descr string - ctxt *build.Context - wantErr string - }{ - { - "self-cycle", - fakeContext(map[string]string{ - "main": `package main; import _ "selfcycle"`, - "selfcycle": `package selfcycle; import _ "selfcycle"`, - }), - `import cycle: selfcycle -> selfcycle`, - }, - { - "three-package cycle", - fakeContext(map[string]string{ - "main": `package main; import _ "a"`, - "a": `package a; import _ "b"`, - "b": `package b; import _ "c"`, - "c": `package c; import _ "a"`, - }), - `import cycle: c -> a -> b -> c`, - }, - { - "self-cycle in dependency of test file", - buildutil.FakeContext(map[string]map[string]string{ - "main": { - "main.go": `package main`, - "main_test.go": `package main; import _ "a"`, - }, - "a": { - "a.go": `package a; import _ "a"`, - }, - }), - `import cycle: a -> a`, - }, - // TODO(adonovan): fix: these fail - // { - // "two-package cycle in dependency of test file", - // buildutil.FakeContext(map[string]map[string]string{ - // "main": { - // "main.go": `package main`, - // "main_test.go": `package main; import _ "a"`, - // }, - // "a": { - // "a.go": `package a; import _ "main"`, - // }, - // }), - // `import cycle: main -> a -> main`, - // }, - // { - // "self-cycle in augmented package", - // buildutil.FakeContext(map[string]map[string]string{ - // "main": { - // "main.go": `package main`, - // "main_test.go": `package main; import _ "main"`, - // }, - // }), - // `import cycle: main -> main`, - // }, - } { - conf := loader.Config{ - AllowErrors: true, - Build: test.ctxt, - } - var mu sync.Mutex - var allErrors []error - conf.TypeChecker.Error = func(err error) { - mu.Lock() - allErrors = append(allErrors, err) - mu.Unlock() - } - conf.ImportWithTests("main") - - prog, err := conf.Load() - if err != nil { - t.Errorf("%s: Load failed: %s", test.descr, err) - } - if prog == nil { - t.Fatalf("%s: Load returned nil *Program", test.descr) - } - - if !hasError(allErrors, test.wantErr) { - t.Errorf("%s: Load() errors = %q, want %q", - test.descr, allErrors, test.wantErr) - } - } - - // TODO(adonovan): - // - Test that in a legal test cycle, none of the symbols - // defined by augmentation are visible via import. -} - -// ---- utilities ---- - -// Simplifying wrapper around buildutil.FakeContext for single-file packages. -func fakeContext(pkgs map[string]string) *build.Context { - pkgs2 := make(map[string]map[string]string) - for path, content := range pkgs { - pkgs2[path] = map[string]string{"x.go": content} - } - return buildutil.FakeContext(pkgs2) -} - -func hasError(errors []error, substr string) bool { - for _, err := range errors { - if strings.Contains(err.Error(), substr) { - return true - } - } - return false -} - -func keys(m map[string]bool) (keys []string) { - for key := range m { - keys = append(keys, key) - } - sort.Strings(keys) - return -} - -// Returns all loaded packages. -func all(prog *loader.Program) []string { - var pkgs []string - for _, info := range prog.AllPackages { - pkgs = append(pkgs, info.Pkg.Path()) - } - sort.Strings(pkgs) - return pkgs -} - -// Returns initially imported packages, as a string. -func imported(prog *loader.Program) string { - var pkgs []string - for _, info := range prog.Imported { - pkgs = append(pkgs, info.Pkg.Path()) - } - sort.Strings(pkgs) - return strings.Join(pkgs, " ") -} - -// Returns initially created packages, as a string. -func created(prog *loader.Program) string { - var pkgs []string - for _, info := range prog.Created { - pkgs = append(pkgs, info.Pkg.Path()) - } - return strings.Join(pkgs, " ") -} diff --git a/go/loader/stdlib14_test.go b/go/loader/stdlib14_test.go deleted file mode 100644 index c3a29874bc..0000000000 --- a/go/loader/stdlib14_test.go +++ /dev/null @@ -1,197 +0,0 @@ -// 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. - -// +build !go1.5 - -package loader_test - -// This file enumerates all packages beneath $GOROOT, loads them, plus -// their external tests if any, runs the type checker on them, and -// prints some summary information. - -import ( - "bytes" - "fmt" - "go/ast" - "go/build" - "go/token" - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" -) - -func TestStdlib(t *testing.T) { - if runtime.GOOS == "android" { - t.Skipf("incomplete std lib on %s", runtime.GOOS) - } - - runtime.GC() - t0 := time.Now() - var memstats runtime.MemStats - runtime.ReadMemStats(&memstats) - alloc := memstats.Alloc - - // Load, parse and type-check the program. - ctxt := build.Default // copy - ctxt.GOPATH = "" // disable GOPATH - conf := loader.Config{Build: &ctxt} - for _, path := range buildutil.AllPackages(conf.Build) { - conf.ImportWithTests(path) - } - - prog, err := conf.Load() - if err != nil { - t.Fatalf("Load failed: %v", err) - } - - t1 := time.Now() - runtime.GC() - runtime.ReadMemStats(&memstats) - - numPkgs := len(prog.AllPackages) - if want := 205; numPkgs < want { - t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) - } - - // Dump package members. - if false { - for pkg := range prog.AllPackages { - fmt.Printf("Package %s:\n", pkg.Path()) - scope := pkg.Scope() - qualifier := types.RelativeTo(pkg) - for _, name := range scope.Names() { - if ast.IsExported(name) { - fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier)) - } - } - fmt.Println() - } - } - - // Check that Test functions for io/ioutil, regexp and - // compress/bzip2 are all simultaneously present. - // (The apparent cycle formed when augmenting all three of - // these packages by their tests was the original motivation - // for reporting b/7114.) - // - // compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil - // io/ioutil.TestTempFile in tempfile_test.go imports regexp - // regexp.TestRE2Search in exec_test.go imports compress/bzip2 - for _, test := range []struct{ pkg, fn string }{ - {"io/ioutil", "TestTempFile"}, - {"regexp", "TestRE2Search"}, - {"compress/bzip2", "TestBitReader"}, - } { - info := prog.Imported[test.pkg] - if info == nil { - t.Errorf("failed to load package %q", test.pkg) - continue - } - obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func) - if obj == nil { - t.Errorf("package %q has no func %q", test.pkg, test.fn) - continue - } - } - - // Dump some statistics. - - // determine line count - var lineCount int - prog.Fset.Iterate(func(f *token.File) bool { - lineCount += f.LineCount() - return true - }) - - t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) - t.Log("#Source lines: ", lineCount) - t.Log("Load/parse/typecheck: ", t1.Sub(t0)) - t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000) -} - -func TestCgoOption(t *testing.T) { - switch runtime.GOOS { - // On these systems, the net and os/user packages don't use cgo - // or the std library is incomplete (Android). - case "android", "plan9", "solaris", "windows": - t.Skipf("no cgo or incomplete std lib on %s", runtime.GOOS) - } - // In nocgo builds (e.g. linux-amd64-nocgo), - // there is no "runtime/cgo" package, - // so cgo-generated Go files will have a failing import. - if !build.Default.CgoEnabled { - return - } - // Test that we can load cgo-using packages with - // CGO_ENABLED=[01], which causes go/build to select pure - // Go/native implementations, respectively, based on build - // tags. - // - // Each entry specifies a package-level object and the generic - // file expected to define it when cgo is disabled. - // When cgo is enabled, the exact file is not specified (since - // it varies by platform), but must differ from the generic one. - // - // The test also loads the actual file to verify that the - // object is indeed defined at that location. - for _, test := range []struct { - pkg, name, genericFile string - }{ - {"net", "cgoLookupHost", "cgo_stub.go"}, - {"os/user", "lookupId", "lookup_stubs.go"}, - } { - ctxt := build.Default - for _, ctxt.CgoEnabled = range []bool{false, true} { - conf := loader.Config{Build: &ctxt} - conf.Import(test.pkg) - prog, err := conf.Load() - if err != nil { - t.Errorf("Load failed: %v", err) - continue - } - info := prog.Imported[test.pkg] - if info == nil { - t.Errorf("package %s not found", test.pkg) - continue - } - obj := info.Pkg.Scope().Lookup(test.name) - if obj == nil { - t.Errorf("no object %s.%s", test.pkg, test.name) - continue - } - posn := prog.Fset.Position(obj.Pos()) - t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled) - - gotFile := filepath.Base(posn.Filename) - filesMatch := gotFile == test.genericFile - - if ctxt.CgoEnabled && filesMatch { - t.Errorf("CGO_ENABLED=1: %s found in %s, want native file", - obj, gotFile) - } else if !ctxt.CgoEnabled && !filesMatch { - t.Errorf("CGO_ENABLED=0: %s found in %s, want %s", - obj, gotFile, test.genericFile) - } - - // Load the file and check the object is declared at the right place. - b, err := ioutil.ReadFile(posn.Filename) - if err != nil { - t.Errorf("can't read %s: %s", posn.Filename, err) - continue - } - line := string(bytes.Split(b, []byte("\n"))[posn.Line-1]) - ident := line[posn.Column-1:] - if !strings.HasPrefix(ident, test.name) { - t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident) - } - } - } -} diff --git a/go/pointer/analysis14.go b/go/pointer/analysis14.go deleted file mode 100644 index 70761feba6..0000000000 --- a/go/pointer/analysis14.go +++ /dev/null @@ -1,449 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This file defines the main datatypes and Analyze function of the pointer analysis. - -import ( - "fmt" - "go/token" - "io" - "os" - "reflect" - "runtime" - "runtime/debug" - "sort" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -const ( - // optimization options; enable all when committing - optRenumber = true // enable renumbering optimization (makes logs hard to read) - optHVN = true // enable pointer equivalence via Hash-Value Numbering - - // debugging options; disable all when committing - debugHVN = false // enable assertions in HVN - debugHVNVerbose = false // enable extra HVN logging - debugHVNCrossCheck = false // run solver with/without HVN and compare (caveats below) - debugTimers = false // show running time of each phase -) - -// object.flags bitmask values. -const ( - otTagged = 1 << iota // type-tagged object - otIndirect // type-tagged object with indirect payload - otFunction // function object -) - -// An object represents a contiguous block of memory to which some -// (generalized) pointer may point. -// -// (Note: most variables called 'obj' are not *objects but nodeids -// such that a.nodes[obj].obj != nil.) -// -type object struct { - // flags is a bitset of the node type (ot*) flags defined above. - flags uint32 - - // Number of following nodes belonging to the same "object" - // allocation. Zero for all other nodes. - size uint32 - - // data describes this object; it has one of these types: - // - // ssa.Value for an object allocated by an SSA operation. - // types.Type for an rtype instance object or *rtype-tagged object. - // string for an instrinsic object, e.g. the array behind os.Args. - // nil for an object allocated by an instrinsic. - // (cgn provides the identity of the intrinsic.) - data interface{} - - // The call-graph node (=context) in which this object was allocated. - // May be nil for global objects: Global, Const, some Functions. - cgn *cgnode -} - -// nodeid denotes a node. -// It is an index within analysis.nodes. -// We use small integers, not *node pointers, for many reasons: -// - they are smaller on 64-bit systems. -// - sets of them can be represented compactly in bitvectors or BDDs. -// - order matters; a field offset can be computed by simple addition. -type nodeid uint32 - -// A node is an equivalence class of memory locations. -// Nodes may be pointers, pointed-to locations, neither, or both. -// -// Nodes that are pointed-to locations ("labels") have an enclosing -// object (see analysis.enclosingObject). -// -type node struct { - // If non-nil, this node is the start of an object - // (addressable memory location). - // The following obj.size nodes implicitly belong to the object; - // they locate their object by scanning back. - obj *object - - // The type of the field denoted by this node. Non-aggregate, - // unless this is an tagged.T node (i.e. the thing - // pointed to by an interface) in which case typ is that type. - typ types.Type - - // subelement indicates which directly embedded subelement of - // an object of aggregate type (struct, tuple, array) this is. - subelement *fieldInfo // e.g. ".a.b[*].c" - - // Solver state for the canonical node of this pointer- - // equivalence class. Each node is created with its own state - // but they become shared after HVN. - solve *solverState -} - -// An analysis instance holds the state of a single pointer analysis problem. -type analysis struct { - config *Config // the client's control/observer interface - prog *ssa.Program // the program being analyzed - log io.Writer // log stream; nil to disable - panicNode nodeid // sink for panic, source for recover - nodes []*node // indexed by nodeid - flattenMemo map[types.Type][]*fieldInfo // memoization of flatten() - trackTypes map[types.Type]bool // memoization of shouldTrack() - constraints []constraint // set of constraints - cgnodes []*cgnode // all cgnodes - genq []*cgnode // queue of functions to generate constraints for - intrinsics map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns - globalval map[ssa.Value]nodeid // node for each global ssa.Value - globalobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton - localval map[ssa.Value]nodeid // node for each local ssa.Value - localobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton - atFuncs map[*ssa.Function]bool // address-taken functions (for presolver) - mapValues []nodeid // values of makemap objects (indirect in HVN) - work nodeset // solver's worklist - result *Result // results of the analysis - track track // pointerlike types whose aliasing we track - deltaSpace []int // working space for iterating over PTS deltas - - // Reflection & intrinsics: - hasher typeutil.Hasher // cache of type hashes - reflectValueObj types.Object // type symbol for reflect.Value (if present) - reflectValueCall *ssa.Function // (reflect.Value).Call - reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present) - reflectRtypePtr *types.Pointer // *reflect.rtype - reflectType *types.Named // reflect.Type - rtypes typeutil.Map // nodeid of canonical *rtype-tagged object for type T - reflectZeros typeutil.Map // nodeid of canonical T-tagged object for zero value - runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer -} - -// enclosingObj returns the first node of the addressable memory -// object that encloses node id. Panic ensues if that node does not -// belong to any object. -func (a *analysis) enclosingObj(id nodeid) nodeid { - // Find previous node with obj != nil. - for i := id; i >= 0; i-- { - n := a.nodes[i] - if obj := n.obj; obj != nil { - if i+nodeid(obj.size) <= id { - break // out of bounds - } - return i - } - } - panic("node has no enclosing object") -} - -// labelFor returns the Label for node id. -// Panic ensues if that node is not addressable. -func (a *analysis) labelFor(id nodeid) *Label { - return &Label{ - obj: a.nodes[a.enclosingObj(id)].obj, - subelement: a.nodes[id].subelement, - } -} - -func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if a.log != nil { - fmt.Fprintf(a.log, "%s: warning: %s\n", a.prog.Fset.Position(pos), msg) - } - a.result.Warnings = append(a.result.Warnings, Warning{pos, msg}) -} - -// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries. -func (a *analysis) computeTrackBits() { - var queryTypes []types.Type - for v := range a.config.Queries { - queryTypes = append(queryTypes, v.Type()) - } - for v := range a.config.IndirectQueries { - queryTypes = append(queryTypes, mustDeref(v.Type())) - } - for _, t := range queryTypes { - switch t.Underlying().(type) { - case *types.Chan: - a.track |= trackChan - case *types.Map: - a.track |= trackMap - case *types.Pointer: - a.track |= trackPtr - case *types.Slice: - a.track |= trackSlice - case *types.Interface: - a.track = trackAll - return - } - if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) { - a.track = trackAll - return - } - } -} - -// Analyze runs the pointer analysis with the scope and options -// specified by config, and returns the (synthetic) root of the callgraph. -// -// Pointer analysis of a transitively closed well-typed program should -// always succeed. An error can occur only due to an internal bug. -// -func Analyze(config *Config) (result *Result, err error) { - if config.Mains == nil { - return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)") - } - defer func() { - if p := recover(); p != nil { - err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p) - fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:") - debug.PrintStack() - } - }() - - a := &analysis{ - config: config, - log: config.Log, - prog: config.prog(), - globalval: make(map[ssa.Value]nodeid), - globalobj: make(map[ssa.Value]nodeid), - flattenMemo: make(map[types.Type][]*fieldInfo), - trackTypes: make(map[types.Type]bool), - atFuncs: make(map[*ssa.Function]bool), - hasher: typeutil.MakeHasher(), - intrinsics: make(map[*ssa.Function]intrinsic), - result: &Result{ - Queries: make(map[ssa.Value]Pointer), - IndirectQueries: make(map[ssa.Value]Pointer), - }, - deltaSpace: make([]int, 0, 100), - } - - if false { - a.log = os.Stderr // for debugging crashes; extremely verbose - } - - if a.log != nil { - fmt.Fprintln(a.log, "==== Starting analysis") - } - - // Pointer analysis requires a complete program for soundness. - // Check to prevent accidental misconfiguration. - for _, pkg := range a.prog.AllPackages() { - // (This only checks that the package scope is complete, - // not that func bodies exist, but it's a good signal.) - if !pkg.Pkg.Complete() { - return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path()) - } - } - - if reflect := a.prog.ImportedPackage("reflect"); reflect != nil { - rV := reflect.Pkg.Scope().Lookup("Value") - a.reflectValueObj = rV - a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call") - a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named) - a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype") - a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type()) - - // Override flattening of reflect.Value, treating it like a basic type. - tReflectValue := a.reflectValueObj.Type() - a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}} - - // Override shouldTrack of reflect.Value and *reflect.rtype. - // Always track pointers of these types. - a.trackTypes[tReflectValue] = true - a.trackTypes[a.reflectRtypePtr] = true - - a.rtypes.SetHasher(a.hasher) - a.reflectZeros.SetHasher(a.hasher) - } - if runtime := a.prog.ImportedPackage("runtime"); runtime != nil { - a.runtimeSetFinalizer = runtime.Func("SetFinalizer") - } - a.computeTrackBits() - - a.generate() - a.showCounts() - - if optRenumber { - a.renumber() - } - - N := len(a.nodes) // excludes solver-created nodes - - if optHVN { - if debugHVNCrossCheck { - // Cross-check: run the solver once without - // optimization, once with, and compare the - // solutions. - savedConstraints := a.constraints - - a.solve() - a.dumpSolution("A.pts", N) - - // Restore. - a.constraints = savedConstraints - for _, n := range a.nodes { - n.solve = new(solverState) - } - a.nodes = a.nodes[:N] - - // rtypes is effectively part of the solver state. - a.rtypes = typeutil.Map{} - a.rtypes.SetHasher(a.hasher) - } - - a.hvn() - } - - if debugHVNCrossCheck { - runtime.GC() - runtime.GC() - } - - a.solve() - - // Compare solutions. - if optHVN && debugHVNCrossCheck { - a.dumpSolution("B.pts", N) - - if !diff("A.pts", "B.pts") { - return nil, fmt.Errorf("internal error: optimization changed solution") - } - } - - // Create callgraph.Nodes in deterministic order. - if cg := a.result.CallGraph; cg != nil { - for _, caller := range a.cgnodes { - cg.CreateNode(caller.fn) - } - } - - // Add dynamic edges to call graph. - var space [100]int - for _, caller := range a.cgnodes { - for _, site := range caller.sites { - for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) { - a.callEdge(caller, site, nodeid(callee)) - } - } - } - - return a.result, nil -} - -// callEdge is called for each edge in the callgraph. -// calleeid is the callee's object node (has otFunction flag). -// -func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) { - obj := a.nodes[calleeid].obj - if obj.flags&otFunction == 0 { - panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid)) - } - callee := obj.cgn - - if cg := a.result.CallGraph; cg != nil { - // TODO(adonovan): opt: I would expect duplicate edges - // (to wrappers) to arise due to the elimination of - // context information, but I haven't observed any. - // Understand this better. - callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn)) - } - - if a.log != nil { - fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee) - } - - // Warn about calls to non-intrinsic external functions. - // TODO(adonovan): de-dup these messages. - if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil { - a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn) - a.warnf(fn.Pos(), " (declared here)") - } -} - -// dumpSolution writes the PTS solution to the specified file. -// -// It only dumps the nodes that existed before solving. The order in -// which solver-created nodes are created depends on pre-solver -// optimization, so we can't include them in the cross-check. -// -func (a *analysis) dumpSolution(filename string, N int) { - f, err := os.Create(filename) - if err != nil { - panic(err) - } - for id, n := range a.nodes[:N] { - if _, err := fmt.Fprintf(f, "pts(n%d) = {", id); err != nil { - panic(err) - } - var sep string - for _, l := range n.solve.pts.AppendTo(a.deltaSpace) { - if l >= N { - break - } - fmt.Fprintf(f, "%s%d", sep, l) - sep = " " - } - fmt.Fprintf(f, "} : %s\n", n.typ) - } - if err := f.Close(); err != nil { - panic(err) - } -} - -// showCounts logs the size of the constraint system. A typical -// optimized distribution is 65% copy, 13% load, 11% addr, 5% -// offsetAddr, 4% store, 2% others. -// -func (a *analysis) showCounts() { - if a.log != nil { - counts := make(map[reflect.Type]int) - for _, c := range a.constraints { - counts[reflect.TypeOf(c)]++ - } - fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints)) - var lines []string - for t, n := range counts { - line := fmt.Sprintf("%7d (%2d%%)\t%s", n, 100*n/len(a.constraints), t) - lines = append(lines, line) - } - sort.Sort(sort.Reverse(sort.StringSlice(lines))) - for _, line := range lines { - fmt.Fprintf(a.log, "\t%s\n", line) - } - - fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes)) - - // Show number of pointer equivalence classes. - m := make(map[*solverState]bool) - for _, n := range a.nodes { - m[n.solve] = true - } - fmt.Fprintf(a.log, "# ptsets:\t%d\n", len(m)) - } -} diff --git a/go/pointer/api14.go b/go/pointer/api14.go deleted file mode 100644 index 548173282d..0000000000 --- a/go/pointer/api14.go +++ /dev/null @@ -1,247 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -import ( - "bytes" - "fmt" - "go/token" - "io" - - "golang.org/x/tools/container/intsets" - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types/typeutil" -) - -// A Config formulates a pointer analysis problem for Analyze(). -type Config struct { - // Mains contains the set of 'main' packages to analyze - // Clients must provide the analysis with at least one - // package defining a main() function. - // - // Non-main packages in the ssa.Program that are not - // dependencies of any main package may still affect the - // analysis result, because they contribute runtime types and - // thus methods. - // TODO(adonovan): investigate whether this is desirable. - Mains []*ssa.Package - - // Reflection determines whether to handle reflection - // operators soundly, which is currently rather slow since it - // causes constraint to be generated during solving - // proportional to the number of constraint variables, which - // has not yet been reduced by presolver optimisation. - Reflection bool - - // BuildCallGraph determines whether to construct a callgraph. - // If enabled, the graph will be available in Result.CallGraph. - BuildCallGraph bool - - // The client populates Queries[v] or IndirectQueries[v] - // for each ssa.Value v of interest, to request that the - // points-to sets pts(v) or pts(*v) be computed. If the - // client needs both points-to sets, v may appear in both - // maps. - // - // (IndirectQueries is typically used for Values corresponding - // to source-level lvalues, e.g. an *ssa.Global.) - // - // The analysis populates the corresponding - // Result.{Indirect,}Queries map when it creates the pointer - // variable for v or *v. Upon completion the client can - // inspect that map for the results. - // - // TODO(adonovan): this API doesn't scale well for batch tools - // that want to dump the entire solution. Perhaps optionally - // populate a map[*ssa.DebugRef]Pointer in the Result, one - // entry per source expression. - // - Queries map[ssa.Value]struct{} - IndirectQueries map[ssa.Value]struct{} - - // If Log is non-nil, log messages are written to it. - // Logging is extremely verbose. - Log io.Writer -} - -type track uint32 - -const ( - trackChan track = 1 << iota // track 'chan' references - trackMap // track 'map' references - trackPtr // track regular pointers - trackSlice // track slice references - - trackAll = ^track(0) -) - -// AddQuery adds v to Config.Queries. -// Precondition: CanPoint(v.Type()). -// TODO(adonovan): consider returning a new Pointer for this query, -// which will be initialized during analysis. That avoids the needs -// for the corresponding ssa.Value-keyed maps in Config and Result. -func (c *Config) AddQuery(v ssa.Value) { - if !CanPoint(v.Type()) { - panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type())) - } - if c.Queries == nil { - c.Queries = make(map[ssa.Value]struct{}) - } - c.Queries[v] = struct{}{} -} - -// AddQuery adds v to Config.IndirectQueries. -// Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()). -func (c *Config) AddIndirectQuery(v ssa.Value) { - if c.IndirectQueries == nil { - c.IndirectQueries = make(map[ssa.Value]struct{}) - } - if !CanPoint(mustDeref(v.Type())) { - panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type())) - } - c.IndirectQueries[v] = struct{}{} -} - -func (c *Config) prog() *ssa.Program { - for _, main := range c.Mains { - return main.Prog - } - panic("empty scope") -} - -type Warning struct { - Pos token.Pos - Message string -} - -// A Result contains the results of a pointer analysis. -// -// See Config for how to request the various Result components. -// -type Result struct { - CallGraph *callgraph.Graph // discovered call graph - Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries. - IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries. - Warnings []Warning // warnings of unsoundness -} - -// A Pointer is an equivalence class of pointer-like values. -// -// A Pointer doesn't have a unique type because pointers of distinct -// types may alias the same object. -// -type Pointer struct { - a *analysis - n nodeid -} - -// A PointsToSet is a set of labels (locations or allocations). -type PointsToSet struct { - a *analysis // may be nil if pts is nil - pts *nodeset -} - -func (s PointsToSet) String() string { - var buf bytes.Buffer - buf.WriteByte('[') - if s.pts != nil { - var space [50]int - for i, l := range s.pts.AppendTo(space[:0]) { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(s.a.labelFor(nodeid(l)).String()) - } - } - buf.WriteByte(']') - return buf.String() -} - -// PointsTo returns the set of labels that this points-to set -// contains. -func (s PointsToSet) Labels() []*Label { - var labels []*Label - if s.pts != nil { - var space [50]int - for _, l := range s.pts.AppendTo(space[:0]) { - labels = append(labels, s.a.labelFor(nodeid(l))) - } - } - return labels -} - -// If this PointsToSet came from a Pointer of interface kind -// or a reflect.Value, DynamicTypes returns the set of dynamic -// types that it may contain. (For an interface, they will -// always be concrete types.) -// -// The result is a mapping whose keys are the dynamic types to which -// it may point. For each pointer-like key type, the corresponding -// map value is the PointsToSet for pointers of that type. -// -// The result is empty unless CanHaveDynamicTypes(T). -// -func (s PointsToSet) DynamicTypes() *typeutil.Map { - var tmap typeutil.Map - tmap.SetHasher(s.a.hasher) - if s.pts != nil { - var space [50]int - for _, x := range s.pts.AppendTo(space[:0]) { - ifaceObjId := nodeid(x) - if !s.a.isTaggedObject(ifaceObjId) { - continue // !CanHaveDynamicTypes(tDyn) - } - tDyn, v, indirect := s.a.taggedValue(ifaceObjId) - if indirect { - panic("indirect tagged object") // implement later - } - pts, ok := tmap.At(tDyn).(PointsToSet) - if !ok { - pts = PointsToSet{s.a, new(nodeset)} - tmap.Set(tDyn, pts) - } - pts.pts.addAll(&s.a.nodes[v].solve.pts) - } - } - return &tmap -} - -// Intersects reports whether this points-to set and the -// argument points-to set contain common members. -func (x PointsToSet) Intersects(y PointsToSet) bool { - if x.pts == nil || y.pts == nil { - return false - } - // This takes Θ(|x|+|y|) time. - var z intsets.Sparse - z.Intersection(&x.pts.Sparse, &y.pts.Sparse) - return !z.IsEmpty() -} - -func (p Pointer) String() string { - return fmt.Sprintf("n%d", p.n) -} - -// PointsTo returns the points-to set of this pointer. -func (p Pointer) PointsTo() PointsToSet { - if p.n == 0 { - return PointsToSet{} - } - return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts} -} - -// MayAlias reports whether the receiver pointer may alias -// the argument pointer. -func (p Pointer) MayAlias(q Pointer) bool { - return p.PointsTo().Intersects(q.PointsTo()) -} - -// DynamicTypes returns p.PointsTo().DynamicTypes(). -func (p Pointer) DynamicTypes() *typeutil.Map { - return p.PointsTo().DynamicTypes() -} diff --git a/go/pointer/constraint14.go b/go/pointer/constraint14.go deleted file mode 100644 index d18064ccbe..0000000000 --- a/go/pointer/constraint14.go +++ /dev/null @@ -1,153 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -import ( - "golang.org/x/tools/go/types" -) - -type constraint interface { - // For a complex constraint, returns the nodeid of the pointer - // to which it is attached. For addr and copy, returns dst. - ptr() nodeid - - // renumber replaces each nodeid n in the constraint by mapping[n]. - renumber(mapping []nodeid) - - // presolve is a hook for constraint-specific behaviour during - // pre-solver optimization. Typical implementations mark as - // indirect the set of nodes to which the solver will add copy - // edges or PTS labels. - presolve(h *hvn) - - // solve is called for complex constraints when the pts for - // the node to which they are attached has changed. - solve(a *analysis, delta *nodeset) - - String() string -} - -// dst = &src -// pts(dst) ⊇ {src} -// A base constraint used to initialize the solver's pt sets -type addrConstraint struct { - dst nodeid // (ptr) - src nodeid -} - -func (c *addrConstraint) ptr() nodeid { return c.dst } -func (c *addrConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst = src -// A simple constraint represented directly as a copyTo graph edge. -type copyConstraint struct { - dst nodeid // (ptr) - src nodeid -} - -func (c *copyConstraint) ptr() nodeid { return c.dst } -func (c *copyConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst = src[offset] -// A complex constraint attached to src (the pointer) -type loadConstraint struct { - offset uint32 - dst nodeid - src nodeid // (ptr) -} - -func (c *loadConstraint) ptr() nodeid { return c.src } -func (c *loadConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst[offset] = src -// A complex constraint attached to dst (the pointer) -type storeConstraint struct { - offset uint32 - dst nodeid // (ptr) - src nodeid -} - -func (c *storeConstraint) ptr() nodeid { return c.dst } -func (c *storeConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst = &src.f or dst = &src[0] -// A complex constraint attached to dst (the pointer) -type offsetAddrConstraint struct { - offset uint32 - dst nodeid - src nodeid // (ptr) -} - -func (c *offsetAddrConstraint) ptr() nodeid { return c.src } -func (c *offsetAddrConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst = src.(typ) where typ is an interface -// A complex constraint attached to src (the interface). -// No representation change: pts(dst) and pts(src) contains tagged objects. -type typeFilterConstraint struct { - typ types.Type // an interface type - dst nodeid - src nodeid // (ptr) -} - -func (c *typeFilterConstraint) ptr() nodeid { return c.src } -func (c *typeFilterConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// dst = src.(typ) where typ is a concrete type -// A complex constraint attached to src (the interface). -// -// If exact, only tagged objects identical to typ are untagged. -// If !exact, tagged objects assignable to typ are untagged too. -// The latter is needed for various reflect operators, e.g. Send. -// -// This entails a representation change: -// pts(src) contains tagged objects, -// pts(dst) contains their payloads. -type untagConstraint struct { - typ types.Type // a concrete type - dst nodeid - src nodeid // (ptr) - exact bool -} - -func (c *untagConstraint) ptr() nodeid { return c.src } -func (c *untagConstraint) renumber(mapping []nodeid) { - c.dst = mapping[c.dst] - c.src = mapping[c.src] -} - -// src.method(params...) -// A complex constraint attached to iface. -type invokeConstraint struct { - method *types.Func // the abstract method - iface nodeid // (ptr) the interface - params nodeid // the start of the identity/params/results block -} - -func (c *invokeConstraint) ptr() nodeid { return c.iface } -func (c *invokeConstraint) renumber(mapping []nodeid) { - c.iface = mapping[c.iface] - c.params = mapping[c.params] -} diff --git a/go/pointer/doc14.go b/go/pointer/doc14.go deleted file mode 100644 index fe5ad6058d..0000000000 --- a/go/pointer/doc14.go +++ /dev/null @@ -1,612 +0,0 @@ -// 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. - -// +build !go1.5 - -/* - -Package pointer implements Andersen's analysis, an inclusion-based -pointer analysis algorithm first described in (Andersen, 1994). - -A pointer analysis relates every pointer expression in a whole program -to the set of memory locations to which it might point. This -information can be used to construct a call graph of the program that -precisely represents the destinations of dynamic function and method -calls. It can also be used to determine, for example, which pairs of -channel operations operate on the same channel. - -The package allows the client to request a set of expressions of -interest for which the points-to information will be returned once the -analysis is complete. In addition, the client may request that a -callgraph is constructed. The example program in example_test.go -demonstrates both of these features. Clients should not request more -information than they need since it may increase the cost of the -analysis significantly. - - -CLASSIFICATION - -Our algorithm is INCLUSION-BASED: the points-to sets for x and y will -be related by pts(y) ⊇ pts(x) if the program contains the statement -y = x. - -It is FLOW-INSENSITIVE: it ignores all control flow constructs and the -order of statements in a program. It is therefore a "MAY ALIAS" -analysis: its facts are of the form "P may/may not point to L", -not "P must point to L". - -It is FIELD-SENSITIVE: it builds separate points-to sets for distinct -fields, such as x and y in struct { x, y *int }. - -It is mostly CONTEXT-INSENSITIVE: most functions are analyzed once, -so values can flow in at one call to the function and return out at -another. Only some smaller functions are analyzed with consideration -of their calling context. - -It has a CONTEXT-SENSITIVE HEAP: objects are named by both allocation -site and context, so the objects returned by two distinct calls to f: - func f() *T { return new(T) } -are distinguished up to the limits of the calling context. - -It is a WHOLE PROGRAM analysis: it requires SSA-form IR for the -complete Go program and summaries for native code. - -See the (Hind, PASTE'01) survey paper for an explanation of these terms. - - -SOUNDNESS - -The analysis is fully sound when invoked on pure Go programs that do not -use reflection or unsafe.Pointer conversions. In other words, if there -is any possible execution of the program in which pointer P may point to -object O, the analysis will report that fact. - - -REFLECTION - -By default, the "reflect" library is ignored by the analysis, as if all -its functions were no-ops, but if the client enables the Reflection flag, -the analysis will make a reasonable attempt to model the effects of -calls into this library. However, this comes at a significant -performance cost, and not all features of that library are yet -implemented. In addition, some simplifying approximations must be made -to ensure that the analysis terminates; for example, reflection can be -used to construct an infinite set of types and values of those types, -but the analysis arbitrarily bounds the depth of such types. - -Most but not all reflection operations are supported. -In particular, addressable reflect.Values are not yet implemented, so -operations such as (reflect.Value).Set have no analytic effect. - - -UNSAFE POINTER CONVERSIONS - -The pointer analysis makes no attempt to understand aliasing between the -operand x and result y of an unsafe.Pointer conversion: - y = (*T)(unsafe.Pointer(x)) -It is as if the conversion allocated an entirely new object: - y = new(T) - - -NATIVE CODE - -The analysis cannot model the aliasing effects of functions written in -languages other than Go, such as runtime intrinsics in C or assembly, or -code accessed via cgo. The result is as if such functions are no-ops. -However, various important intrinsics are understood by the analysis, -along with built-ins such as append. - -The analysis currently provides no way for users to specify the aliasing -effects of native code. - ------------------------------------------------------------------------- - -IMPLEMENTATION - -The remaining documentation is intended for package maintainers and -pointer analysis specialists. Maintainers should have a solid -understanding of the referenced papers (especially those by H&L and PKH) -before making making significant changes. - -The implementation is similar to that described in (Pearce et al, -PASTE'04). Unlike many algorithms which interleave constraint -generation and solving, constructing the callgraph as they go, this -implementation for the most part observes a phase ordering (generation -before solving), with only simple (copy) constraints being generated -during solving. (The exception is reflection, which creates various -constraints during solving as new types flow to reflect.Value -operations.) This improves the traction of presolver optimisations, -but imposes certain restrictions, e.g. potential context sensitivity -is limited since all variants must be created a priori. - - -TERMINOLOGY - -A type is said to be "pointer-like" if it is a reference to an object. -Pointer-like types include pointers and also interfaces, maps, channels, -functions and slices. - -We occasionally use C's x->f notation to distinguish the case where x -is a struct pointer from x.f where is a struct value. - -Pointer analysis literature (and our comments) often uses the notation -dst=*src+offset to mean something different than what it means in Go. -It means: for each node index p in pts(src), the node index p+offset is -in pts(dst). Similarly *dst+offset=src is used for store constraints -and dst=src+offset for offset-address constraints. - - -NODES - -Nodes are the key datastructure of the analysis, and have a dual role: -they represent both constraint variables (equivalence classes of -pointers) and members of points-to sets (things that can be pointed -at, i.e. "labels"). - -Nodes are naturally numbered. The numbering enables compact -representations of sets of nodes such as bitvectors (or BDDs); and the -ordering enables a very cheap way to group related nodes together. For -example, passing n parameters consists of generating n parallel -constraints from caller+i to callee+i for 0<=i y is added. - - ChangeInterface is a simple copy because the representation of - tagged objects is independent of the interface type (in contrast - to the "method tables" approach used by the gc runtime). - - y := Invoke x.m(...) is implemented by allocating contiguous P/R - blocks for the callsite and adding a dynamic rule triggered by each - tagged object added to pts(x). The rule adds param/results copy - edges to/from each discovered concrete method. - - (Q. Why do we model an interface as a pointer to a pair of type and - value, rather than as a pair of a pointer to type and a pointer to - value? - A. Control-flow joins would merge interfaces ({T1}, {V1}) and ({T2}, - {V2}) to make ({T1,T2}, {V1,V2}), leading to the infeasible and - type-unsafe combination (T1,V2). Treating the value and its concrete - type as inseparable makes the analysis type-safe.) - -reflect.Value - A reflect.Value is modelled very similar to an interface{}, i.e. as - a pointer exclusively to tagged objects, but with two generalizations. - - 1) a reflect.Value that represents an lvalue points to an indirect - (obj.flags ⊇ {otIndirect}) tagged object, which has a similar - layout to an tagged object except that the value is a pointer to - the dynamic type. Indirect tagged objects preserve the correct - aliasing so that mutations made by (reflect.Value).Set can be - observed. - - Indirect objects only arise when an lvalue is derived from an - rvalue by indirection, e.g. the following code: - - type S struct { X T } - var s S - var i interface{} = &s // i points to a *S-tagged object (from MakeInterface) - v1 := reflect.ValueOf(i) // v1 points to same *S-tagged object as i - v2 := v1.Elem() // v2 points to an indirect S-tagged object, pointing to s - v3 := v2.FieldByName("X") // v3 points to an indirect int-tagged object, pointing to s.X - v3.Set(y) // pts(s.X) ⊇ pts(y) - - Whether indirect or not, the concrete type of the tagged object - corresponds to the user-visible dynamic type, and the existence - of a pointer is an implementation detail. - - (NB: indirect tagged objects are not yet implemented) - - 2) The dynamic type tag of a tagged object pointed to by a - reflect.Value may be an interface type; it need not be concrete. - - This arises in code such as this: - tEface := reflect.TypeOf(new(interface{}).Elem() // interface{} - eface := reflect.Zero(tEface) - pts(eface) is a singleton containing an interface{}-tagged - object. That tagged object's payload is an interface{} value, - i.e. the pts of the payload contains only concrete-tagged - objects, although in this example it's the zero interface{} value, - so its pts is empty. - -reflect.Type - Just as in the real "reflect" library, we represent a reflect.Type - as an interface whose sole implementation is the concrete type, - *reflect.rtype. (This choice is forced on us by go/types: clients - cannot fabricate types with arbitrary method sets.) - - rtype instances are canonical: there is at most one per dynamic - type. (rtypes are in fact large structs but since identity is all - that matters, we represent them by a single node.) - - The payload of each *rtype-tagged object is an *rtype pointer that - points to exactly one such canonical rtype object. We exploit this - by setting the node.typ of the payload to the dynamic type, not - '*rtype'. This saves us an indirection in each resolution rule. As - an optimisation, *rtype-tagged objects are canonicalized too. - - -Aggregate types: - -Aggregate types are treated as if all directly contained -aggregates are recursively flattened out. - -Structs - *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset. - - *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create - simple edges for each struct discovered in pts(x). - - The nodes of a struct consist of a special 'identity' node (whose - type is that of the struct itself), followed by the nodes for all - the struct's fields, recursively flattened out. A pointer to the - struct is a pointer to its identity node. That node allows us to - distinguish a pointer to a struct from a pointer to its first field. - - Field offsets are logical field offsets (plus one for the identity - node), so the sizes of the fields can be ignored by the analysis. - - (The identity node is non-traditional but enables the distiction - described above, which is valuable for code comprehension tools. - Typical pointer analyses for C, whose purpose is compiler - optimization, must soundly model unsafe.Pointer (void*) conversions, - and this requires fidelity to the actual memory layout using physical - field offsets.) - - *ssa.Field y = x.f creates a simple edge to y from x's node at f's offset. - - *ssa.FieldAddr y = &x->f requires a dynamic closure rule to create - simple edges for each struct discovered in pts(x). - -Arrays - We model an array by an identity node (whose type is that of the - array itself) followed by a node representing all the elements of - the array; the analysis does not distinguish elements with different - indices. Effectively, an array is treated like struct{elem T}, a - load y=x[i] like y=x.elem, and a store x[i]=y like x.elem=y; the - index i is ignored. - - A pointer to an array is pointer to its identity node. (A slice is - also a pointer to an array's identity node.) The identity node - allows us to distinguish a pointer to an array from a pointer to one - of its elements, but it is rather costly because it introduces more - offset constraints into the system. Furthermore, sound treatment of - unsafe.Pointer would require us to dispense with this node. - - Arrays may be allocated by Alloc, by make([]T), by calls to append, - and via reflection. - -Tuples (T, ...) - Tuples are treated like structs with naturally numbered fields. - *ssa.Extract is analogous to *ssa.Field. - - However, tuples have no identity field since by construction, they - cannot be address-taken. - - -FUNCTION CALLS - - There are three kinds of function call: - (1) static "call"-mode calls of functions. - (2) dynamic "call"-mode calls of functions. - (3) dynamic "invoke"-mode calls of interface methods. - Cases 1 and 2 apply equally to methods and standalone functions. - - Static calls. - A static call consists three steps: - - finding the function object of the callee; - - creating copy edges from the actual parameter value nodes to the - P-block in the function object (this includes the receiver if - the callee is a method); - - creating copy edges from the R-block in the function object to - the value nodes for the result of the call. - - A static function call is little more than two struct value copies - between the P/R blocks of caller and callee: - - callee.P = caller.P - caller.R = callee.R - - Context sensitivity - - Static calls (alone) may be treated context sensitively, - i.e. each callsite may cause a distinct re-analysis of the - callee, improving precision. Our current context-sensitivity - policy treats all intrinsics and getter/setter methods in this - manner since such functions are small and seem like an obvious - source of spurious confluences, though this has not yet been - evaluated. - - Dynamic function calls - - Dynamic calls work in a similar manner except that the creation of - copy edges occurs dynamically, in a similar fashion to a pair of - struct copies in which the callee is indirect: - - callee->P = caller.P - caller.R = callee->R - - (Recall that the function object's P- and R-blocks are contiguous.) - - Interface method invocation - - For invoke-mode calls, we create a params/results block for the - callsite and attach a dynamic closure rule to the interface. For - each new tagged object that flows to the interface, we look up - the concrete method, find its function object, and connect its P/R - blocks to the callsite's P/R blocks, adding copy edges to the graph - during solving. - - Recording call targets - - The analysis notifies its clients of each callsite it encounters, - passing a CallSite interface. Among other things, the CallSite - contains a synthetic constraint variable ("targets") whose - points-to solution includes the set of all function objects to - which the call may dispatch. - - It is via this mechanism that the callgraph is made available. - Clients may also elect to be notified of callgraph edges directly; - internally this just iterates all "targets" variables' pts(·)s. - - -PRESOLVER - -We implement Hash-Value Numbering (HVN), a pre-solver constraint -optimization described in Hardekopf & Lin, SAS'07. This is documented -in more detail in hvn.go. We intend to add its cousins HR and HU in -future. - - -SOLVER - -The solver is currently a naive Andersen-style implementation; it does -not perform online cycle detection, though we plan to add solver -optimisations such as Hybrid- and Lazy- Cycle Detection from (Hardekopf -& Lin, PLDI'07). - -It uses difference propagation (Pearce et al, SQC'04) to avoid -redundant re-triggering of closure rules for values already seen. - -Points-to sets are represented using sparse bit vectors (similar to -those used in LLVM and gcc), which are more space- and time-efficient -than sets based on Go's built-in map type or dense bit vectors. - -Nodes are permuted prior to solving so that object nodes (which may -appear in points-to sets) are lower numbered than non-object (var) -nodes. This improves the density of the set over which the PTSs -range, and thus the efficiency of the representation. - -Partly thanks to avoiding map iteration, the execution of the solver is -100% deterministic, a great help during debugging. - - -FURTHER READING - -Andersen, L. O. 1994. Program analysis and specialization for the C -programming language. Ph.D. dissertation. DIKU, University of -Copenhagen. - -David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Efficient -field-sensitive pointer analysis for C. In Proceedings of the 5th ACM -SIGPLAN-SIGSOFT workshop on Program analysis for software tools and -engineering (PASTE '04). ACM, New York, NY, USA, 37-42. -http://doi.acm.org/10.1145/996821.996835 - -David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Online -Cycle Detection and Difference Propagation: Applications to Pointer -Analysis. Software Quality Control 12, 4 (December 2004), 311-337. -http://dx.doi.org/10.1023/B:SQJO.0000039791.93071.a2 - -David Grove and Craig Chambers. 2001. A framework for call graph -construction algorithms. ACM Trans. Program. Lang. Syst. 23, 6 -(November 2001), 685-746. -http://doi.acm.org/10.1145/506315.506316 - -Ben Hardekopf and Calvin Lin. 2007. The ant and the grasshopper: fast -and accurate pointer analysis for millions of lines of code. In -Proceedings of the 2007 ACM SIGPLAN conference on Programming language -design and implementation (PLDI '07). ACM, New York, NY, USA, 290-299. -http://doi.acm.org/10.1145/1250734.1250767 - -Ben Hardekopf and Calvin Lin. 2007. Exploiting pointer and location -equivalence to optimize pointer analysis. In Proceedings of the 14th -international conference on Static Analysis (SAS'07), Hanne Riis -Nielson and Gilberto Filé (Eds.). Springer-Verlag, Berlin, Heidelberg, -265-280. - -Atanas Rountev and Satish Chandra. 2000. Off-line variable substitution -for scaling points-to analysis. In Proceedings of the ACM SIGPLAN 2000 -conference on Programming language design and implementation (PLDI '00). -ACM, New York, NY, USA, 47-56. DOI=10.1145/349299.349310 -http://doi.acm.org/10.1145/349299.349310 - -*/ -package pointer // import "golang.org/x/tools/go/pointer" diff --git a/go/pointer/gen14.go b/go/pointer/gen14.go deleted file mode 100644 index c1bce7c2b1..0000000000 --- a/go/pointer/gen14.go +++ /dev/null @@ -1,1315 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This file defines the constraint generation phase. - -// TODO(adonovan): move the constraint definitions and the store() etc -// functions which add them (and are also used by the solver) into a -// new file, constraints.go. - -import ( - "fmt" - "go/token" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -var ( - tEface = types.NewInterface(nil, nil).Complete() - tInvalid = types.Typ[types.Invalid] - tUnsafePtr = types.Typ[types.UnsafePointer] -) - -// ---------- Node creation ---------- - -// nextNode returns the index of the next unused node. -func (a *analysis) nextNode() nodeid { - return nodeid(len(a.nodes)) -} - -// addNodes creates nodes for all scalar elements in type typ, and -// returns the id of the first one, or zero if the type was -// analytically uninteresting. -// -// comment explains the origin of the nodes, as a debugging aid. -// -func (a *analysis) addNodes(typ types.Type, comment string) nodeid { - id := a.nextNode() - for _, fi := range a.flatten(typ) { - a.addOneNode(fi.typ, comment, fi) - } - if id == a.nextNode() { - return 0 // type contained no pointers - } - return id -} - -// addOneNode creates a single node with type typ, and returns its id. -// -// typ should generally be scalar (except for tagged.T nodes -// and struct/array identity nodes). Use addNodes for non-scalar types. -// -// comment explains the origin of the nodes, as a debugging aid. -// subelement indicates the subelement, e.g. ".a.b[*].c". -// -func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldInfo) nodeid { - id := a.nextNode() - a.nodes = append(a.nodes, &node{typ: typ, subelement: subelement, solve: new(solverState)}) - if a.log != nil { - fmt.Fprintf(a.log, "\tcreate n%d %s for %s%s\n", - id, typ, comment, subelement.path()) - } - return id -} - -// setValueNode associates node id with the value v. -// cgn identifies the context iff v is a local variable. -// -func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) { - if cgn != nil { - a.localval[v] = id - } else { - a.globalval[v] = id - } - if a.log != nil { - fmt.Fprintf(a.log, "\tval[%s] = n%d (%T)\n", v.Name(), id, v) - } - - // Due to context-sensitivity, we may encounter the same Value - // in many contexts. We merge them to a canonical node, since - // that's what all clients want. - - // Record the (v, id) relation if the client has queried pts(v). - if _, ok := a.config.Queries[v]; ok { - t := v.Type() - ptr, ok := a.result.Queries[v] - if !ok { - // First time? Create the canonical query node. - ptr = Pointer{a, a.addNodes(t, "query")} - a.result.Queries[v] = ptr - } - a.result.Queries[v] = ptr - a.copy(ptr.n, id, a.sizeof(t)) - } - - // Record the (*v, id) relation if the client has queried pts(*v). - if _, ok := a.config.IndirectQueries[v]; ok { - t := v.Type() - ptr, ok := a.result.IndirectQueries[v] - if !ok { - // First time? Create the canonical indirect query node. - ptr = Pointer{a, a.addNodes(v.Type(), "query.indirect")} - a.result.IndirectQueries[v] = ptr - } - a.genLoad(cgn, ptr.n, v, 0, a.sizeof(t)) - } -} - -// endObject marks the end of a sequence of calls to addNodes denoting -// a single object allocation. -// -// obj is the start node of the object, from a prior call to nextNode. -// Its size, flags and optional data will be updated. -// -func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object { - // Ensure object is non-empty by padding; - // the pad will be the object node. - size := uint32(a.nextNode() - obj) - if size == 0 { - a.addOneNode(tInvalid, "padding", nil) - } - objNode := a.nodes[obj] - o := &object{ - size: size, // excludes padding - cgn: cgn, - data: data, - } - objNode.obj = o - - return o -} - -// makeFunctionObject creates and returns a new function object -// (contour) for fn, and returns the id of its first node. It also -// enqueues fn for subsequent constraint generation. -// -// For a context-sensitive contour, callersite identifies the sole -// callsite; for shared contours, caller is nil. -// -func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid { - if a.log != nil { - fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn) - } - - // obj is the function object (identity, params, results). - obj := a.nextNode() - cgn := a.makeCGNode(fn, obj, callersite) - sig := fn.Signature - a.addOneNode(sig, "func.cgnode", nil) // (scalar with Signature type) - if recv := sig.Recv(); recv != nil { - a.addNodes(recv.Type(), "func.recv") - } - a.addNodes(sig.Params(), "func.params") - a.addNodes(sig.Results(), "func.results") - a.endObject(obj, cgn, fn).flags |= otFunction - - if a.log != nil { - fmt.Fprintf(a.log, "\t----\n") - } - - // Queue it up for constraint processing. - a.genq = append(a.genq, cgn) - - return obj -} - -// makeTagged creates a tagged object of type typ. -func (a *analysis) makeTagged(typ types.Type, cgn *cgnode, data interface{}) nodeid { - obj := a.addOneNode(typ, "tagged.T", nil) // NB: type may be non-scalar! - a.addNodes(typ, "tagged.v") - a.endObject(obj, cgn, data).flags |= otTagged - return obj -} - -// makeRtype returns the canonical tagged object of type *rtype whose -// payload points to the sole rtype object for T. -// -// TODO(adonovan): move to reflect.go; it's part of the solver really. -// -func (a *analysis) makeRtype(T types.Type) nodeid { - if v := a.rtypes.At(T); v != nil { - return v.(nodeid) - } - - // Create the object for the reflect.rtype itself, which is - // ordinarily a large struct but here a single node will do. - obj := a.nextNode() - a.addOneNode(T, "reflect.rtype", nil) - a.endObject(obj, nil, T) - - id := a.makeTagged(a.reflectRtypePtr, nil, T) - a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton) - a.addressOf(a.reflectRtypePtr, id+1, obj) - - a.rtypes.Set(T, id) - return id -} - -// rtypeValue returns the type of the *reflect.rtype-tagged object obj. -func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type { - tDyn, t, _ := a.taggedValue(obj) - if tDyn != a.reflectRtypePtr { - panic(fmt.Sprintf("not a *reflect.rtype-tagged object: obj=n%d tag=%v payload=n%d", obj, tDyn, t)) - } - return a.nodes[t].typ -} - -// valueNode returns the id of the value node for v, creating it (and -// the association) as needed. It may return zero for uninteresting -// values containing no pointers. -// -func (a *analysis) valueNode(v ssa.Value) nodeid { - // Value nodes for locals are created en masse by genFunc. - if id, ok := a.localval[v]; ok { - return id - } - - // Value nodes for globals are created on demand. - id, ok := a.globalval[v] - if !ok { - var comment string - if a.log != nil { - comment = v.String() - } - id = a.addNodes(v.Type(), comment) - if obj := a.objectNode(nil, v); obj != 0 { - a.addressOf(v.Type(), id, obj) - } - a.setValueNode(v, id, nil) - } - return id -} - -// valueOffsetNode ascertains the node for tuple/struct value v, -// then returns the node for its subfield #index. -// -func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid { - id := a.valueNode(v) - if id == 0 { - panic(fmt.Sprintf("cannot offset within n0: %s = %s", v.Name(), v)) - } - return id + nodeid(a.offsetOf(v.Type(), index)) -} - -// isTaggedObject reports whether object obj is a tagged object. -func (a *analysis) isTaggedObject(obj nodeid) bool { - return a.nodes[obj].obj.flags&otTagged != 0 -} - -// taggedValue returns the dynamic type tag, the (first node of the) -// payload, and the indirect flag of the tagged object starting at id. -// Panic ensues if !isTaggedObject(id). -// -func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) { - n := a.nodes[obj] - flags := n.obj.flags - if flags&otTagged == 0 { - panic(fmt.Sprintf("not a tagged object: n%d", obj)) - } - return n.typ, obj + 1, flags&otIndirect != 0 -} - -// funcParams returns the first node of the params (P) block of the -// function whose object node (obj.flags&otFunction) is id. -// -func (a *analysis) funcParams(id nodeid) nodeid { - n := a.nodes[id] - if n.obj == nil || n.obj.flags&otFunction == 0 { - panic(fmt.Sprintf("funcParams(n%d): not a function object block", id)) - } - return id + 1 -} - -// funcResults returns the first node of the results (R) block of the -// function whose object node (obj.flags&otFunction) is id. -// -func (a *analysis) funcResults(id nodeid) nodeid { - n := a.nodes[id] - if n.obj == nil || n.obj.flags&otFunction == 0 { - panic(fmt.Sprintf("funcResults(n%d): not a function object block", id)) - } - sig := n.typ.(*types.Signature) - id += 1 + nodeid(a.sizeof(sig.Params())) - if sig.Recv() != nil { - id += nodeid(a.sizeof(sig.Recv().Type())) - } - return id -} - -// ---------- Constraint creation ---------- - -// copy creates a constraint of the form dst = src. -// sizeof is the width (in logical fields) of the copied type. -// -func (a *analysis) copy(dst, src nodeid, sizeof uint32) { - if src == dst || sizeof == 0 { - return // trivial - } - if src == 0 || dst == 0 { - panic(fmt.Sprintf("ill-typed copy dst=n%d src=n%d", dst, src)) - } - for i := uint32(0); i < sizeof; i++ { - a.addConstraint(©Constraint{dst, src}) - src++ - dst++ - } -} - -// addressOf creates a constraint of the form id = &obj. -// T is the type of the address. -func (a *analysis) addressOf(T types.Type, id, obj nodeid) { - if id == 0 { - panic("addressOf: zero id") - } - if obj == 0 { - panic("addressOf: zero obj") - } - if a.shouldTrack(T) { - a.addConstraint(&addrConstraint{id, obj}) - } -} - -// load creates a load constraint of the form dst = src[offset]. -// offset is the pointer offset in logical fields. -// sizeof is the width (in logical fields) of the loaded type. -// -func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) { - if dst == 0 { - return // load of non-pointerlike value - } - if src == 0 && dst == 0 { - return // non-pointerlike operation - } - if src == 0 || dst == 0 { - panic(fmt.Sprintf("ill-typed load dst=n%d src=n%d", dst, src)) - } - for i := uint32(0); i < sizeof; i++ { - a.addConstraint(&loadConstraint{offset, dst, src}) - offset++ - dst++ - } -} - -// store creates a store constraint of the form dst[offset] = src. -// offset is the pointer offset in logical fields. -// sizeof is the width (in logical fields) of the stored type. -// -func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) { - if src == 0 { - return // store of non-pointerlike value - } - if src == 0 && dst == 0 { - return // non-pointerlike operation - } - if src == 0 || dst == 0 { - panic(fmt.Sprintf("ill-typed store dst=n%d src=n%d", dst, src)) - } - for i := uint32(0); i < sizeof; i++ { - a.addConstraint(&storeConstraint{offset, dst, src}) - offset++ - src++ - } -} - -// offsetAddr creates an offsetAddr constraint of the form dst = &src.#offset. -// offset is the field offset in logical fields. -// T is the type of the address. -// -func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) { - if !a.shouldTrack(T) { - return - } - if offset == 0 { - // Simplify dst = &src->f0 - // to dst = src - // (NB: this optimisation is defeated by the identity - // field prepended to struct and array objects.) - a.copy(dst, src, 1) - } else { - a.addConstraint(&offsetAddrConstraint{offset, dst, src}) - } -} - -// typeAssert creates a typeFilter or untag constraint of the form dst = src.(T): -// typeFilter for an interface, untag for a concrete type. -// The exact flag is specified as for untagConstraint. -// -func (a *analysis) typeAssert(T types.Type, dst, src nodeid, exact bool) { - if isInterface(T) { - a.addConstraint(&typeFilterConstraint{T, dst, src}) - } else { - a.addConstraint(&untagConstraint{T, dst, src, exact}) - } -} - -// addConstraint adds c to the constraint set. -func (a *analysis) addConstraint(c constraint) { - a.constraints = append(a.constraints, c) - if a.log != nil { - fmt.Fprintf(a.log, "\t%s\n", c) - } -} - -// copyElems generates load/store constraints for *dst = *src, -// where src and dst are slices or *arrays. -// -func (a *analysis) copyElems(cgn *cgnode, typ types.Type, dst, src ssa.Value) { - tmp := a.addNodes(typ, "copy") - sz := a.sizeof(typ) - a.genLoad(cgn, tmp, src, 1, sz) - a.genStore(cgn, dst, tmp, 1, sz) -} - -// ---------- Constraint generation ---------- - -// genConv generates constraints for the conversion operation conv. -func (a *analysis) genConv(conv *ssa.Convert, cgn *cgnode) { - res := a.valueNode(conv) - if res == 0 { - return // result is non-pointerlike - } - - tSrc := conv.X.Type() - tDst := conv.Type() - - switch utSrc := tSrc.Underlying().(type) { - case *types.Slice: - // []byte/[]rune -> string? - return - - case *types.Pointer: - // *T -> unsafe.Pointer? - if tDst.Underlying() == tUnsafePtr { - return // we don't model unsafe aliasing (unsound) - } - - case *types.Basic: - switch tDst.Underlying().(type) { - case *types.Pointer: - // Treat unsafe.Pointer->*T conversions like - // new(T) and create an unaliased object. - if utSrc == tUnsafePtr { - obj := a.addNodes(mustDeref(tDst), "unsafe.Pointer conversion") - a.endObject(obj, cgn, conv) - a.addressOf(tDst, res, obj) - return - } - - case *types.Slice: - // string -> []byte/[]rune (or named aliases)? - if utSrc.Info()&types.IsString != 0 { - obj := a.addNodes(sliceToArray(tDst), "convert") - a.endObject(obj, cgn, conv) - a.addressOf(tDst, res, obj) - return - } - - case *types.Basic: - // All basic-to-basic type conversions are no-ops. - // This includes uintptr<->unsafe.Pointer conversions, - // which we (unsoundly) ignore. - return - } - } - - panic(fmt.Sprintf("illegal *ssa.Convert %s -> %s: %s", tSrc, tDst, conv.Parent())) -} - -// genAppend generates constraints for a call to append. -func (a *analysis) genAppend(instr *ssa.Call, cgn *cgnode) { - // Consider z = append(x, y). y is optional. - // This may allocate a new [1]T array; call its object w. - // We get the following constraints: - // z = x - // z = &w - // *z = *y - - x := instr.Call.Args[0] - - z := instr - a.copy(a.valueNode(z), a.valueNode(x), 1) // z = x - - if len(instr.Call.Args) == 1 { - return // no allocation for z = append(x) or _ = append(x). - } - - // TODO(adonovan): test append([]byte, ...string) []byte. - - y := instr.Call.Args[1] - tArray := sliceToArray(instr.Call.Args[0].Type()) - - var w nodeid - w = a.nextNode() - a.addNodes(tArray, "append") - a.endObject(w, cgn, instr) - - a.copyElems(cgn, tArray.Elem(), z, y) // *z = *y - a.addressOf(instr.Type(), a.valueNode(z), w) // z = &w -} - -// genBuiltinCall generates contraints for a call to a built-in. -func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) { - call := instr.Common() - switch call.Value.(*ssa.Builtin).Name() { - case "append": - // Safe cast: append cannot appear in a go or defer statement. - a.genAppend(instr.(*ssa.Call), cgn) - - case "copy": - tElem := call.Args[0].Type().Underlying().(*types.Slice).Elem() - a.copyElems(cgn, tElem, call.Args[0], call.Args[1]) - - case "panic": - a.copy(a.panicNode, a.valueNode(call.Args[0]), 1) - - case "recover": - if v := instr.Value(); v != nil { - a.copy(a.valueNode(v), a.panicNode, 1) - } - - case "print": - // In the tests, the probe might be the sole reference - // to its arg, so make sure we create nodes for it. - if len(call.Args) > 0 { - a.valueNode(call.Args[0]) - } - - case "ssa:wrapnilchk": - a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1) - - default: - // No-ops: close len cap real imag complex print println delete. - } -} - -// shouldUseContext defines the context-sensitivity policy. It -// returns true if we should analyse all static calls to fn anew. -// -// Obviously this interface rather limits how much freedom we have to -// choose a policy. The current policy, rather arbitrarily, is true -// for intrinsics and accessor methods (actually: short, single-block, -// call-free functions). This is just a starting point. -// -func (a *analysis) shouldUseContext(fn *ssa.Function) bool { - if a.findIntrinsic(fn) != nil { - return true // treat intrinsics context-sensitively - } - if len(fn.Blocks) != 1 { - return false // too expensive - } - blk := fn.Blocks[0] - if len(blk.Instrs) > 10 { - return false // too expensive - } - if fn.Synthetic != "" && (fn.Pkg == nil || fn != fn.Pkg.Func("init")) { - return true // treat synthetic wrappers context-sensitively - } - for _, instr := range blk.Instrs { - switch instr := instr.(type) { - case ssa.CallInstruction: - // Disallow function calls (except to built-ins) - // because of the danger of unbounded recursion. - if _, ok := instr.Common().Value.(*ssa.Builtin); !ok { - return false - } - } - } - return true -} - -// genStaticCall generates constraints for a statically dispatched function call. -func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { - fn := call.StaticCallee() - - // Special cases for inlined intrinsics. - switch fn { - case a.runtimeSetFinalizer: - // Inline SetFinalizer so the call appears direct. - site.targets = a.addOneNode(tInvalid, "SetFinalizer.targets", nil) - a.addConstraint(&runtimeSetFinalizerConstraint{ - targets: site.targets, - x: a.valueNode(call.Args[0]), - f: a.valueNode(call.Args[1]), - }) - return - - case a.reflectValueCall: - // Inline (reflect.Value).Call so the call appears direct. - dotdotdot := false - ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot) - if result != 0 { - a.addressOf(fn.Signature.Results().At(0).Type(), result, ret) - } - return - } - - // Ascertain the context (contour/cgnode) for a particular call. - var obj nodeid - if a.shouldUseContext(fn) { - obj = a.makeFunctionObject(fn, site) // new contour - } else { - obj = a.objectNode(nil, fn) // shared contour - } - a.callEdge(caller, site, obj) - - sig := call.Signature() - - // Copy receiver, if any. - params := a.funcParams(obj) - args := call.Args - if sig.Recv() != nil { - sz := a.sizeof(sig.Recv().Type()) - a.copy(params, a.valueNode(args[0]), sz) - params += nodeid(sz) - args = args[1:] - } - - // Copy actual parameters into formal params block. - // Must loop, since the actuals aren't contiguous. - for i, arg := range args { - sz := a.sizeof(sig.Params().At(i).Type()) - a.copy(params, a.valueNode(arg), sz) - params += nodeid(sz) - } - - // Copy formal results block to actual result. - if result != 0 { - a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) - } -} - -// genDynamicCall generates constraints for a dynamic function call. -func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { - // pts(targets) will be the set of possible call targets. - site.targets = a.valueNode(call.Value) - - // We add dynamic closure rules that store the arguments into - // the P-block and load the results from the R-block of each - // function discovered in pts(targets). - - sig := call.Signature() - var offset uint32 = 1 // P/R block starts at offset 1 - for i, arg := range call.Args { - sz := a.sizeof(sig.Params().At(i).Type()) - a.genStore(caller, call.Value, a.valueNode(arg), offset, sz) - offset += sz - } - if result != 0 { - a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results())) - } -} - -// genInvoke generates constraints for a dynamic method invocation. -func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { - if call.Value.Type() == a.reflectType { - a.genInvokeReflectType(caller, site, call, result) - return - } - - sig := call.Signature() - - // Allocate a contiguous targets/params/results block for this call. - block := a.nextNode() - // pts(targets) will be the set of possible call targets - site.targets = a.addOneNode(sig, "invoke.targets", nil) - p := a.addNodes(sig.Params(), "invoke.params") - r := a.addNodes(sig.Results(), "invoke.results") - - // Copy the actual parameters into the call's params block. - for i, n := 0, sig.Params().Len(); i < n; i++ { - sz := a.sizeof(sig.Params().At(i).Type()) - a.copy(p, a.valueNode(call.Args[i]), sz) - p += nodeid(sz) - } - // Copy the call's results block to the actual results. - if result != 0 { - a.copy(result, r, a.sizeof(sig.Results())) - } - - // We add a dynamic invoke constraint that will connect the - // caller's and the callee's P/R blocks for each discovered - // call target. - a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block}) -} - -// genInvokeReflectType is a specialization of genInvoke where the -// receiver type is a reflect.Type, under the assumption that there -// can be at most one implementation of this interface, *reflect.rtype. -// -// (Though this may appear to be an instance of a pattern---method -// calls on interfaces known to have exactly one implementation---in -// practice it occurs rarely, so we special case for reflect.Type.) -// -// In effect we treat this: -// var rt reflect.Type = ... -// rt.F() -// as this: -// rt.(*reflect.rtype).F() -// -func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) { - // Unpack receiver into rtype - rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil) - recv := a.valueNode(call.Value) - a.typeAssert(a.reflectRtypePtr, rtype, recv, true) - - // Look up the concrete method. - fn := a.prog.LookupMethod(a.reflectRtypePtr, call.Method.Pkg(), call.Method.Name()) - - obj := a.makeFunctionObject(fn, site) // new contour for this call - a.callEdge(caller, site, obj) - - // From now on, it's essentially a static call, but little is - // gained by factoring together the code for both cases. - - sig := fn.Signature // concrete method - targets := a.addOneNode(sig, "call.targets", nil) - a.addressOf(sig, targets, obj) // (a singleton) - - // Copy receiver. - params := a.funcParams(obj) - a.copy(params, rtype, 1) - params++ - - // Copy actual parameters into formal P-block. - // Must loop, since the actuals aren't contiguous. - for i, arg := range call.Args { - sz := a.sizeof(sig.Params().At(i).Type()) - a.copy(params, a.valueNode(arg), sz) - params += nodeid(sz) - } - - // Copy formal R-block to actual R-block. - if result != 0 { - a.copy(result, a.funcResults(obj), a.sizeof(sig.Results())) - } -} - -// genCall generates constraints for call instruction instr. -func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) { - call := instr.Common() - - // Intrinsic implementations of built-in functions. - if _, ok := call.Value.(*ssa.Builtin); ok { - a.genBuiltinCall(instr, caller) - return - } - - var result nodeid - if v := instr.Value(); v != nil { - result = a.valueNode(v) - } - - site := &callsite{instr: instr} - if call.StaticCallee() != nil { - a.genStaticCall(caller, site, call, result) - } else if call.IsInvoke() { - a.genInvoke(caller, site, call, result) - } else { - a.genDynamicCall(caller, site, call, result) - } - - caller.sites = append(caller.sites, site) - - if a.log != nil { - // TODO(adonovan): debug: improve log message. - fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) - } -} - -// objectNode returns the object to which v points, if known. -// In other words, if the points-to set of v is a singleton, it -// returns the sole label, zero otherwise. -// -// We exploit this information to make the generated constraints less -// dynamic. For example, a complex load constraint can be replaced by -// a simple copy constraint when the sole destination is known a priori. -// -// Some SSA instructions always have singletons points-to sets: -// Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice. -// Others may be singletons depending on their operands: -// FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice. -// -// Idempotent. Objects are created as needed, possibly via recursion -// down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))). -// -func (a *analysis) objectNode(cgn *cgnode, v ssa.Value) nodeid { - switch v.(type) { - case *ssa.Global, *ssa.Function, *ssa.Const, *ssa.FreeVar: - // Global object. - obj, ok := a.globalobj[v] - if !ok { - switch v := v.(type) { - case *ssa.Global: - obj = a.nextNode() - a.addNodes(mustDeref(v.Type()), "global") - a.endObject(obj, nil, v) - - case *ssa.Function: - obj = a.makeFunctionObject(v, nil) - - case *ssa.Const: - // not addressable - - case *ssa.FreeVar: - // not addressable - } - - if a.log != nil { - fmt.Fprintf(a.log, "\tglobalobj[%s] = n%d\n", v, obj) - } - a.globalobj[v] = obj - } - return obj - } - - // Local object. - obj, ok := a.localobj[v] - if !ok { - switch v := v.(type) { - case *ssa.Alloc: - obj = a.nextNode() - a.addNodes(mustDeref(v.Type()), "alloc") - a.endObject(obj, cgn, v) - - case *ssa.MakeSlice: - obj = a.nextNode() - a.addNodes(sliceToArray(v.Type()), "makeslice") - a.endObject(obj, cgn, v) - - case *ssa.MakeChan: - obj = a.nextNode() - a.addNodes(v.Type().Underlying().(*types.Chan).Elem(), "makechan") - a.endObject(obj, cgn, v) - - case *ssa.MakeMap: - obj = a.nextNode() - tmap := v.Type().Underlying().(*types.Map) - a.addNodes(tmap.Key(), "makemap.key") - elem := a.addNodes(tmap.Elem(), "makemap.value") - - // To update the value field, MapUpdate - // generates store-with-offset constraints which - // the presolver can't model, so we must mark - // those nodes indirect. - for id, end := elem, elem+nodeid(a.sizeof(tmap.Elem())); id < end; id++ { - a.mapValues = append(a.mapValues, id) - } - a.endObject(obj, cgn, v) - - case *ssa.MakeInterface: - tConc := v.X.Type() - obj = a.makeTagged(tConc, cgn, v) - - // Copy the value into it, if nontrivial. - if x := a.valueNode(v.X); x != 0 { - a.copy(obj+1, x, a.sizeof(tConc)) - } - - case *ssa.FieldAddr: - if xobj := a.objectNode(cgn, v.X); xobj != 0 { - obj = xobj + nodeid(a.offsetOf(mustDeref(v.X.Type()), v.Field)) - } - - case *ssa.IndexAddr: - if xobj := a.objectNode(cgn, v.X); xobj != 0 { - obj = xobj + 1 - } - - case *ssa.Slice: - obj = a.objectNode(cgn, v.X) - - case *ssa.Convert: - // TODO(adonovan): opt: handle these cases too: - // - unsafe.Pointer->*T conversion acts like Alloc - // - string->[]byte/[]rune conversion acts like MakeSlice - } - - if a.log != nil { - fmt.Fprintf(a.log, "\tlocalobj[%s] = n%d\n", v.Name(), obj) - } - a.localobj[v] = obj - } - return obj -} - -// genLoad generates constraints for result = *(ptr + val). -func (a *analysis) genLoad(cgn *cgnode, result nodeid, ptr ssa.Value, offset, sizeof uint32) { - if obj := a.objectNode(cgn, ptr); obj != 0 { - // Pre-apply loadConstraint.solve(). - a.copy(result, obj+nodeid(offset), sizeof) - } else { - a.load(result, a.valueNode(ptr), offset, sizeof) - } -} - -// genOffsetAddr generates constraints for a 'v=ptr.field' (FieldAddr) -// or 'v=ptr[*]' (IndexAddr) instruction v. -func (a *analysis) genOffsetAddr(cgn *cgnode, v ssa.Value, ptr nodeid, offset uint32) { - dst := a.valueNode(v) - if obj := a.objectNode(cgn, v); obj != 0 { - // Pre-apply offsetAddrConstraint.solve(). - a.addressOf(v.Type(), dst, obj) - } else { - a.offsetAddr(v.Type(), dst, ptr, offset) - } -} - -// genStore generates constraints for *(ptr + offset) = val. -func (a *analysis) genStore(cgn *cgnode, ptr ssa.Value, val nodeid, offset, sizeof uint32) { - if obj := a.objectNode(cgn, ptr); obj != 0 { - // Pre-apply storeConstraint.solve(). - a.copy(obj+nodeid(offset), val, sizeof) - } else { - a.store(a.valueNode(ptr), val, offset, sizeof) - } -} - -// genInstr generates constraints for instruction instr in context cgn. -func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) { - if a.log != nil { - var prefix string - if val, ok := instr.(ssa.Value); ok { - prefix = val.Name() + " = " - } - fmt.Fprintf(a.log, "; %s%s\n", prefix, instr) - } - - switch instr := instr.(type) { - case *ssa.DebugRef: - // no-op. - - case *ssa.UnOp: - switch instr.Op { - case token.ARROW: // <-x - // We can ignore instr.CommaOk because the node we're - // altering is always at zero offset relative to instr - tElem := instr.X.Type().Underlying().(*types.Chan).Elem() - a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(tElem)) - - case token.MUL: // *x - a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(instr.Type())) - - default: - // NOT, SUB, XOR: no-op. - } - - case *ssa.BinOp: - // All no-ops. - - case ssa.CallInstruction: // *ssa.Call, *ssa.Go, *ssa.Defer - a.genCall(cgn, instr) - - case *ssa.ChangeType: - a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) - - case *ssa.Convert: - a.genConv(instr, cgn) - - case *ssa.Extract: - a.copy(a.valueNode(instr), - a.valueOffsetNode(instr.Tuple, instr.Index), - a.sizeof(instr.Type())) - - case *ssa.FieldAddr: - a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), - a.offsetOf(mustDeref(instr.X.Type()), instr.Field)) - - case *ssa.IndexAddr: - a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), 1) - - case *ssa.Field: - a.copy(a.valueNode(instr), - a.valueOffsetNode(instr.X, instr.Field), - a.sizeof(instr.Type())) - - case *ssa.Index: - a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type())) - - case *ssa.Select: - recv := a.valueOffsetNode(instr, 2) // instr : (index, recvOk, recv0, ... recv_n-1) - for _, st := range instr.States { - elemSize := a.sizeof(st.Chan.Type().Underlying().(*types.Chan).Elem()) - switch st.Dir { - case types.RecvOnly: - a.genLoad(cgn, recv, st.Chan, 0, elemSize) - recv += nodeid(elemSize) - - case types.SendOnly: - a.genStore(cgn, st.Chan, a.valueNode(st.Send), 0, elemSize) - } - } - - case *ssa.Return: - results := a.funcResults(cgn.obj) - for _, r := range instr.Results { - sz := a.sizeof(r.Type()) - a.copy(results, a.valueNode(r), sz) - results += nodeid(sz) - } - - case *ssa.Send: - a.genStore(cgn, instr.Chan, a.valueNode(instr.X), 0, a.sizeof(instr.X.Type())) - - case *ssa.Store: - a.genStore(cgn, instr.Addr, a.valueNode(instr.Val), 0, a.sizeof(instr.Val.Type())) - - case *ssa.Alloc, *ssa.MakeSlice, *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeInterface: - v := instr.(ssa.Value) - a.addressOf(v.Type(), a.valueNode(v), a.objectNode(cgn, v)) - - case *ssa.ChangeInterface: - a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) - - case *ssa.TypeAssert: - a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X), true) - - case *ssa.Slice: - a.copy(a.valueNode(instr), a.valueNode(instr.X), 1) - - case *ssa.If, *ssa.Jump: - // no-op. - - case *ssa.Phi: - sz := a.sizeof(instr.Type()) - for _, e := range instr.Edges { - a.copy(a.valueNode(instr), a.valueNode(e), sz) - } - - case *ssa.MakeClosure: - fn := instr.Fn.(*ssa.Function) - a.copy(a.valueNode(instr), a.valueNode(fn), 1) - // Free variables are treated like global variables. - for i, b := range instr.Bindings { - a.copy(a.valueNode(fn.FreeVars[i]), a.valueNode(b), a.sizeof(b.Type())) - } - - case *ssa.RunDefers: - // The analysis is flow insensitive, so we just "call" - // defers as we encounter them. - - case *ssa.Range: - // Do nothing. Next{Iter: *ssa.Range} handles this case. - - case *ssa.Next: - if !instr.IsString { // map - // Assumes that Next is always directly applied to a Range result. - theMap := instr.Iter.(*ssa.Range).X - tMap := theMap.Type().Underlying().(*types.Map) - - ksize := a.sizeof(tMap.Key()) - vsize := a.sizeof(tMap.Elem()) - - // The k/v components of the Next tuple may each be invalid. - tTuple := instr.Type().(*types.Tuple) - - // Load from the map's (k,v) into the tuple's (ok, k, v). - osrc := uint32(0) // offset within map object - odst := uint32(1) // offset within tuple (initially just after 'ok bool') - sz := uint32(0) // amount to copy - - // Is key valid? - if tTuple.At(1).Type() != tInvalid { - sz += ksize - } else { - odst += ksize - osrc += ksize - } - - // Is value valid? - if tTuple.At(2).Type() != tInvalid { - sz += vsize - } - - a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz) - } - - case *ssa.Lookup: - if tMap, ok := instr.X.Type().Underlying().(*types.Map); ok { - // CommaOk can be ignored: field 0 is a no-op. - ksize := a.sizeof(tMap.Key()) - vsize := a.sizeof(tMap.Elem()) - a.genLoad(cgn, a.valueNode(instr), instr.X, ksize, vsize) - } - - case *ssa.MapUpdate: - tmap := instr.Map.Type().Underlying().(*types.Map) - ksize := a.sizeof(tmap.Key()) - vsize := a.sizeof(tmap.Elem()) - a.genStore(cgn, instr.Map, a.valueNode(instr.Key), 0, ksize) - a.genStore(cgn, instr.Map, a.valueNode(instr.Value), ksize, vsize) - - case *ssa.Panic: - a.copy(a.panicNode, a.valueNode(instr.X), 1) - - default: - panic(fmt.Sprintf("unimplemented: %T", instr)) - } -} - -func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite) *cgnode { - cgn := &cgnode{fn: fn, obj: obj, callersite: callersite} - a.cgnodes = append(a.cgnodes, cgn) - return cgn -} - -// genRootCalls generates the synthetic root of the callgraph and the -// initial calls from it to the analysis scope, such as main, a test -// or a library. -// -func (a *analysis) genRootCalls() *cgnode { - r := a.prog.NewFunction("", new(types.Signature), "root of callgraph") - root := a.makeCGNode(r, 0, nil) - - // TODO(adonovan): make an ssa utility to construct an actual - // root function so we don't need to special-case site-less - // call edges. - - // For each main package, call main.init(), main.main(). - for _, mainPkg := range a.config.Mains { - main := mainPkg.Func("main") - if main == nil { - panic(fmt.Sprintf("%s has no main function", mainPkg)) - } - - targets := a.addOneNode(main.Signature, "root.targets", nil) - site := &callsite{targets: targets} - root.sites = append(root.sites, site) - for _, fn := range [2]*ssa.Function{mainPkg.Func("init"), main} { - if a.log != nil { - fmt.Fprintf(a.log, "\troot call to %s:\n", fn) - } - a.copy(targets, a.valueNode(fn), 1) - } - } - - return root -} - -// genFunc generates constraints for function fn. -func (a *analysis) genFunc(cgn *cgnode) { - fn := cgn.fn - - impl := a.findIntrinsic(fn) - - if a.log != nil { - fmt.Fprintf(a.log, "\n\n==== Generating constraints for %s, %s\n", cgn, cgn.contour()) - - // Hack: don't display body if intrinsic. - if impl != nil { - fn2 := *cgn.fn // copy - fn2.Locals = nil - fn2.Blocks = nil - fn2.WriteTo(a.log) - } else { - cgn.fn.WriteTo(a.log) - } - } - - if impl != nil { - impl(a, cgn) - return - } - - if fn.Blocks == nil { - // External function with no intrinsic treatment. - // We'll warn about calls to such functions at the end. - return - } - - if a.log != nil { - fmt.Fprintln(a.log, "; Creating nodes for local values") - } - - a.localval = make(map[ssa.Value]nodeid) - a.localobj = make(map[ssa.Value]nodeid) - - // The value nodes for the params are in the func object block. - params := a.funcParams(cgn.obj) - for _, p := range fn.Params { - a.setValueNode(p, params, cgn) - params += nodeid(a.sizeof(p.Type())) - } - - // Free variables have global cardinality: - // the outer function sets them with MakeClosure; - // the inner function accesses them with FreeVar. - // - // TODO(adonovan): treat free vars context-sensitively. - - // Create value nodes for all value instructions - // since SSA may contain forward references. - var space [10]*ssa.Value - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - switch instr := instr.(type) { - case *ssa.Range: - // do nothing: it has a funky type, - // and *ssa.Next does all the work. - - case ssa.Value: - var comment string - if a.log != nil { - comment = instr.Name() - } - id := a.addNodes(instr.Type(), comment) - a.setValueNode(instr, id, cgn) - } - - // Record all address-taken functions (for presolver). - rands := instr.Operands(space[:0]) - if call, ok := instr.(ssa.CallInstruction); ok && !call.Common().IsInvoke() { - // Skip CallCommon.Value in "call" mode. - // TODO(adonovan): fix: relies on unspecified ordering. Specify it. - rands = rands[1:] - } - for _, rand := range rands { - if atf, ok := (*rand).(*ssa.Function); ok { - a.atFuncs[atf] = true - } - } - } - } - - // Generate constraints for instructions. - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - a.genInstr(cgn, instr) - } - } - - a.localval = nil - a.localobj = nil -} - -// genMethodsOf generates nodes and constraints for all methods of type T. -func (a *analysis) genMethodsOf(T types.Type) { - itf := isInterface(T) - - // TODO(adonovan): can we skip this entirely if itf is true? - // I think so, but the answer may depend on reflection. - mset := a.prog.MethodSets.MethodSet(T) - for i, n := 0, mset.Len(); i < n; i++ { - m := a.prog.MethodValue(mset.At(i)) - a.valueNode(m) - - if !itf { - // Methods of concrete types are address-taken functions. - a.atFuncs[m] = true - } - } -} - -// generate generates offline constraints for the entire program. -func (a *analysis) generate() { - start("Constraint generation") - if a.log != nil { - fmt.Fprintln(a.log, "==== Generating constraints") - } - - // Create a dummy node since we use the nodeid 0 for - // non-pointerlike variables. - a.addNodes(tInvalid, "(zero)") - - // Create the global node for panic values. - a.panicNode = a.addNodes(tEface, "panic") - - // Create nodes and constraints for all methods of reflect.rtype. - // (Shared contours are used by dynamic calls to reflect.Type - // methods---typically just String().) - if rtype := a.reflectRtypePtr; rtype != nil { - a.genMethodsOf(rtype) - } - - root := a.genRootCalls() - - if a.config.BuildCallGraph { - a.result.CallGraph = callgraph.New(root.fn) - } - - // Create nodes and constraints for all methods of all types - // that are dynamically accessible via reflection or interfaces. - for _, T := range a.prog.RuntimeTypes() { - a.genMethodsOf(T) - } - - // Generate constraints for entire program. - for len(a.genq) > 0 { - cgn := a.genq[0] - a.genq = a.genq[1:] - a.genFunc(cgn) - } - - // The runtime magically allocates os.Args; so should we. - if os := a.prog.ImportedPackage("os"); os != nil { - // In effect: os.Args = new([1]string)[:] - T := types.NewSlice(types.Typ[types.String]) - obj := a.addNodes(sliceToArray(T), "") - a.endObject(obj, nil, "") - a.addressOf(T, a.objectNode(nil, os.Var("Args")), obj) - } - - // Discard generation state, to avoid confusion after node renumbering. - a.panicNode = 0 - a.globalval = nil - a.localval = nil - a.localobj = nil - - stop("Constraint generation") -} diff --git a/go/pointer/hvn14.go b/go/pointer/hvn14.go deleted file mode 100644 index bc387456fb..0000000000 --- a/go/pointer/hvn14.go +++ /dev/null @@ -1,975 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This file implements Hash-Value Numbering (HVN), a pre-solver -// constraint optimization described in Hardekopf & Lin, SAS'07 (see -// doc.go) that analyses the graph topology to determine which sets of -// variables are "pointer equivalent" (PE), i.e. must have identical -// points-to sets in the solution. -// -// A separate ("offline") graph is constructed. Its nodes are those of -// the main-graph, plus an additional node *X for each pointer node X. -// With this graph we can reason about the unknown points-to set of -// dereferenced pointers. (We do not generalize this to represent -// unknown fields x->f, perhaps because such fields would be numerous, -// though it might be worth an experiment.) -// -// Nodes whose points-to relations are not entirely captured by the -// graph are marked as "indirect": the *X nodes, the parameters of -// address-taken functions (which includes all functions in method -// sets), or nodes updated by the solver rules for reflection, etc. -// -// All addr (y=&x) nodes are initially assigned a pointer-equivalence -// (PE) label equal to x's nodeid in the main graph. (These are the -// only PE labels that are less than len(a.nodes).) -// -// All offsetAddr (y=&x.f) constraints are initially assigned a PE -// label; such labels are memoized, keyed by (x, f), so that equivalent -// nodes y as assigned the same label. -// -// Then we process each strongly connected component (SCC) of the graph -// in topological order, assigning it a PE label based on the set P of -// PE labels that flow to it from its immediate dependencies. -// -// If any node in P is "indirect", the entire SCC is assigned a fresh PE -// label. Otherwise: -// -// |P|=0 if P is empty, all nodes in the SCC are non-pointers (e.g. -// uninitialized variables, or formal params of dead functions) -// and the SCC is assigned the PE label of zero. -// -// |P|=1 if P is a singleton, the SCC is assigned the same label as the -// sole element of P. -// -// |P|>1 if P contains multiple labels, a unique label representing P is -// invented and recorded in an hash table, so that other -// equivalent SCCs may also be assigned this label, akin to -// conventional hash-value numbering in a compiler. -// -// Finally, a renumbering is computed such that each node is replaced by -// the lowest-numbered node with the same PE label. All constraints are -// renumbered, and any resulting duplicates are eliminated. -// -// The only nodes that are not renumbered are the objects x in addr -// (y=&x) constraints, since the ids of these nodes (and fields derived -// from them via offsetAddr rules) are the elements of all points-to -// sets, so they must remain as they are if we want the same solution. -// -// The solverStates (node.solve) for nodes in the same equivalence class -// are linked together so that all nodes in the class have the same -// solution. This avoids the need to renumber nodeids buried in -// Queries, cgnodes, etc (like (*analysis).renumber() does) since only -// the solution is needed. -// -// The result of HVN is that the number of distinct nodes and -// constraints is reduced, but the solution is identical (almost---see -// CROSS-CHECK below). In particular, both linear and cyclic chains of -// copies are each replaced by a single node. -// -// Nodes and constraints created "online" (e.g. while solving reflection -// constraints) are not subject to this optimization. -// -// PERFORMANCE -// -// In two benchmarks (oracle and godoc), HVN eliminates about two thirds -// of nodes, the majority accounted for by non-pointers: nodes of -// non-pointer type, pointers that remain nil, formal parameters of dead -// functions, nodes of untracked types, etc. It also reduces the number -// of constraints, also by about two thirds, and the solving time by -// 30--42%, although we must pay about 15% for the running time of HVN -// itself. The benefit is greater for larger applications. -// -// There are many possible optimizations to improve the performance: -// * Use fewer than 1:1 onodes to main graph nodes: many of the onodes -// we create are not needed. -// * HU (HVN with Union---see paper): coalesce "union" peLabels when -// their expanded-out sets are equal. -// * HR (HVN with deReference---see paper): this will require that we -// apply HVN until fixed point, which may need more bookkeeping of the -// correspondance of main nodes to onodes. -// * Location Equivalence (see paper): have points-to sets contain not -// locations but location-equivalence class labels, each representing -// a set of locations. -// * HVN with field-sensitive ref: model each of the fields of a -// pointer-to-struct. -// -// CROSS-CHECK -// -// To verify the soundness of the optimization, when the -// debugHVNCrossCheck option is enabled, we run the solver twice, once -// before and once after running HVN, dumping the solution to disk, and -// then we compare the results. If they are not identical, the analysis -// panics. -// -// The solution dumped to disk includes only the N*N submatrix of the -// complete solution where N is the number of nodes after generation. -// In other words, we ignore pointer variables and objects created by -// the solver itself, since their numbering depends on the solver order, -// which is affected by the optimization. In any case, that's the only -// part the client cares about. -// -// The cross-check is too strict and may fail spuriously. Although the -// H&L paper describing HVN states that the solutions obtained should be -// identical, this is not the case in practice because HVN can collapse -// cycles involving *p even when pts(p)={}. Consider this example -// distilled from testdata/hello.go: -// -// var x T -// func f(p **T) { -// t0 = *p -// ... -// t1 = φ(t0, &x) -// *p = t1 -// } -// -// If f is dead code, we get: -// unoptimized: pts(p)={} pts(t0)={} pts(t1)={&x} -// optimized: pts(p)={} pts(t0)=pts(t1)=pts(*p)={&x} -// -// It's hard to argue that this is a bug: the result is sound and the -// loss of precision is inconsequential---f is dead code, after all. -// But unfortunately it limits the usefulness of the cross-check since -// failures must be carefully analyzed. Ben Hardekopf suggests (in -// personal correspondence) some approaches to mitigating it: -// -// If there is a node with an HVN points-to set that is a superset -// of the NORM points-to set, then either it's a bug or it's a -// result of this issue. If it's a result of this issue, then in -// the offline constraint graph there should be a REF node inside -// some cycle that reaches this node, and in the NORM solution the -// pointer being dereferenced by that REF node should be the empty -// set. If that isn't true then this is a bug. If it is true, then -// you can further check that in the NORM solution the "extra" -// points-to info in the HVN solution does in fact come from that -// purported cycle (if it doesn't, then this is still a bug). If -// you're doing the further check then you'll need to do it for -// each "extra" points-to element in the HVN points-to set. -// -// There are probably ways to optimize these checks by taking -// advantage of graph properties. For example, extraneous points-to -// info will flow through the graph and end up in many -// nodes. Rather than checking every node with extra info, you -// could probably work out the "origin point" of the extra info and -// just check there. Note that the check in the first bullet is -// looking for soundness bugs, while the check in the second bullet -// is looking for precision bugs; depending on your needs, you may -// care more about one than the other. -// -// which we should evaluate. The cross-check is nonetheless invaluable -// for all but one of the programs in the pointer_test suite. - -import ( - "fmt" - "io" - "reflect" - - "golang.org/x/tools/container/intsets" - "golang.org/x/tools/go/types" -) - -// A peLabel is a pointer-equivalence label: two nodes with the same -// peLabel have identical points-to solutions. -// -// The numbers are allocated consecutively like so: -// 0 not a pointer -// 1..N-1 addrConstraints (equals the constraint's .src field, hence sparse) -// ... offsetAddr constraints -// ... SCCs (with indirect nodes or multiple inputs) -// -// Each PE label denotes a set of pointers containing a single addr, a -// single offsetAddr, or some set of other PE labels. -// -type peLabel int - -type hvn struct { - a *analysis - N int // len(a.nodes) immediately after constraint generation - log io.Writer // (optional) log of HVN lemmas - onodes []*onode // nodes of the offline graph - label peLabel // the next available PE label - hvnLabel map[string]peLabel // hash-value numbering (PE label) for each set of onodeids - stack []onodeid // DFS stack - index int32 // next onode.index, from Tarjan's SCC algorithm - - // For each distinct offsetAddrConstraint (src, offset) pair, - // offsetAddrLabels records a unique PE label >= N. - offsetAddrLabels map[offsetAddr]peLabel -} - -// The index of an node in the offline graph. -// (Currently the first N align with the main nodes, -// but this may change with HRU.) -type onodeid uint32 - -// An onode is a node in the offline constraint graph. -// (Where ambiguous, members of analysis.nodes are referred to as -// "main graph" nodes.) -// -// Edges in the offline constraint graph (edges and implicit) point to -// the source, i.e. against the flow of values: they are dependencies. -// Implicit edges are used for SCC computation, but not for gathering -// incoming labels. -// -type onode struct { - rep onodeid // index of representative of SCC in offline constraint graph - - edges intsets.Sparse // constraint edges X-->Y (this onode is X) - implicit intsets.Sparse // implicit edges *X-->*Y (this onode is X) - peLabels intsets.Sparse // set of peLabels are pointer-equivalent to this one - indirect bool // node has points-to relations not represented in graph - - // Tarjan's SCC algorithm - index, lowlink int32 // Tarjan numbering - scc int32 // -ve => on stack; 0 => unvisited; +ve => node is root of a found SCC -} - -type offsetAddr struct { - ptr nodeid - offset uint32 -} - -// nextLabel issues the next unused pointer-equivalence label. -func (h *hvn) nextLabel() peLabel { - h.label++ - return h.label -} - -// ref(X) returns the index of the onode for *X. -func (h *hvn) ref(id onodeid) onodeid { - return id + onodeid(len(h.a.nodes)) -} - -// hvn computes pointer-equivalence labels (peLabels) using the Hash-based -// Value Numbering (HVN) algorithm described in Hardekopf & Lin, SAS'07. -// -func (a *analysis) hvn() { - start("HVN") - - if a.log != nil { - fmt.Fprintf(a.log, "\n\n==== Pointer equivalence optimization\n\n") - } - - h := hvn{ - a: a, - N: len(a.nodes), - log: a.log, - hvnLabel: make(map[string]peLabel), - offsetAddrLabels: make(map[offsetAddr]peLabel), - } - - if h.log != nil { - fmt.Fprintf(h.log, "\nCreating offline graph nodes...\n") - } - - // Create offline nodes. The first N nodes correspond to main - // graph nodes; the next N are their corresponding ref() nodes. - h.onodes = make([]*onode, 2*h.N) - for id := range a.nodes { - id := onodeid(id) - h.onodes[id] = &onode{} - h.onodes[h.ref(id)] = &onode{indirect: true} - } - - // Each node initially represents just itself. - for id, o := range h.onodes { - o.rep = onodeid(id) - } - - h.markIndirectNodes() - - // Reserve the first N PE labels for addrConstraints. - h.label = peLabel(h.N) - - // Add offline constraint edges. - if h.log != nil { - fmt.Fprintf(h.log, "\nAdding offline graph edges...\n") - } - for _, c := range a.constraints { - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "; %s\n", c) - } - c.presolve(&h) - } - - // Find and collapse SCCs. - if h.log != nil { - fmt.Fprintf(h.log, "\nFinding SCCs...\n") - } - h.index = 1 - for id, o := range h.onodes { - if id > 0 && o.index == 0 { - // Start depth-first search at each unvisited node. - h.visit(onodeid(id)) - } - } - - // Dump the solution - // (NB: somewhat redundant with logging from simplify().) - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\nPointer equivalences:\n") - for id, o := range h.onodes { - if id == 0 { - continue - } - if id == int(h.N) { - fmt.Fprintf(h.log, "---\n") - } - fmt.Fprintf(h.log, "o%d\t", id) - if o.rep != onodeid(id) { - fmt.Fprintf(h.log, "rep=o%d", o.rep) - } else { - fmt.Fprintf(h.log, "p%d", o.peLabels.Min()) - if o.indirect { - fmt.Fprint(h.log, " indirect") - } - } - fmt.Fprintln(h.log) - } - } - - // Simplify the main constraint graph - h.simplify() - - a.showCounts() - - stop("HVN") -} - -// ---- constraint-specific rules ---- - -// dst := &src -func (c *addrConstraint) presolve(h *hvn) { - // Each object (src) is an initial PE label. - label := peLabel(c.src) // label < N - if debugHVNVerbose && h.log != nil { - // duplicate log messages are possible - fmt.Fprintf(h.log, "\tcreate p%d: {&n%d}\n", label, c.src) - } - odst := onodeid(c.dst) - osrc := onodeid(c.src) - - // Assign dst this label. - h.onodes[odst].peLabels.Insert(int(label)) - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d has p%d\n", odst, label) - } - - h.addImplicitEdge(h.ref(odst), osrc) // *dst ~~> src. -} - -// dst = src -func (c *copyConstraint) presolve(h *hvn) { - odst := onodeid(c.dst) - osrc := onodeid(c.src) - h.addEdge(odst, osrc) // dst --> src - h.addImplicitEdge(h.ref(odst), h.ref(osrc)) // *dst ~~> *src -} - -// dst = *src + offset -func (c *loadConstraint) presolve(h *hvn) { - odst := onodeid(c.dst) - osrc := onodeid(c.src) - if c.offset == 0 { - h.addEdge(odst, h.ref(osrc)) // dst --> *src - } else { - // We don't interpret load-with-offset, e.g. results - // of map value lookup, R-block of dynamic call, slice - // copy/append, reflection. - h.markIndirect(odst, "load with offset") - } -} - -// *dst + offset = src -func (c *storeConstraint) presolve(h *hvn) { - odst := onodeid(c.dst) - osrc := onodeid(c.src) - if c.offset == 0 { - h.onodes[h.ref(odst)].edges.Insert(int(osrc)) // *dst --> src - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d --> o%d\n", h.ref(odst), osrc) - } - } else { - // We don't interpret store-with-offset. - // See discussion of soundness at markIndirectNodes. - } -} - -// dst = &src.offset -func (c *offsetAddrConstraint) presolve(h *hvn) { - // Give each distinct (addr, offset) pair a fresh PE label. - // The cache performs CSE, effectively. - key := offsetAddr{c.src, c.offset} - label, ok := h.offsetAddrLabels[key] - if !ok { - label = h.nextLabel() - h.offsetAddrLabels[key] = label - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tcreate p%d: {&n%d.#%d}\n", - label, c.src, c.offset) - } - } - - // Assign dst this label. - h.onodes[c.dst].peLabels.Insert(int(label)) - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d has p%d\n", c.dst, label) - } -} - -// dst = src.(typ) where typ is an interface -func (c *typeFilterConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.dst), "typeFilter result") -} - -// dst = src.(typ) where typ is concrete -func (c *untagConstraint) presolve(h *hvn) { - odst := onodeid(c.dst) - for end := odst + onodeid(h.a.sizeof(c.typ)); odst < end; odst++ { - h.markIndirect(odst, "untag result") - } -} - -// dst = src.method(c.params...) -func (c *invokeConstraint) presolve(h *hvn) { - // All methods are address-taken functions, so - // their formal P-blocks were already marked indirect. - - // Mark the caller's targets node as indirect. - sig := c.method.Type().(*types.Signature) - id := c.params - h.markIndirect(onodeid(c.params), "invoke targets node") - id++ - - id += nodeid(h.a.sizeof(sig.Params())) - - // Mark the caller's R-block as indirect. - end := id + nodeid(h.a.sizeof(sig.Results())) - for id < end { - h.markIndirect(onodeid(id), "invoke R-block") - id++ - } -} - -// markIndirectNodes marks as indirect nodes whose points-to relations -// are not entirely captured by the offline graph, including: -// -// (a) All address-taken nodes (including the following nodes within -// the same object). This is described in the paper. -// -// The most subtle cause of indirect nodes is the generation of -// store-with-offset constraints since the offline graph doesn't -// represent them. A global audit of constraint generation reveals the -// following uses of store-with-offset: -// -// (b) genDynamicCall, for P-blocks of dynamically called functions, -// to which dynamic copy edges will be added to them during -// solving: from storeConstraint for standalone functions, -// and from invokeConstraint for methods. -// All such P-blocks must be marked indirect. -// (c) MakeUpdate, to update the value part of a map object. -// All MakeMap objects's value parts must be marked indirect. -// (d) copyElems, to update the destination array. -// All array elements must be marked indirect. -// -// Not all indirect marking happens here. ref() nodes are marked -// indirect at construction, and each constraint's presolve() method may -// mark additional nodes. -// -func (h *hvn) markIndirectNodes() { - // (a) all address-taken nodes, plus all nodes following them - // within the same object, since these may be indirectly - // stored or address-taken. - for _, c := range h.a.constraints { - if c, ok := c.(*addrConstraint); ok { - start := h.a.enclosingObj(c.src) - end := start + nodeid(h.a.nodes[start].obj.size) - for id := c.src; id < end; id++ { - h.markIndirect(onodeid(id), "A-T object") - } - } - } - - // (b) P-blocks of all address-taken functions. - for id := 0; id < h.N; id++ { - obj := h.a.nodes[id].obj - - // TODO(adonovan): opt: if obj.cgn.fn is a method and - // obj.cgn is not its shared contour, this is an - // "inlined" static method call. We needn't consider it - // address-taken since no invokeConstraint will affect it. - - if obj != nil && obj.flags&otFunction != 0 && h.a.atFuncs[obj.cgn.fn] { - // address-taken function - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "n%d is address-taken: %s\n", id, obj.cgn.fn) - } - h.markIndirect(onodeid(id), "A-T func identity") - id++ - sig := obj.cgn.fn.Signature - psize := h.a.sizeof(sig.Params()) - if sig.Recv() != nil { - psize += h.a.sizeof(sig.Recv().Type()) - } - for end := id + int(psize); id < end; id++ { - h.markIndirect(onodeid(id), "A-T func P-block") - } - id-- - continue - } - } - - // (c) all map objects' value fields. - for _, id := range h.a.mapValues { - h.markIndirect(onodeid(id), "makemap.value") - } - - // (d) all array element objects. - // TODO(adonovan): opt: can we do better? - for id := 0; id < h.N; id++ { - // Identity node for an object of array type? - if tArray, ok := h.a.nodes[id].typ.(*types.Array); ok { - // Mark the array element nodes indirect. - // (Skip past the identity field.) - for _ = range h.a.flatten(tArray.Elem()) { - id++ - h.markIndirect(onodeid(id), "array elem") - } - } - } -} - -func (h *hvn) markIndirect(oid onodeid, comment string) { - h.onodes[oid].indirect = true - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d is indirect: %s\n", oid, comment) - } -} - -// Adds an edge dst-->src. -// Note the unusual convention: edges are dependency (contraflow) edges. -func (h *hvn) addEdge(odst, osrc onodeid) { - h.onodes[odst].edges.Insert(int(osrc)) - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d --> o%d\n", odst, osrc) - } -} - -func (h *hvn) addImplicitEdge(odst, osrc onodeid) { - h.onodes[odst].implicit.Insert(int(osrc)) - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d ~~> o%d\n", odst, osrc) - } -} - -// visit implements the depth-first search of Tarjan's SCC algorithm. -// Precondition: x is canonical. -func (h *hvn) visit(x onodeid) { - h.checkCanonical(x) - xo := h.onodes[x] - xo.index = h.index - xo.lowlink = h.index - h.index++ - - h.stack = append(h.stack, x) // push - assert(xo.scc == 0, "node revisited") - xo.scc = -1 - - var deps []int - deps = xo.edges.AppendTo(deps) - deps = xo.implicit.AppendTo(deps) - - for _, y := range deps { - // Loop invariant: x is canonical. - - y := h.find(onodeid(y)) - - if x == y { - continue // nodes already coalesced - } - - xo := h.onodes[x] - yo := h.onodes[y] - - switch { - case yo.scc > 0: - // y is already a collapsed SCC - - case yo.scc < 0: - // y is on the stack, and thus in the current SCC. - if yo.index < xo.lowlink { - xo.lowlink = yo.index - } - - default: - // y is unvisited; visit it now. - h.visit(y) - // Note: x and y are now non-canonical. - - x = h.find(onodeid(x)) - - if yo.lowlink < xo.lowlink { - xo.lowlink = yo.lowlink - } - } - } - h.checkCanonical(x) - - // Is x the root of an SCC? - if xo.lowlink == xo.index { - // Coalesce all nodes in the SCC. - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "scc o%d\n", x) - } - for { - // Pop y from stack. - i := len(h.stack) - 1 - y := h.stack[i] - h.stack = h.stack[:i] - - h.checkCanonical(x) - xo := h.onodes[x] - h.checkCanonical(y) - yo := h.onodes[y] - - if xo == yo { - // SCC is complete. - xo.scc = 1 - h.labelSCC(x) - break - } - h.coalesce(x, y) - } - } -} - -// Precondition: x is canonical. -func (h *hvn) labelSCC(x onodeid) { - h.checkCanonical(x) - xo := h.onodes[x] - xpe := &xo.peLabels - - // All indirect nodes get new labels. - if xo.indirect { - label := h.nextLabel() - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tcreate p%d: indirect SCC\n", label) - fmt.Fprintf(h.log, "\to%d has p%d\n", x, label) - } - - // Remove pre-labeling, in case a direct pre-labeled node was - // merged with an indirect one. - xpe.Clear() - xpe.Insert(int(label)) - - return - } - - // Invariant: all peLabels sets are non-empty. - // Those that are logically empty contain zero as their sole element. - // No other sets contains zero. - - // Find all labels coming in to the coalesced SCC node. - for _, y := range xo.edges.AppendTo(nil) { - y := h.find(onodeid(y)) - if y == x { - continue // already coalesced - } - ype := &h.onodes[y].peLabels - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tedge from o%d = %s\n", y, ype) - } - - if ype.IsEmpty() { - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tnode has no PE label\n") - } - } - assert(!ype.IsEmpty(), "incoming node has no PE label") - - if ype.Has(0) { - // {0} represents a non-pointer. - assert(ype.Len() == 1, "PE set contains {0, ...}") - } else { - xpe.UnionWith(ype) - } - } - - switch xpe.Len() { - case 0: - // SCC has no incoming non-zero PE labels: it is a non-pointer. - xpe.Insert(0) - - case 1: - // already a singleton - - default: - // SCC has multiple incoming non-zero PE labels. - // Find the canonical label representing this set. - // We use String() as a fingerprint consistent with Equals(). - key := xpe.String() - label, ok := h.hvnLabel[key] - if !ok { - label = h.nextLabel() - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tcreate p%d: union %s\n", label, xpe.String()) - } - h.hvnLabel[key] = label - } - xpe.Clear() - xpe.Insert(int(label)) - } - - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\to%d has p%d\n", x, xpe.Min()) - } -} - -// coalesce combines two nodes in the offline constraint graph. -// Precondition: x and y are canonical. -func (h *hvn) coalesce(x, y onodeid) { - xo := h.onodes[x] - yo := h.onodes[y] - - // x becomes y's canonical representative. - yo.rep = x - - if debugHVNVerbose && h.log != nil { - fmt.Fprintf(h.log, "\tcoalesce o%d into o%d\n", y, x) - } - - // x accumulates y's edges. - xo.edges.UnionWith(&yo.edges) - yo.edges.Clear() - - // x accumulates y's implicit edges. - xo.implicit.UnionWith(&yo.implicit) - yo.implicit.Clear() - - // x accumulates y's pointer-equivalence labels. - xo.peLabels.UnionWith(&yo.peLabels) - yo.peLabels.Clear() - - // x accumulates y's indirect flag. - if yo.indirect { - xo.indirect = true - } -} - -// simplify computes a degenerate renumbering of nodeids from the PE -// labels assigned by the hvn, and uses it to simplify the main -// constraint graph, eliminating non-pointer nodes and duplicate -// constraints. -// -func (h *hvn) simplify() { - // canon maps each peLabel to its canonical main node. - canon := make([]nodeid, h.label) - for i := range canon { - canon[i] = nodeid(h.N) // indicates "unset" - } - - // mapping maps each main node index to the index of the canonical node. - mapping := make([]nodeid, len(h.a.nodes)) - - for id := range h.a.nodes { - id := nodeid(id) - if id == 0 { - canon[0] = 0 - mapping[0] = 0 - continue - } - oid := h.find(onodeid(id)) - peLabels := &h.onodes[oid].peLabels - assert(peLabels.Len() == 1, "PE class is not a singleton") - label := peLabel(peLabels.Min()) - - canonId := canon[label] - if canonId == nodeid(h.N) { - // id becomes the representative of the PE label. - canonId = id - canon[label] = canonId - - if h.a.log != nil { - fmt.Fprintf(h.a.log, "\tpts(n%d) is canonical : \t(%s)\n", - id, h.a.nodes[id].typ) - } - - } else { - // Link the solver states for the two nodes. - assert(h.a.nodes[canonId].solve != nil, "missing solver state") - h.a.nodes[id].solve = h.a.nodes[canonId].solve - - if h.a.log != nil { - // TODO(adonovan): debug: reorganize the log so it prints - // one line: - // pe y = x1, ..., xn - // for each canonical y. Requires allocation. - fmt.Fprintf(h.a.log, "\tpts(n%d) = pts(n%d) : %s\n", - id, canonId, h.a.nodes[id].typ) - } - } - - mapping[id] = canonId - } - - // Renumber the constraints, eliminate duplicates, and eliminate - // any containing non-pointers (n0). - addrs := make(map[addrConstraint]bool) - copys := make(map[copyConstraint]bool) - loads := make(map[loadConstraint]bool) - stores := make(map[storeConstraint]bool) - offsetAddrs := make(map[offsetAddrConstraint]bool) - untags := make(map[untagConstraint]bool) - typeFilters := make(map[typeFilterConstraint]bool) - invokes := make(map[invokeConstraint]bool) - - nbefore := len(h.a.constraints) - cc := h.a.constraints[:0] // in-situ compaction - for _, c := range h.a.constraints { - // Renumber. - switch c := c.(type) { - case *addrConstraint: - // Don't renumber c.src since it is the label of - // an addressable object and will appear in PT sets. - c.dst = mapping[c.dst] - default: - c.renumber(mapping) - } - - if c.ptr() == 0 { - continue // skip: constraint attached to non-pointer - } - - var dup bool - switch c := c.(type) { - case *addrConstraint: - _, dup = addrs[*c] - addrs[*c] = true - - case *copyConstraint: - if c.src == c.dst { - continue // skip degenerate copies - } - if c.src == 0 { - continue // skip copy from non-pointer - } - _, dup = copys[*c] - copys[*c] = true - - case *loadConstraint: - if c.src == 0 { - continue // skip load from non-pointer - } - _, dup = loads[*c] - loads[*c] = true - - case *storeConstraint: - if c.src == 0 { - continue // skip store from non-pointer - } - _, dup = stores[*c] - stores[*c] = true - - case *offsetAddrConstraint: - if c.src == 0 { - continue // skip offset from non-pointer - } - _, dup = offsetAddrs[*c] - offsetAddrs[*c] = true - - case *untagConstraint: - if c.src == 0 { - continue // skip untag of non-pointer - } - _, dup = untags[*c] - untags[*c] = true - - case *typeFilterConstraint: - if c.src == 0 { - continue // skip filter of non-pointer - } - _, dup = typeFilters[*c] - typeFilters[*c] = true - - case *invokeConstraint: - if c.params == 0 { - panic("non-pointer invoke.params") - } - if c.iface == 0 { - continue // skip invoke on non-pointer - } - _, dup = invokes[*c] - invokes[*c] = true - - default: - // We don't bother de-duping advanced constraints - // (e.g. reflection) since they are uncommon. - - // Eliminate constraints containing non-pointer nodeids. - // - // We use reflection to find the fields to avoid - // adding yet another method to constraint. - // - // TODO(adonovan): experiment with a constraint - // method that returns a slice of pointers to - // nodeids fields to enable uniform iteration; - // the renumber() method could be removed and - // implemented using the new one. - // - // TODO(adonovan): opt: this is unsound since - // some constraints still have an effect if one - // of the operands is zero: rVCall, rVMapIndex, - // rvSetMapIndex. Handle them specially. - rtNodeid := reflect.TypeOf(nodeid(0)) - x := reflect.ValueOf(c).Elem() - for i, nf := 0, x.NumField(); i < nf; i++ { - f := x.Field(i) - if f.Type() == rtNodeid { - if f.Uint() == 0 { - dup = true // skip it - break - } - } - } - } - if dup { - continue // skip duplicates - } - - cc = append(cc, c) - } - h.a.constraints = cc - - if h.log != nil { - fmt.Fprintf(h.log, "#constraints: was %d, now %d\n", nbefore, len(h.a.constraints)) - } -} - -// find returns the canonical onodeid for x. -// (The onodes form a disjoint set forest.) -func (h *hvn) find(x onodeid) onodeid { - // TODO(adonovan): opt: this is a CPU hotspot. Try "union by rank". - xo := h.onodes[x] - rep := xo.rep - if rep != x { - rep = h.find(rep) // simple path compression - xo.rep = rep - } - return rep -} - -func (h *hvn) checkCanonical(x onodeid) { - if debugHVN { - assert(x == h.find(x), "not canonical") - } -} - -func assert(p bool, msg string) { - if debugHVN && !p { - panic("assertion failed: " + msg) - } -} diff --git a/go/pointer/intrinsics14.go b/go/pointer/intrinsics14.go deleted file mode 100644 index 5e108d27e1..0000000000 --- a/go/pointer/intrinsics14.go +++ /dev/null @@ -1,382 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This package defines the treatment of intrinsics, i.e. library -// functions requiring special analytical treatment. -// -// Most of these are C or assembly functions, but even some Go -// functions require may special treatment if the analysis completely -// replaces the implementation of an API such as reflection. - -// TODO(adonovan): support a means of writing analytic summaries in -// the target code, so that users can summarise the effects of their -// own C functions using a snippet of Go. - -import ( - "fmt" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -// Instances of 'intrinsic' generate analysis constraints for calls to -// intrinsic functions. -// Implementations may exploit information from the calling site -// via cgn.callersite; for shared contours this is nil. -type intrinsic func(a *analysis, cgn *cgnode) - -// Initialized in explicit init() to defeat (spurious) initialization -// cycle error. -var intrinsicsByName = make(map[string]intrinsic) - -func init() { - // Key strings are from Function.String(). - // That little dot ۰ is an Arabic zero numeral (U+06F0), - // categories [Nd]. - for name, fn := range map[string]intrinsic{ - // Other packages. - "bytes.Equal": ext۰NoEffect, - "bytes.IndexByte": ext۰NoEffect, - "crypto/aes.decryptBlockAsm": ext۰NoEffect, - "crypto/aes.encryptBlockAsm": ext۰NoEffect, - "crypto/aes.expandKeyAsm": ext۰NoEffect, - "crypto/aes.hasAsm": ext۰NoEffect, - "crypto/md5.block": ext۰NoEffect, - "crypto/rc4.xorKeyStream": ext۰NoEffect, - "crypto/sha1.block": ext۰NoEffect, - "crypto/sha256.block": ext۰NoEffect, - "hash/crc32.castagnoliSSE42": ext۰NoEffect, - "hash/crc32.haveSSE42": ext۰NoEffect, - "math.Abs": ext۰NoEffect, - "math.Acos": ext۰NoEffect, - "math.Asin": ext۰NoEffect, - "math.Atan": ext۰NoEffect, - "math.Atan2": ext۰NoEffect, - "math.Ceil": ext۰NoEffect, - "math.Cos": ext۰NoEffect, - "math.Dim": ext۰NoEffect, - "math.Exp": ext۰NoEffect, - "math.Exp2": ext۰NoEffect, - "math.Expm1": ext۰NoEffect, - "math.Float32bits": ext۰NoEffect, - "math.Float32frombits": ext۰NoEffect, - "math.Float64bits": ext۰NoEffect, - "math.Float64frombits": ext۰NoEffect, - "math.Floor": ext۰NoEffect, - "math.Frexp": ext۰NoEffect, - "math.Hypot": ext۰NoEffect, - "math.Ldexp": ext۰NoEffect, - "math.Log": ext۰NoEffect, - "math.Log10": ext۰NoEffect, - "math.Log1p": ext۰NoEffect, - "math.Log2": ext۰NoEffect, - "math.Max": ext۰NoEffect, - "math.Min": ext۰NoEffect, - "math.Mod": ext۰NoEffect, - "math.Modf": ext۰NoEffect, - "math.Remainder": ext۰NoEffect, - "math.Sin": ext۰NoEffect, - "math.Sincos": ext۰NoEffect, - "math.Sqrt": ext۰NoEffect, - "math.Tan": ext۰NoEffect, - "math.Trunc": ext۰NoEffect, - "math/big.addMulVVW": ext۰NoEffect, - "math/big.addVV": ext۰NoEffect, - "math/big.addVW": ext۰NoEffect, - "math/big.bitLen": ext۰NoEffect, - "math/big.divWVW": ext۰NoEffect, - "math/big.divWW": ext۰NoEffect, - "math/big.mulAddVWW": ext۰NoEffect, - "math/big.mulWW": ext۰NoEffect, - "math/big.shlVU": ext۰NoEffect, - "math/big.shrVU": ext۰NoEffect, - "math/big.subVV": ext۰NoEffect, - "math/big.subVW": ext۰NoEffect, - "net.runtime_Semacquire": ext۰NoEffect, - "net.runtime_Semrelease": ext۰NoEffect, - "net.runtime_pollClose": ext۰NoEffect, - "net.runtime_pollOpen": ext۰NoEffect, - "net.runtime_pollReset": ext۰NoEffect, - "net.runtime_pollServerInit": ext۰NoEffect, - "net.runtime_pollSetDeadline": ext۰NoEffect, - "net.runtime_pollUnblock": ext۰NoEffect, - "net.runtime_pollWait": ext۰NoEffect, - "net.runtime_pollWaitCanceled": ext۰NoEffect, - "os.epipecheck": ext۰NoEffect, - "runtime.BlockProfile": ext۰NoEffect, - "runtime.Breakpoint": ext۰NoEffect, - "runtime.CPUProfile": ext۰NoEffect, // good enough - "runtime.Caller": ext۰NoEffect, - "runtime.Callers": ext۰NoEffect, // good enough - "runtime.FuncForPC": ext۰NoEffect, - "runtime.GC": ext۰NoEffect, - "runtime.GOMAXPROCS": ext۰NoEffect, - "runtime.Goexit": ext۰NoEffect, - "runtime.GoroutineProfile": ext۰NoEffect, - "runtime.Gosched": ext۰NoEffect, - "runtime.MemProfile": ext۰NoEffect, - "runtime.NumCPU": ext۰NoEffect, - "runtime.NumGoroutine": ext۰NoEffect, - "runtime.ReadMemStats": ext۰NoEffect, - "runtime.SetBlockProfileRate": ext۰NoEffect, - "runtime.SetCPUProfileRate": ext۰NoEffect, - "runtime.SetFinalizer": ext۰runtime۰SetFinalizer, - "runtime.Stack": ext۰NoEffect, - "runtime.ThreadCreateProfile": ext۰NoEffect, - "runtime.cstringToGo": ext۰NoEffect, - "runtime.funcentry_go": ext۰NoEffect, - "runtime.funcline_go": ext۰NoEffect, - "runtime.funcname_go": ext۰NoEffect, - "runtime.getgoroot": ext۰NoEffect, - "runtime/pprof.runtime_cyclesPerSecond": ext۰NoEffect, - "strings.IndexByte": ext۰NoEffect, - "sync.runtime_Semacquire": ext۰NoEffect, - "sync.runtime_Semrelease": ext۰NoEffect, - "sync.runtime_Syncsemacquire": ext۰NoEffect, - "sync.runtime_Syncsemcheck": ext۰NoEffect, - "sync.runtime_Syncsemrelease": ext۰NoEffect, - "sync.runtime_procPin": ext۰NoEffect, - "sync.runtime_procUnpin": ext۰NoEffect, - "sync.runtime_registerPool": ext۰NoEffect, - "sync/atomic.AddInt32": ext۰NoEffect, - "sync/atomic.AddInt64": ext۰NoEffect, - "sync/atomic.AddUint32": ext۰NoEffect, - "sync/atomic.AddUint64": ext۰NoEffect, - "sync/atomic.AddUintptr": ext۰NoEffect, - "sync/atomic.CompareAndSwapInt32": ext۰NoEffect, - "sync/atomic.CompareAndSwapUint32": ext۰NoEffect, - "sync/atomic.CompareAndSwapUint64": ext۰NoEffect, - "sync/atomic.CompareAndSwapUintptr": ext۰NoEffect, - "sync/atomic.LoadInt32": ext۰NoEffect, - "sync/atomic.LoadInt64": ext۰NoEffect, - "sync/atomic.LoadPointer": ext۰NoEffect, // ignore unsafe.Pointers - "sync/atomic.LoadUint32": ext۰NoEffect, - "sync/atomic.LoadUint64": ext۰NoEffect, - "sync/atomic.LoadUintptr": ext۰NoEffect, - "sync/atomic.StoreInt32": ext۰NoEffect, - "sync/atomic.StorePointer": ext۰NoEffect, // ignore unsafe.Pointers - "sync/atomic.StoreUint32": ext۰NoEffect, - "sync/atomic.StoreUintptr": ext۰NoEffect, - "syscall.Close": ext۰NoEffect, - "syscall.Exit": ext۰NoEffect, - "syscall.Getpid": ext۰NoEffect, - "syscall.Getwd": ext۰NoEffect, - "syscall.Kill": ext۰NoEffect, - "syscall.RawSyscall": ext۰NoEffect, - "syscall.RawSyscall6": ext۰NoEffect, - "syscall.Syscall": ext۰NoEffect, - "syscall.Syscall6": ext۰NoEffect, - "syscall.runtime_AfterFork": ext۰NoEffect, - "syscall.runtime_BeforeFork": ext۰NoEffect, - "syscall.setenv_c": ext۰NoEffect, - "time.Sleep": ext۰NoEffect, - "time.now": ext۰NoEffect, - "time.startTimer": ext۰time۰startTimer, - "time.stopTimer": ext۰NoEffect, - } { - intrinsicsByName[name] = fn - } -} - -// findIntrinsic returns the constraint generation function for an -// intrinsic function fn, or nil if the function should be handled normally. -// -func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic { - // Consult the *Function-keyed cache. - // A cached nil indicates a normal non-intrinsic function. - impl, ok := a.intrinsics[fn] - if !ok { - impl = intrinsicsByName[fn.String()] // may be nil - - if a.isReflect(fn) { - if !a.config.Reflection { - impl = ext۰NoEffect // reflection disabled - } else if impl == nil { - // Ensure all "reflect" code is treated intrinsically. - impl = ext۰NotYetImplemented - } - } - - a.intrinsics[fn] = impl - } - return impl -} - -// isReflect reports whether fn belongs to the "reflect" package. -func (a *analysis) isReflect(fn *ssa.Function) bool { - if a.reflectValueObj == nil { - return false // "reflect" package not loaded - } - reflectPackage := a.reflectValueObj.Pkg() - if fn.Pkg != nil && fn.Pkg.Pkg == reflectPackage { - return true - } - // Synthetic wrappers have a nil Pkg, so they slip through the - // previous check. Check the receiver package. - // TODO(adonovan): should synthetic wrappers have a non-nil Pkg? - if recv := fn.Signature.Recv(); recv != nil { - if named, ok := deref(recv.Type()).(*types.Named); ok { - if named.Obj().Pkg() == reflectPackage { - return true // e.g. wrapper of (reflect.Value).f - } - } - } - return false -} - -// A trivial intrinsic suitable for any function that does not: -// 1) induce aliases between its arguments or any global variables; -// 2) call any functions; or -// 3) create any labels. -// -// Many intrinsics (such as CompareAndSwapInt32) have a fourth kind of -// effect: loading or storing through a pointer. Though these could -// be significant, we deliberately ignore them because they are -// generally not worth the effort. -// -// We sometimes violate condition #3 if the function creates only -// non-function labels, as the control-flow graph is still sound. -// -func ext۰NoEffect(a *analysis, cgn *cgnode) {} - -func ext۰NotYetImplemented(a *analysis, cgn *cgnode) { - fn := cgn.fn - a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn) -} - -// ---------- func runtime.SetFinalizer(x, f interface{}) ---------- - -// runtime.SetFinalizer(x, f) -type runtimeSetFinalizerConstraint struct { - targets nodeid // (indirect) - f nodeid // (ptr) - x nodeid -} - -func (c *runtimeSetFinalizerConstraint) ptr() nodeid { return c.f } -func (c *runtimeSetFinalizerConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.targets), "SetFinalizer.targets") -} -func (c *runtimeSetFinalizerConstraint) renumber(mapping []nodeid) { - c.targets = mapping[c.targets] - c.f = mapping[c.f] - c.x = mapping[c.x] -} - -func (c *runtimeSetFinalizerConstraint) String() string { - return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f) -} - -func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) { - for _, fObj := range delta.AppendTo(a.deltaSpace) { - tDyn, f, indirect := a.taggedValue(nodeid(fObj)) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - tSig, ok := tDyn.Underlying().(*types.Signature) - if !ok { - continue // not a function - } - if tSig.Recv() != nil { - panic(tSig) - } - if tSig.Params().Len() != 1 { - continue // not a unary function - } - - // Extract x to tmp. - tx := tSig.Params().At(0).Type() - tmp := a.addNodes(tx, "SetFinalizer.tmp") - a.typeAssert(tx, tmp, c.x, false) - - // Call f(tmp). - a.store(f, tmp, 1, a.sizeof(tx)) - - // Add dynamic call target. - if a.onlineCopy(c.targets, f) { - a.addWork(c.targets) - } - } -} - -func ext۰runtime۰SetFinalizer(a *analysis, cgn *cgnode) { - // This is the shared contour, used for dynamic calls. - targets := a.addOneNode(tInvalid, "SetFinalizer.targets", nil) - cgn.sites = append(cgn.sites, &callsite{targets: targets}) - params := a.funcParams(cgn.obj) - a.addConstraint(&runtimeSetFinalizerConstraint{ - targets: targets, - x: params, - f: params + 1, - }) -} - -// ---------- func time.startTimer(t *runtimeTimer) ---------- - -// time.StartTimer(t) -type timeStartTimerConstraint struct { - targets nodeid // (indirect) - t nodeid // (ptr) -} - -func (c *timeStartTimerConstraint) ptr() nodeid { return c.t } -func (c *timeStartTimerConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.targets), "StartTimer.targets") -} -func (c *timeStartTimerConstraint) renumber(mapping []nodeid) { - c.targets = mapping[c.targets] - c.t = mapping[c.t] -} - -func (c *timeStartTimerConstraint) String() string { - return fmt.Sprintf("time.startTimer(n%d)", c.t) -} - -func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) { - for _, tObj := range delta.AppendTo(a.deltaSpace) { - t := nodeid(tObj) - - // We model startTimer as if it was defined thus: - // func startTimer(t *runtimeTimer) { t.f(t.arg) } - - // We hard-code the field offsets of time.runtimeTimer: - // type runtimeTimer struct { - // 0 __identity__ - // 1 i int32 - // 2 when int64 - // 3 period int64 - // 4 f func(int64, interface{}) - // 5 arg interface{} - // } - f := t + 4 - arg := t + 5 - - // store t.arg to t.f.params[0] - // (offset 1 => skip identity) - a.store(f, arg, 1, 1) - - // Add dynamic call target. - if a.onlineCopy(c.targets, f) { - a.addWork(c.targets) - } - } -} - -func ext۰time۰startTimer(a *analysis, cgn *cgnode) { - // This is the shared contour, used for dynamic calls. - targets := a.addOneNode(tInvalid, "startTimer.targets", nil) - cgn.sites = append(cgn.sites, &callsite{targets: targets}) - params := a.funcParams(cgn.obj) - a.addConstraint(&timeStartTimerConstraint{ - targets: targets, - t: params, - }) -} diff --git a/go/pointer/labels14.go b/go/pointer/labels14.go deleted file mode 100644 index c9ca6a3e37..0000000000 --- a/go/pointer/labels14.go +++ /dev/null @@ -1,154 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -import ( - "fmt" - "go/token" - "strings" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -// A Label is an entity that may be pointed to by a pointer, map, -// channel, 'func', slice or interface. -// -// Labels include: -// - functions -// - globals -// - tagged objects, representing interfaces and reflect.Values -// - arrays created by conversions (e.g. []byte("foo"), []byte(s)) -// - stack- and heap-allocated variables (including composite literals) -// - channels, maps and arrays created by make() -// - intrinsic or reflective operations that allocate (e.g. append, reflect.New) -// - intrinsic objects, e.g. the initial array behind os.Args. -// - and their subelements, e.g. "alloc.y[*].z" -// -// Labels are so varied that they defy good generalizations; -// some have no value, no callgraph node, or no position. -// Many objects have types that are inexpressible in Go: -// maps, channels, functions, tagged objects. -// -// At most one of Value() or ReflectType() may return non-nil. -// -type Label struct { - obj *object // the addressable memory location containing this label - subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c" -} - -// Value returns the ssa.Value that allocated this label's object, if any. -func (l Label) Value() ssa.Value { - val, _ := l.obj.data.(ssa.Value) - return val -} - -// ReflectType returns the type represented by this label if it is an -// reflect.rtype instance object or *reflect.rtype-tagged object. -// -func (l Label) ReflectType() types.Type { - rtype, _ := l.obj.data.(types.Type) - return rtype -} - -// Path returns the path to the subelement of the object containing -// this label. For example, ".x[*].y". -// -func (l Label) Path() string { - return l.subelement.path() -} - -// Pos returns the position of this label, if known, zero otherwise. -func (l Label) Pos() token.Pos { - switch data := l.obj.data.(type) { - case ssa.Value: - return data.Pos() - case types.Type: - if nt, ok := deref(data).(*types.Named); ok { - return nt.Obj().Pos() - } - } - if cgn := l.obj.cgn; cgn != nil { - return cgn.fn.Pos() - } - return token.NoPos -} - -// String returns the printed form of this label. -// -// Examples: Object type: -// x (a variable) -// (sync.Mutex).Lock (a function) -// convert (array created by conversion) -// makemap (map allocated via make) -// makechan (channel allocated via make) -// makeinterface (tagged object allocated by makeinterface) -// (allocation in instrinsic) -// sync.Mutex (a reflect.rtype instance) -// (an intrinsic object) -// -// Labels within compound objects have subelement paths: -// x.y[*].z (a struct variable, x) -// append.y[*].z (array allocated by append) -// makeslice.y[*].z (array allocated via make) -// -// TODO(adonovan): expose func LabelString(*types.Package, Label). -// -func (l Label) String() string { - var s string - switch v := l.obj.data.(type) { - case types.Type: - return v.String() - - case string: - s = v // an intrinsic object (e.g. os.Args[*]) - - case nil: - if l.obj.cgn != nil { - // allocation by intrinsic or reflective operation - s = fmt.Sprintf("", l.obj.cgn.fn) - } else { - s = "" // should be unreachable - } - - case *ssa.Function: - s = v.String() - - case *ssa.Global: - s = v.String() - - case *ssa.Const: - s = v.Name() - - case *ssa.Alloc: - s = v.Comment - if s == "" { - s = "alloc" - } - - case *ssa.Call: - // Currently only calls to append can allocate objects. - if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" { - panic("unhandled *ssa.Call label: " + v.Name()) - } - s = "append" - - case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert: - s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")) - - case *ssa.MakeInterface: - // MakeInterface is usually implicit in Go source (so - // Pos()==0), and tagged objects may be allocated - // synthetically (so no *MakeInterface data). - s = "makeinterface:" + v.X.Type().String() - - default: - panic(fmt.Sprintf("unhandled object data type: %T", v)) - } - - return s + l.subelement.path() -} diff --git a/go/pointer/pointer14_test.go b/go/pointer/pointer14_test.go deleted file mode 100644 index 2bcdd56388..0000000000 --- a/go/pointer/pointer14_test.go +++ /dev/null @@ -1,578 +0,0 @@ -// 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. - -// +build !go1.5 - -// No testdata on Android. - -// +build !android - -package pointer_test - -// This test uses 'expectation' comments embedded within testdata/*.go -// files to specify the expected pointer analysis behaviour. -// See below for grammar. - -import ( - "bytes" - "errors" - "fmt" - "go/token" - "io/ioutil" - "os" - "regexp" - "strconv" - "strings" - "testing" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/pointer" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -var inputs = []string{ - "testdata/a_test.go", - "testdata/another.go", - "testdata/arrayreflect.go", - "testdata/arrays.go", - "testdata/channels.go", - "testdata/chanreflect.go", - "testdata/context.go", - "testdata/conv.go", - "testdata/finalizer.go", - "testdata/flow.go", - "testdata/fmtexcerpt.go", - "testdata/func.go", - "testdata/funcreflect.go", - "testdata/hello.go", // NB: causes spurious failure of HVN cross-check - "testdata/interfaces.go", - "testdata/issue9002.go", - "testdata/mapreflect.go", - "testdata/maps.go", - "testdata/panic.go", - "testdata/recur.go", - "testdata/reflect.go", - "testdata/rtti.go", - "testdata/structreflect.go", - "testdata/structs.go", - "testdata/timer.go", -} - -// Expectation grammar: -// -// @calls f -> g -// -// A 'calls' expectation asserts that edge (f, g) appears in the -// callgraph. f and g are notated as per Function.String(), which -// may contain spaces (e.g. promoted method in anon struct). -// -// @pointsto a | b | c -// -// A 'pointsto' expectation asserts that the points-to set of its -// operand contains exactly the set of labels {a,b,c} notated as per -// labelString. -// -// A 'pointsto' expectation must appear on the same line as a -// print(x) statement; the expectation's operand is x. -// -// If one of the strings is "...", the expectation asserts that the -// points-to set at least the other labels. -// -// We use '|' because label names may contain spaces, e.g. methods -// of anonymous structs. -// -// From a theoretical perspective, concrete types in interfaces are -// labels too, but they are represented differently and so have a -// different expectation, @types, below. -// -// @types t | u | v -// -// A 'types' expectation asserts that the set of possible dynamic -// types of its interface operand is exactly {t,u,v}, notated per -// go/types.Type.String(). In other words, it asserts that the type -// component of the interface may point to that set of concrete type -// literals. It also works for reflect.Value, though the types -// needn't be concrete in that case. -// -// A 'types' expectation must appear on the same line as a -// print(x) statement; the expectation's operand is x. -// -// If one of the strings is "...", the expectation asserts that the -// interface's type may point to at least the other types. -// -// We use '|' because type names may contain spaces. -// -// @warning "regexp" -// -// A 'warning' expectation asserts that the analysis issues a -// warning that matches the regular expression within the string -// literal. -// -// @line id -// -// A line directive associates the name "id" with the current -// file:line. The string form of labels will use this id instead of -// a file:line, making @pointsto expectations more robust against -// perturbations in the source file. -// (NB, anon functions still include line numbers.) -// -type expectation struct { - kind string // "pointsto" | "types" | "calls" | "warning" - filename string - linenum int // source line number, 1-based - args []string - types []types.Type // for types -} - -func (e *expectation) String() string { - return fmt.Sprintf("@%s[%s]", e.kind, strings.Join(e.args, " | ")) -} - -func (e *expectation) errorf(format string, args ...interface{}) { - fmt.Printf("%s:%d: ", e.filename, e.linenum) - fmt.Printf(format, args...) - fmt.Println() -} - -func (e *expectation) needsProbe() bool { - return e.kind == "pointsto" || e.kind == "types" -} - -// Find probe (call to print(x)) of same source file/line as expectation. -func findProbe(prog *ssa.Program, probes map[*ssa.CallCommon]bool, queries map[ssa.Value]pointer.Pointer, e *expectation) (site *ssa.CallCommon, pts pointer.PointsToSet) { - for call := range probes { - pos := prog.Fset.Position(call.Pos()) - if pos.Line == e.linenum && pos.Filename == e.filename { - // TODO(adonovan): send this to test log (display only on failure). - // fmt.Printf("%s:%d: info: found probe for %s: %s\n", - // e.filename, e.linenum, e, p.arg0) // debugging - return call, queries[call.Args[0]].PointsTo() - } - } - return // e.g. analysis didn't reach this call -} - -func doOneInput(input, filename string) bool { - var conf loader.Config - - // Parsing. - f, err := conf.ParseFile(filename, input) - if err != nil { - fmt.Println(err) - return false - } - - // Create single-file main package and import its dependencies. - conf.CreateFromFiles("main", f) - iprog, err := conf.Load() - if err != nil { - fmt.Println(err) - return false - } - mainPkgInfo := iprog.Created[0].Pkg - - // SSA creation + building. - prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) - prog.Build() - - mainpkg := prog.Package(mainPkgInfo) - ptrmain := mainpkg // main package for the pointer analysis - if mainpkg.Func("main") == nil { - // No main function; assume it's a test. - ptrmain = prog.CreateTestMainPackage(mainpkg) - } - - // Find all calls to the built-in print(x). Analytically, - // print is a no-op, but it's a convenient hook for testing - // the PTS of an expression, so our tests use it. - probes := make(map[*ssa.CallCommon]bool) - for fn := range ssautil.AllFunctions(prog) { - if fn.Pkg == mainpkg { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - if instr, ok := instr.(ssa.CallInstruction); ok { - call := instr.Common() - if b, ok := call.Value.(*ssa.Builtin); ok && b.Name() == "print" && len(call.Args) == 1 { - probes[instr.Common()] = true - } - } - } - } - } - } - - ok := true - - lineMapping := make(map[string]string) // maps "file:line" to @line tag - - // Parse expectations in this input. - var exps []*expectation - re := regexp.MustCompile("// *@([a-z]*) *(.*)$") - lines := strings.Split(input, "\n") - for linenum, line := range lines { - linenum++ // make it 1-based - if matches := re.FindAllStringSubmatch(line, -1); matches != nil { - match := matches[0] - kind, rest := match[1], match[2] - e := &expectation{kind: kind, filename: filename, linenum: linenum} - - if kind == "line" { - if rest == "" { - ok = false - e.errorf("@%s expectation requires identifier", kind) - } else { - lineMapping[fmt.Sprintf("%s:%d", filename, linenum)] = rest - } - continue - } - - if e.needsProbe() && !strings.Contains(line, "print(") { - ok = false - e.errorf("@%s expectation must follow call to print(x)", kind) - continue - } - - switch kind { - case "pointsto": - e.args = split(rest, "|") - - case "types": - for _, typstr := range split(rest, "|") { - var t types.Type = types.Typ[types.Invalid] // means "..." - if typstr != "..." { - tv, err := types.Eval(prog.Fset, mainpkg.Pkg, f.Pos(), typstr) - if err != nil { - ok = false - // Don't print err since its location is bad. - e.errorf("'%s' is not a valid type: %s", typstr, err) - continue - } - t = tv.Type - } - e.types = append(e.types, t) - } - - case "calls": - e.args = split(rest, "->") - // TODO(adonovan): eagerly reject the - // expectation if fn doesn't denote - // existing function, rather than fail - // the expectation after analysis. - if len(e.args) != 2 { - ok = false - e.errorf("@calls expectation wants 'caller -> callee' arguments") - continue - } - - case "warning": - lit, err := strconv.Unquote(strings.TrimSpace(rest)) - if err != nil { - ok = false - e.errorf("couldn't parse @warning operand: %s", err.Error()) - continue - } - e.args = append(e.args, lit) - - default: - ok = false - e.errorf("unknown expectation kind: %s", e) - continue - } - exps = append(exps, e) - } - } - - var log bytes.Buffer - fmt.Fprintf(&log, "Input: %s\n", filename) - - // Run the analysis. - config := &pointer.Config{ - Reflection: true, - BuildCallGraph: true, - Mains: []*ssa.Package{ptrmain}, - Log: &log, - } - for probe := range probes { - v := probe.Args[0] - if pointer.CanPoint(v.Type()) { - config.AddQuery(v) - } - } - - // Print the log is there was an error or a panic. - complete := false - defer func() { - if !complete || !ok { - log.WriteTo(os.Stderr) - } - }() - - result, err := pointer.Analyze(config) - if err != nil { - panic(err) // internal error in pointer analysis - } - - // Check the expectations. - for _, e := range exps { - var call *ssa.CallCommon - var pts pointer.PointsToSet - var tProbe types.Type - if e.needsProbe() { - if call, pts = findProbe(prog, probes, result.Queries, e); call == nil { - ok = false - e.errorf("unreachable print() statement has expectation %s", e) - continue - } - tProbe = call.Args[0].Type() - if !pointer.CanPoint(tProbe) { - ok = false - e.errorf("expectation on non-pointerlike operand: %s", tProbe) - continue - } - } - - switch e.kind { - case "pointsto": - if !checkPointsToExpectation(e, pts, lineMapping, prog) { - ok = false - } - - case "types": - if !checkTypesExpectation(e, pts, tProbe) { - ok = false - } - - case "calls": - if !checkCallsExpectation(prog, e, result.CallGraph) { - ok = false - } - - case "warning": - if !checkWarningExpectation(prog, e, result.Warnings) { - ok = false - } - } - } - - complete = true - - // ok = false // debugging: uncomment to always see log - - return ok -} - -func labelString(l *pointer.Label, lineMapping map[string]string, prog *ssa.Program) string { - // Functions and Globals need no pos suffix, - // nor do allocations in intrinsic operations - // (for which we'll print the function name). - switch l.Value().(type) { - case nil, *ssa.Function, *ssa.Global: - return l.String() - } - - str := l.String() - if pos := l.Pos(); pos != token.NoPos { - // Append the position, using a @line tag instead of a line number, if defined. - posn := prog.Fset.Position(pos) - s := fmt.Sprintf("%s:%d", posn.Filename, posn.Line) - if tag, ok := lineMapping[s]; ok { - return fmt.Sprintf("%s@%s:%d", str, tag, posn.Column) - } - str = fmt.Sprintf("%s@%s", str, posn) - } - return str -} - -func checkPointsToExpectation(e *expectation, pts pointer.PointsToSet, lineMapping map[string]string, prog *ssa.Program) bool { - expected := make(map[string]int) - surplus := make(map[string]int) - exact := true - for _, g := range e.args { - if g == "..." { - exact = false - continue - } - expected[g]++ - } - // Find the set of labels that the probe's - // argument (x in print(x)) may point to. - for _, label := range pts.Labels() { - name := labelString(label, lineMapping, prog) - if expected[name] > 0 { - expected[name]-- - } else if exact { - surplus[name]++ - } - } - // Report multiset difference: - ok := true - for _, count := range expected { - if count > 0 { - ok = false - e.errorf("value does not alias these expected labels: %s", join(expected)) - break - } - } - for _, count := range surplus { - if count > 0 { - ok = false - e.errorf("value may additionally alias these labels: %s", join(surplus)) - break - } - } - return ok -} - -func checkTypesExpectation(e *expectation, pts pointer.PointsToSet, typ types.Type) bool { - var expected typeutil.Map - var surplus typeutil.Map - exact := true - for _, g := range e.types { - if g == types.Typ[types.Invalid] { - exact = false - continue - } - expected.Set(g, struct{}{}) - } - - if !pointer.CanHaveDynamicTypes(typ) { - e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", typ) - return false - } - - // Find the set of types that the probe's - // argument (x in print(x)) may contain. - for _, T := range pts.DynamicTypes().Keys() { - if expected.At(T) != nil { - expected.Delete(T) - } else if exact { - surplus.Set(T, struct{}{}) - } - } - // Report set difference: - ok := true - if expected.Len() > 0 { - ok = false - e.errorf("interface cannot contain these types: %s", expected.KeysString()) - } - if surplus.Len() > 0 { - ok = false - e.errorf("interface may additionally contain these types: %s", surplus.KeysString()) - } - return ok -} - -var errOK = errors.New("OK") - -func checkCallsExpectation(prog *ssa.Program, e *expectation, cg *callgraph.Graph) bool { - found := make(map[string]int) - err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error { - // Name-based matching is inefficient but it allows us to - // match functions whose names that would not appear in an - // index ("") or which are not unique ("func@1.2"). - if edge.Caller.Func.String() == e.args[0] { - calleeStr := edge.Callee.Func.String() - if calleeStr == e.args[1] { - return errOK // expectation satisified; stop the search - } - found[calleeStr]++ - } - return nil - }) - if err == errOK { - return true - } - if len(found) == 0 { - e.errorf("didn't find any calls from %s", e.args[0]) - } - e.errorf("found no call from %s to %s, but only to %s", - e.args[0], e.args[1], join(found)) - return false -} - -func checkWarningExpectation(prog *ssa.Program, e *expectation, warnings []pointer.Warning) bool { - // TODO(adonovan): check the position part of the warning too? - re, err := regexp.Compile(e.args[0]) - if err != nil { - e.errorf("invalid regular expression in @warning expectation: %s", err.Error()) - return false - } - - if len(warnings) == 0 { - e.errorf("@warning %s expectation, but no warnings", strconv.Quote(e.args[0])) - return false - } - - for _, w := range warnings { - if re.MatchString(w.Message) { - return true - } - } - - e.errorf("@warning %s expectation not satised; found these warnings though:", strconv.Quote(e.args[0])) - for _, w := range warnings { - fmt.Printf("%s: warning: %s\n", prog.Fset.Position(w.Pos), w.Message) - } - return false -} - -func TestInput(t *testing.T) { - ok := true - - wd, err := os.Getwd() - if err != nil { - t.Errorf("os.Getwd: %s", err) - return - } - - // 'go test' does a chdir so that relative paths in - // diagnostics no longer make sense relative to the invoking - // shell's cwd. We print a special marker so that Emacs can - // make sense of them. - fmt.Fprintf(os.Stderr, "Entering directory `%s'\n", wd) - - for _, filename := range inputs { - content, err := ioutil.ReadFile(filename) - if err != nil { - t.Errorf("couldn't read file '%s': %s", filename, err) - continue - } - - if !doOneInput(string(content), filename) { - ok = false - } - } - if !ok { - t.Fail() - } -} - -// join joins the elements of multiset with " | "s. -func join(set map[string]int) string { - var buf bytes.Buffer - sep := "" - for name, count := range set { - for i := 0; i < count; i++ { - buf.WriteString(sep) - sep = " | " - buf.WriteString(name) - } - } - return buf.String() -} - -// split returns the list of sep-delimited non-empty strings in s. -func split(s, sep string) (r []string) { - for _, elem := range strings.Split(s, sep) { - elem = strings.TrimSpace(elem) - if elem != "" { - r = append(r, elem) - } - } - return -} diff --git a/go/pointer/reflect14.go b/go/pointer/reflect14.go deleted file mode 100644 index c55f69e4f9..0000000000 --- a/go/pointer/reflect14.go +++ /dev/null @@ -1,1977 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This file implements the generation and resolution rules for -// constraints arising from the use of reflection in the target -// program. See doc.go for explanation of the representation. -// -// For consistency, the names of all parameters match those of the -// actual functions in the "reflect" package. -// -// To avoid proliferation of equivalent labels, intrinsics should -// memoize as much as possible, like TypeOf and Zero do for their -// tagged objects. -// -// TODO(adonovan): this file is rather subtle. Explain how we derive -// the implementation of each reflect operator from its spec, -// including the subtleties of reflect.flag{Addr,RO,Indir}. -// [Hint: our implementation is as if reflect.flagIndir was always -// true, i.e. reflect.Values are pointers to tagged objects, there is -// no inline allocation optimization; and indirect tagged objects (not -// yet implemented) correspond to reflect.Values with -// reflect.flagAddr.] -// A picture would help too. -// -// TODO(adonovan): try factoring up the common parts of the majority of -// these constraints that are single input, single output. - -import ( - "fmt" - "reflect" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -func init() { - for name, fn := range map[string]intrinsic{ - // reflect.Value methods. - "(reflect.Value).Addr": ext۰reflect۰Value۰Addr, - "(reflect.Value).Bool": ext۰NoEffect, - "(reflect.Value).Bytes": ext۰reflect۰Value۰Bytes, - "(reflect.Value).Call": ext۰reflect۰Value۰Call, - "(reflect.Value).CallSlice": ext۰reflect۰Value۰CallSlice, - "(reflect.Value).CanAddr": ext۰NoEffect, - "(reflect.Value).CanInterface": ext۰NoEffect, - "(reflect.Value).CanSet": ext۰NoEffect, - "(reflect.Value).Cap": ext۰NoEffect, - "(reflect.Value).Close": ext۰NoEffect, - "(reflect.Value).Complex": ext۰NoEffect, - "(reflect.Value).Convert": ext۰reflect۰Value۰Convert, - "(reflect.Value).Elem": ext۰reflect۰Value۰Elem, - "(reflect.Value).Field": ext۰reflect۰Value۰Field, - "(reflect.Value).FieldByIndex": ext۰reflect۰Value۰FieldByIndex, - "(reflect.Value).FieldByName": ext۰reflect۰Value۰FieldByName, - "(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc, - "(reflect.Value).Float": ext۰NoEffect, - "(reflect.Value).Index": ext۰reflect۰Value۰Index, - "(reflect.Value).Int": ext۰NoEffect, - "(reflect.Value).Interface": ext۰reflect۰Value۰Interface, - "(reflect.Value).InterfaceData": ext۰NoEffect, - "(reflect.Value).IsNil": ext۰NoEffect, - "(reflect.Value).IsValid": ext۰NoEffect, - "(reflect.Value).Kind": ext۰NoEffect, - "(reflect.Value).Len": ext۰NoEffect, - "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, - "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, - "(reflect.Value).Method": ext۰reflect۰Value۰Method, - "(reflect.Value).MethodByName": ext۰reflect۰Value۰MethodByName, - "(reflect.Value).NumField": ext۰NoEffect, - "(reflect.Value).NumMethod": ext۰NoEffect, - "(reflect.Value).OverflowComplex": ext۰NoEffect, - "(reflect.Value).OverflowFloat": ext۰NoEffect, - "(reflect.Value).OverflowInt": ext۰NoEffect, - "(reflect.Value).OverflowUint": ext۰NoEffect, - "(reflect.Value).Pointer": ext۰NoEffect, - "(reflect.Value).Recv": ext۰reflect۰Value۰Recv, - "(reflect.Value).Send": ext۰reflect۰Value۰Send, - "(reflect.Value).Set": ext۰reflect۰Value۰Set, - "(reflect.Value).SetBool": ext۰NoEffect, - "(reflect.Value).SetBytes": ext۰reflect۰Value۰SetBytes, - "(reflect.Value).SetComplex": ext۰NoEffect, - "(reflect.Value).SetFloat": ext۰NoEffect, - "(reflect.Value).SetInt": ext۰NoEffect, - "(reflect.Value).SetLen": ext۰NoEffect, - "(reflect.Value).SetMapIndex": ext۰reflect۰Value۰SetMapIndex, - "(reflect.Value).SetPointer": ext۰reflect۰Value۰SetPointer, - "(reflect.Value).SetString": ext۰NoEffect, - "(reflect.Value).SetUint": ext۰NoEffect, - "(reflect.Value).Slice": ext۰reflect۰Value۰Slice, - "(reflect.Value).String": ext۰NoEffect, - "(reflect.Value).TryRecv": ext۰reflect۰Value۰Recv, - "(reflect.Value).TrySend": ext۰reflect۰Value۰Send, - "(reflect.Value).Type": ext۰NoEffect, - "(reflect.Value).Uint": ext۰NoEffect, - "(reflect.Value).UnsafeAddr": ext۰NoEffect, - - // Standalone reflect.* functions. - "reflect.Append": ext۰reflect۰Append, - "reflect.AppendSlice": ext۰reflect۰AppendSlice, - "reflect.Copy": ext۰reflect۰Copy, - "reflect.ChanOf": ext۰reflect۰ChanOf, - "reflect.DeepEqual": ext۰NoEffect, - "reflect.Indirect": ext۰reflect۰Indirect, - "reflect.MakeChan": ext۰reflect۰MakeChan, - "reflect.MakeFunc": ext۰reflect۰MakeFunc, - "reflect.MakeMap": ext۰reflect۰MakeMap, - "reflect.MakeSlice": ext۰reflect۰MakeSlice, - "reflect.MapOf": ext۰reflect۰MapOf, - "reflect.New": ext۰reflect۰New, - "reflect.NewAt": ext۰reflect۰NewAt, - "reflect.PtrTo": ext۰reflect۰PtrTo, - "reflect.Select": ext۰reflect۰Select, - "reflect.SliceOf": ext۰reflect۰SliceOf, - "reflect.TypeOf": ext۰reflect۰TypeOf, - "reflect.ValueOf": ext۰reflect۰ValueOf, - "reflect.Zero": ext۰reflect۰Zero, - "reflect.init": ext۰NoEffect, - - // *reflect.rtype methods - "(*reflect.rtype).Align": ext۰NoEffect, - "(*reflect.rtype).AssignableTo": ext۰NoEffect, - "(*reflect.rtype).Bits": ext۰NoEffect, - "(*reflect.rtype).ChanDir": ext۰NoEffect, - "(*reflect.rtype).ConvertibleTo": ext۰NoEffect, - "(*reflect.rtype).Elem": ext۰reflect۰rtype۰Elem, - "(*reflect.rtype).Field": ext۰reflect۰rtype۰Field, - "(*reflect.rtype).FieldAlign": ext۰NoEffect, - "(*reflect.rtype).FieldByIndex": ext۰reflect۰rtype۰FieldByIndex, - "(*reflect.rtype).FieldByName": ext۰reflect۰rtype۰FieldByName, - "(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc, - "(*reflect.rtype).Implements": ext۰NoEffect, - "(*reflect.rtype).In": ext۰reflect۰rtype۰In, - "(*reflect.rtype).IsVariadic": ext۰NoEffect, - "(*reflect.rtype).Key": ext۰reflect۰rtype۰Key, - "(*reflect.rtype).Kind": ext۰NoEffect, - "(*reflect.rtype).Len": ext۰NoEffect, - "(*reflect.rtype).Method": ext۰reflect۰rtype۰Method, - "(*reflect.rtype).MethodByName": ext۰reflect۰rtype۰MethodByName, - "(*reflect.rtype).Name": ext۰NoEffect, - "(*reflect.rtype).NumField": ext۰NoEffect, - "(*reflect.rtype).NumIn": ext۰NoEffect, - "(*reflect.rtype).NumMethod": ext۰NoEffect, - "(*reflect.rtype).NumOut": ext۰NoEffect, - "(*reflect.rtype).Out": ext۰reflect۰rtype۰Out, - "(*reflect.rtype).PkgPath": ext۰NoEffect, - "(*reflect.rtype).Size": ext۰NoEffect, - "(*reflect.rtype).String": ext۰NoEffect, - } { - intrinsicsByName[name] = fn - } -} - -// -------------------- (reflect.Value) -------------------- - -func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).Bytes() Value ---------- - -// result = v.Bytes() -type rVBytesConstraint struct { - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVBytesConstraint) ptr() nodeid { return c.v } -func (c *rVBytesConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVBytes.result") -} -func (c *rVBytesConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVBytesConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v) -} - -func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, slice, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - tSlice, ok := tDyn.Underlying().(*types.Slice) - if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { - if a.onlineCopy(c.result, slice) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) { - a.addConstraint(&rVBytesConstraint{ - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (Value).Call(in []Value) []Value ---------- - -// result = v.Call(in) -type rVCallConstraint struct { - cgn *cgnode - targets nodeid // (indirect) - v nodeid // (ptr) - arg nodeid // = in[*] - result nodeid // (indirect) - dotdotdot bool // interpret last arg as a "..." slice -} - -func (c *rVCallConstraint) ptr() nodeid { return c.v } -func (c *rVCallConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.targets), "rVCall.targets") - h.markIndirect(onodeid(c.result), "rVCall.result") -} -func (c *rVCallConstraint) renumber(mapping []nodeid) { - c.targets = mapping[c.targets] - c.v = mapping[c.v] - c.arg = mapping[c.arg] - c.result = mapping[c.result] -} - -func (c *rVCallConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg) -} - -func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) { - if c.targets == 0 { - panic("no targets") - } - - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, fn, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - tSig, ok := tDyn.Underlying().(*types.Signature) - if !ok { - continue // not a function - } - if tSig.Recv() != nil { - panic(tSig) // TODO(adonovan): rethink when we implement Method() - } - - // Add dynamic call target. - if a.onlineCopy(c.targets, fn) { - a.addWork(c.targets) - // TODO(adonovan): is 'else continue' a sound optimisation here? - } - - // Allocate a P/R block. - tParams := tSig.Params() - tResults := tSig.Results() - params := a.addNodes(tParams, "rVCall.params") - results := a.addNodes(tResults, "rVCall.results") - - // Make a dynamic call to 'fn'. - a.store(fn, params, 1, a.sizeof(tParams)) - a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults)) - - // Populate P by type-asserting each actual arg (all merged in c.arg). - for i, n := 0, tParams.Len(); i < n; i++ { - T := tParams.At(i).Type() - a.typeAssert(T, params, c.arg, false) - params += nodeid(a.sizeof(T)) - } - - // Use R by tagging and copying each actual result to c.result. - for i, n := 0, tResults.Len(); i < n; i++ { - T := tResults.At(i).Type() - // Convert from an arbitrary type to a reflect.Value - // (like MakeInterface followed by reflect.ValueOf). - if isInterface(T) { - // (don't tag) - if a.onlineCopy(c.result, results) { - changed = true - } - } else { - obj := a.makeTagged(T, c.cgn, nil) - a.onlineCopyN(obj+1, results, a.sizeof(T)) - if a.addLabel(c.result, obj) { // (true) - changed = true - } - } - results += nodeid(a.sizeof(T)) - } - } - if changed { - a.addWork(c.result) - } -} - -// Common code for direct (inlined) and indirect calls to (reflect.Value).Call. -func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid { - // Allocate []reflect.Value array for the result. - ret := a.nextNode() - a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret") - a.endObject(ret, cgn, nil) - - // pts(targets) will be the set of possible call targets. - site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil) - - // All arguments are merged since they arrive in a slice. - argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil) - a.load(argelts, arg, 1, 1) // slice elements - - a.addConstraint(&rVCallConstraint{ - cgn: cgn, - targets: site.targets, - v: recv, - arg: argelts, - result: ret + 1, // results go into elements of ret - dotdotdot: dotdotdot, - }) - return ret -} - -func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) { - // This is the shared contour implementation of (reflect.Value).Call - // and CallSlice, as used by indirect calls (rare). - // Direct calls are inlined in gen.go, eliding the - // intermediate cgnode for Call. - site := new(callsite) - cgn.sites = append(cgn.sites, site) - recv := a.funcParams(cgn.obj) - arg := recv + 1 - ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot) - a.addressOf(cgn.fn.Signature.Results().At(0).Type(), a.funcResults(cgn.obj), ret) -} - -func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) { - reflectCall(a, cgn, false) -} - -func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) { - // TODO(adonovan): implement. Also, inline direct calls in gen.go too. - if false { - reflectCall(a, cgn, true) - } -} - -func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).Elem() Value ---------- - -// result = v.Elem() -type rVElemConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVElemConstraint) ptr() nodeid { return c.v } -func (c *rVElemConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVElem.result") -} -func (c *rVElemConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVElemConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v) -} - -func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, payload, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - switch t := tDyn.Underlying().(type) { - case *types.Interface: - if a.onlineCopy(c.result, payload) { - changed = true - } - - case *types.Pointer: - obj := a.makeTagged(t.Elem(), c.cgn, nil) - a.load(obj+1, payload, 0, a.sizeof(t.Elem())) - if a.addLabel(c.result, obj) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) { - a.addConstraint(&rVElemConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).Index() Value ---------- - -// result = v.Index() -type rVIndexConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVIndexConstraint) ptr() nodeid { return c.v } -func (c *rVIndexConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVIndex.result") -} -func (c *rVIndexConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVIndexConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v) -} - -func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, payload, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - var res nodeid - switch t := tDyn.Underlying().(type) { - case *types.Array: - res = a.makeTagged(t.Elem(), c.cgn, nil) - a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem())) - - case *types.Slice: - res = a.makeTagged(t.Elem(), c.cgn, nil) - a.load(res+1, payload, 1, a.sizeof(t.Elem())) - - case *types.Basic: - if t.Kind() == types.String { - res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil) - } - } - if res != 0 && a.addLabel(c.result, res) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) { - a.addConstraint(&rVIndexConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (Value).Interface() Value ---------- - -// result = v.Interface() -type rVInterfaceConstraint struct { - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVInterfaceConstraint) ptr() nodeid { return c.v } -func (c *rVInterfaceConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVInterface.result") -} -func (c *rVInterfaceConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVInterfaceConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v) -} - -func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, payload, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - if isInterface(tDyn) { - if a.onlineCopy(c.result, payload) { - a.addWork(c.result) - } - } else { - if a.addLabel(c.result, vObj) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) { - a.addConstraint(&rVInterfaceConstraint{ - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (Value).MapIndex(Value) Value ---------- - -// result = v.MapIndex(_) -type rVMapIndexConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVMapIndexConstraint) ptr() nodeid { return c.v } -func (c *rVMapIndexConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVMapIndex.result") -} -func (c *rVMapIndexConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVMapIndexConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v) -} - -func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, m, indirect := a.taggedValue(vObj) - tMap, _ := tDyn.Underlying().(*types.Map) - if tMap == nil { - continue // not a map - } - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - obj := a.makeTagged(tMap.Elem(), c.cgn, nil) - a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem())) - if a.addLabel(c.result, obj) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) { - a.addConstraint(&rVMapIndexConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (Value).MapKeys() []Value ---------- - -// result = v.MapKeys() -type rVMapKeysConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVMapKeysConstraint) ptr() nodeid { return c.v } -func (c *rVMapKeysConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVMapKeys.result") -} -func (c *rVMapKeysConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVMapKeysConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v) -} - -func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, m, indirect := a.taggedValue(vObj) - tMap, _ := tDyn.Underlying().(*types.Map) - if tMap == nil { - continue // not a map - } - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - kObj := a.makeTagged(tMap.Key(), c.cgn, nil) - a.load(kObj+1, m, 0, a.sizeof(tMap.Key())) - if a.addLabel(c.result, kObj) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) { - // Allocate an array for the result. - obj := a.nextNode() - T := types.NewSlice(a.reflectValueObj.Type()) - a.addNodes(sliceToArray(T), "reflect.MapKeys result") - a.endObject(obj, cgn, nil) - a.addressOf(T, a.funcResults(cgn.obj), obj) - - a.addConstraint(&rVMapKeysConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: obj + 1, // result is stored in array elems - }) -} - -func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).Recv(Value) Value ---------- - -// result, _ = v.Recv() -type rVRecvConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVRecvConstraint) ptr() nodeid { return c.v } -func (c *rVRecvConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVRecv.result") -} -func (c *rVRecvConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVRecvConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v) -} - -func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, ch, indirect := a.taggedValue(vObj) - tChan, _ := tDyn.Underlying().(*types.Chan) - if tChan == nil { - continue // not a channel - } - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - tElem := tChan.Elem() - elemObj := a.makeTagged(tElem, c.cgn, nil) - a.load(elemObj+1, ch, 0, a.sizeof(tElem)) - if a.addLabel(c.result, elemObj) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) { - a.addConstraint(&rVRecvConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (Value).Send(Value) ---------- - -// v.Send(x) -type rVSendConstraint struct { - cgn *cgnode - v nodeid // (ptr) - x nodeid -} - -func (c *rVSendConstraint) ptr() nodeid { return c.v } -func (c *rVSendConstraint) presolve(*hvn) {} -func (c *rVSendConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.x = mapping[c.x] -} - -func (c *rVSendConstraint) String() string { - return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x) -} - -func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, ch, indirect := a.taggedValue(vObj) - tChan, _ := tDyn.Underlying().(*types.Chan) - if tChan == nil { - continue // not a channel - } - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - // Extract x's payload to xtmp, then store to channel. - tElem := tChan.Elem() - xtmp := a.addNodes(tElem, "Send.xtmp") - a.typeAssert(tElem, xtmp, c.x, false) - a.store(ch, xtmp, 0, a.sizeof(tElem)) - } -} - -func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) { - params := a.funcParams(cgn.obj) - a.addConstraint(&rVSendConstraint{ - cgn: cgn, - v: params, - x: params + 1, - }) -} - -func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).SetBytes(x []byte) ---------- - -// v.SetBytes(x) -type rVSetBytesConstraint struct { - cgn *cgnode - v nodeid // (ptr) - x nodeid -} - -func (c *rVSetBytesConstraint) ptr() nodeid { return c.v } -func (c *rVSetBytesConstraint) presolve(*hvn) {} -func (c *rVSetBytesConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.x = mapping[c.x] -} - -func (c *rVSetBytesConstraint) String() string { - return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x) -} - -func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, slice, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - tSlice, ok := tDyn.Underlying().(*types.Slice) - if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) { - if a.onlineCopy(slice, c.x) { - a.addWork(slice) - } - } - } -} - -func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) { - params := a.funcParams(cgn.obj) - a.addConstraint(&rVSetBytesConstraint{ - cgn: cgn, - v: params, - x: params + 1, - }) -} - -// ---------- func (Value).SetMapIndex(k Value, v Value) ---------- - -// v.SetMapIndex(key, val) -type rVSetMapIndexConstraint struct { - cgn *cgnode - v nodeid // (ptr) - key nodeid - val nodeid -} - -func (c *rVSetMapIndexConstraint) ptr() nodeid { return c.v } -func (c *rVSetMapIndexConstraint) presolve(*hvn) {} -func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.key = mapping[c.key] - c.val = mapping[c.val] -} - -func (c *rVSetMapIndexConstraint) String() string { - return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val) -} - -func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, m, indirect := a.taggedValue(vObj) - tMap, _ := tDyn.Underlying().(*types.Map) - if tMap == nil { - continue // not a map - } - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - keysize := a.sizeof(tMap.Key()) - - // Extract key's payload to keytmp, then store to map key. - keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp") - a.typeAssert(tMap.Key(), keytmp, c.key, false) - a.store(m, keytmp, 0, keysize) - - // Extract val's payload to vtmp, then store to map value. - valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp") - a.typeAssert(tMap.Elem(), valtmp, c.val, false) - a.store(m, valtmp, keysize, a.sizeof(tMap.Elem())) - } -} - -func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) { - params := a.funcParams(cgn.obj) - a.addConstraint(&rVSetMapIndexConstraint{ - cgn: cgn, - v: params, - key: params + 1, - val: params + 2, - }) -} - -func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (Value).Slice(v Value, i, j int) Value ---------- - -// result = v.Slice(_, _) -type rVSliceConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rVSliceConstraint) ptr() nodeid { return c.v } -func (c *rVSliceConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rVSlice.result") -} -func (c *rVSliceConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *rVSliceConstraint) String() string { - return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v) -} - -func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, payload, indirect := a.taggedValue(vObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - var res nodeid - switch t := tDyn.Underlying().(type) { - case *types.Pointer: - if tArr, ok := t.Elem().Underlying().(*types.Array); ok { - // pointer to array - res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil) - if a.onlineCopy(res+1, payload) { - a.addWork(res + 1) - } - } - - case *types.Array: - // TODO(adonovan): implement addressable - // arrays when we do indirect tagged objects. - - case *types.Slice: - res = vObj - - case *types.Basic: - if t == types.Typ[types.String] { - res = vObj - } - } - - if res != 0 && a.addLabel(c.result, res) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) { - a.addConstraint(&rVSliceConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// -------------------- Standalone reflect functions -------------------- - -func ext۰reflect۰Append(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰Copy(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func ChanOf(ChanDir, Type) Type ---------- - -// result = ChanOf(dir, t) -type reflectChanOfConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) - dirs []types.ChanDir -} - -func (c *reflectChanOfConstraint) ptr() nodeid { return c.t } -func (c *reflectChanOfConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectChanOf.result") -} -func (c *reflectChanOfConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *reflectChanOfConstraint) String() string { - return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t) -} - -func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.rtypeTaggedValue(tObj) - - if typeTooHigh(T) { - continue - } - - for _, dir := range c.dirs { - if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -// dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf. -var dirMap = [...][]types.ChanDir{ - 0: {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown - reflect.RecvDir: {types.RecvOnly}, - reflect.SendDir: {types.SendOnly}, - reflect.BothDir: {types.SendRecv}, -} - -func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) { - // If we have access to the callsite, - // and the channel argument is a constant (as is usual), - // only generate the requested direction. - var dir reflect.ChanDir // unknown - if site := cgn.callersite; site != nil { - if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { - v, _ := exact.Int64Val(c.Value) - if 0 <= v && v <= int64(reflect.BothDir) { - dir = reflect.ChanDir(v) - } - } - } - - params := a.funcParams(cgn.obj) - a.addConstraint(&reflectChanOfConstraint{ - cgn: cgn, - t: params + 1, - result: a.funcResults(cgn.obj), - dirs: dirMap[dir], - }) -} - -// ---------- func Indirect(v Value) Value ---------- - -// result = Indirect(v) -type reflectIndirectConstraint struct { - cgn *cgnode - v nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectIndirectConstraint) ptr() nodeid { return c.v } -func (c *reflectIndirectConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectIndirect.result") -} -func (c *reflectIndirectConstraint) renumber(mapping []nodeid) { - c.v = mapping[c.v] - c.result = mapping[c.result] -} - -func (c *reflectIndirectConstraint) String() string { - return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v) -} - -func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - vObj := nodeid(x) - tDyn, _, _ := a.taggedValue(vObj) - var res nodeid - if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok { - // load the payload of the pointer's tagged object - // into a new tagged object - res = a.makeTagged(tPtr.Elem(), c.cgn, nil) - a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem())) - } else { - res = vObj - } - - if a.addLabel(c.result, res) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectIndirectConstraint{ - cgn: cgn, - v: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func MakeChan(Type) Value ---------- - -// result = MakeChan(typ) -type reflectMakeChanConstraint struct { - cgn *cgnode - typ nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ } -func (c *reflectMakeChanConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectMakeChan.result") -} -func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) { - c.typ = mapping[c.typ] - c.result = mapping[c.result] -} - -func (c *reflectMakeChanConstraint) String() string { - return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ) -} - -func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - typObj := nodeid(x) - T := a.rtypeTaggedValue(typObj) - tChan, ok := T.Underlying().(*types.Chan) - if !ok || tChan.Dir() != types.SendRecv { - continue // not a bidirectional channel type - } - - obj := a.nextNode() - a.addNodes(tChan.Elem(), "reflect.MakeChan.value") - a.endObject(obj, c.cgn, nil) - - // put its address in a new T-tagged object - id := a.makeTagged(T, c.cgn, nil) - a.addLabel(id+1, obj) - - // flow the T-tagged object to the result - if a.addLabel(c.result, id) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectMakeChanConstraint{ - cgn: cgn, - typ: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func MakeMap(Type) Value ---------- - -// result = MakeMap(typ) -type reflectMakeMapConstraint struct { - cgn *cgnode - typ nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ } -func (c *reflectMakeMapConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectMakeMap.result") -} -func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) { - c.typ = mapping[c.typ] - c.result = mapping[c.result] -} - -func (c *reflectMakeMapConstraint) String() string { - return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ) -} - -func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - typObj := nodeid(x) - T := a.rtypeTaggedValue(typObj) - tMap, ok := T.Underlying().(*types.Map) - if !ok { - continue // not a map type - } - - mapObj := a.nextNode() - a.addNodes(tMap.Key(), "reflect.MakeMap.key") - a.addNodes(tMap.Elem(), "reflect.MakeMap.value") - a.endObject(mapObj, c.cgn, nil) - - // put its address in a new T-tagged object - id := a.makeTagged(T, c.cgn, nil) - a.addLabel(id+1, mapObj) - - // flow the T-tagged object to the result - if a.addLabel(c.result, id) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectMakeMapConstraint{ - cgn: cgn, - typ: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func MakeSlice(Type) Value ---------- - -// result = MakeSlice(typ) -type reflectMakeSliceConstraint struct { - cgn *cgnode - typ nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ } -func (c *reflectMakeSliceConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectMakeSlice.result") -} -func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) { - c.typ = mapping[c.typ] - c.result = mapping[c.result] -} - -func (c *reflectMakeSliceConstraint) String() string { - return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ) -} - -func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - typObj := nodeid(x) - T := a.rtypeTaggedValue(typObj) - if _, ok := T.Underlying().(*types.Slice); !ok { - continue // not a slice type - } - - obj := a.nextNode() - a.addNodes(sliceToArray(T), "reflect.MakeSlice") - a.endObject(obj, c.cgn, nil) - - // put its address in a new T-tagged object - id := a.makeTagged(T, c.cgn, nil) - a.addLabel(id+1, obj) - - // flow the T-tagged object to the result - if a.addLabel(c.result, id) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectMakeSliceConstraint{ - cgn: cgn, - typ: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func New(Type) Value ---------- - -// result = New(typ) -type reflectNewConstraint struct { - cgn *cgnode - typ nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectNewConstraint) ptr() nodeid { return c.typ } -func (c *reflectNewConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectNew.result") -} -func (c *reflectNewConstraint) renumber(mapping []nodeid) { - c.typ = mapping[c.typ] - c.result = mapping[c.result] -} - -func (c *reflectNewConstraint) String() string { - return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ) -} - -func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - typObj := nodeid(x) - T := a.rtypeTaggedValue(typObj) - - // allocate new T object - newObj := a.nextNode() - a.addNodes(T, "reflect.New") - a.endObject(newObj, c.cgn, nil) - - // put its address in a new *T-tagged object - id := a.makeTagged(types.NewPointer(T), c.cgn, nil) - a.addLabel(id+1, newObj) - - // flow the pointer to the result - if a.addLabel(c.result, id) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰New(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectNewConstraint{ - cgn: cgn, - typ: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) { - ext۰reflect۰New(a, cgn) - - // TODO(adonovan): also report dynamic calls to unsound intrinsics. - if site := cgn.callersite; site != nil { - a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent()) - } -} - -// ---------- func PtrTo(Type) Type ---------- - -// result = PtrTo(t) -type reflectPtrToConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectPtrToConstraint) ptr() nodeid { return c.t } -func (c *reflectPtrToConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectPtrTo.result") -} -func (c *reflectPtrToConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *reflectPtrToConstraint) String() string { - return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t) -} - -func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.rtypeTaggedValue(tObj) - - if typeTooHigh(T) { - continue - } - - if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectPtrToConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func SliceOf(Type) Type ---------- - -// result = SliceOf(t) -type reflectSliceOfConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t } -func (c *reflectSliceOfConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectSliceOf.result") -} -func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *reflectSliceOfConstraint) String() string { - return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t) -} - -func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.rtypeTaggedValue(tObj) - - if typeTooHigh(T) { - continue - } - - if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectSliceOfConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func TypeOf(v Value) Type ---------- - -// result = TypeOf(i) -type reflectTypeOfConstraint struct { - cgn *cgnode - i nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i } -func (c *reflectTypeOfConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectTypeOf.result") -} -func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) { - c.i = mapping[c.i] - c.result = mapping[c.result] -} - -func (c *reflectTypeOfConstraint) String() string { - return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i) -} - -func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - iObj := nodeid(x) - tDyn, _, _ := a.taggedValue(iObj) - if a.addLabel(c.result, a.makeRtype(tDyn)) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectTypeOfConstraint{ - cgn: cgn, - i: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func ValueOf(interface{}) Value ---------- - -func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) { - // TODO(adonovan): when we start creating indirect tagged - // objects, we'll need to handle them specially here since - // they must never appear in the PTS of an interface{}. - a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1) -} - -// ---------- func Zero(Type) Value ---------- - -// result = Zero(typ) -type reflectZeroConstraint struct { - cgn *cgnode - typ nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *reflectZeroConstraint) ptr() nodeid { return c.typ } -func (c *reflectZeroConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "reflectZero.result") -} -func (c *reflectZeroConstraint) renumber(mapping []nodeid) { - c.typ = mapping[c.typ] - c.result = mapping[c.result] -} - -func (c *reflectZeroConstraint) String() string { - return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ) -} - -func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - typObj := nodeid(x) - T := a.rtypeTaggedValue(typObj) - - // TODO(adonovan): if T is an interface type, we need - // to create an indirect tagged object containing - // new(T). To avoid updates of such shared values, - // we'll need another flag on indirect tagged objects - // that marks whether they are addressable or - // readonly, just like the reflect package does. - - // memoize using a.reflectZeros[T] - var id nodeid - if z := a.reflectZeros.At(T); false && z != nil { - id = z.(nodeid) - } else { - id = a.makeTagged(T, c.cgn, nil) - a.reflectZeros.Set(T, id) - } - if a.addLabel(c.result, id) { - changed = true - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰Zero(a *analysis, cgn *cgnode) { - a.addConstraint(&reflectZeroConstraint{ - cgn: cgn, - typ: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// -------------------- (*reflect.rtype) methods -------------------- - -// ---------- func (*rtype) Elem() Type ---------- - -// result = Elem(t) -type rtypeElemConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rtypeElemConstraint) ptr() nodeid { return c.t } -func (c *rtypeElemConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rtypeElem.result") -} -func (c *rtypeElemConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *rtypeElemConstraint) String() string { - return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t) -} - -func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) { - // Implemented by *types.{Map,Chan,Array,Slice,Pointer}. - type hasElem interface { - Elem() types.Type - } - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.nodes[tObj].obj.data.(types.Type) - if tHasElem, ok := T.Underlying().(hasElem); ok { - if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰rtype۰Elem(a *analysis, cgn *cgnode) { - a.addConstraint(&rtypeElemConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (*rtype) Field(int) StructField ---------- -// ---------- func (*rtype) FieldByName(string) (StructField, bool) ---------- - -// result = FieldByName(t, name) -// result = Field(t, _) -type rtypeFieldByNameConstraint struct { - cgn *cgnode - name string // name of field; "" for unknown - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t } -func (c *rtypeFieldByNameConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type") -} -func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *rtypeFieldByNameConstraint) String() string { - return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name) -} - -func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) { - // type StructField struct { - // 0 __identity__ - // 1 Name string - // 2 PkgPath string - // 3 Type Type - // 4 Tag StructTag - // 5 Offset uintptr - // 6 Index []int - // 7 Anonymous bool - // } - - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.nodes[tObj].obj.data.(types.Type) - tStruct, ok := T.Underlying().(*types.Struct) - if !ok { - continue // not a struct type - } - - n := tStruct.NumFields() - for i := 0; i < n; i++ { - f := tStruct.Field(i) - if c.name == "" || c.name == f.Name() { - - // a.offsetOf(Type) is 3. - if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) { - a.addWork(id) - } - // TODO(adonovan): StructField.Index should be non-nil. - } - } - } -} - -func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) { - // If we have access to the callsite, - // and the argument is a string constant, - // return only that field. - var name string - if site := cgn.callersite; site != nil { - if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { - name = exact.StringVal(c.Value) - } - } - - a.addConstraint(&rtypeFieldByNameConstraint{ - cgn: cgn, - name: name, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) { - // No-one ever calls Field with a constant argument, - // so we don't specialize that case. - a.addConstraint(&rtypeFieldByNameConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {} // TODO(adonovan) -func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan) - -// ---------- func (*rtype) In/Out(i int) Type ---------- - -// result = In/Out(t, i) -type rtypeInOutConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) - out bool - i int // -ve if not a constant -} - -func (c *rtypeInOutConstraint) ptr() nodeid { return c.t } -func (c *rtypeInOutConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rtypeInOut.result") -} -func (c *rtypeInOutConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *rtypeInOutConstraint) String() string { - return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i) -} - -func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.nodes[tObj].obj.data.(types.Type) - sig, ok := T.Underlying().(*types.Signature) - if !ok { - continue // not a func type - } - - tuple := sig.Params() - if c.out { - tuple = sig.Results() - } - for i, n := 0, tuple.Len(); i < n; i++ { - if c.i < 0 || c.i == i { - if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) { - changed = true - } - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) { - // If we have access to the callsite, - // and the argument is an int constant, - // return only that parameter. - index := -1 - if site := cgn.callersite; site != nil { - if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { - v, _ := exact.Int64Val(c.Value) - index = int(v) - } - } - a.addConstraint(&rtypeInOutConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - out: out, - i: index, - }) -} - -func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) { - ext۰reflect۰rtype۰InOut(a, cgn, false) -} - -func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) { - ext۰reflect۰rtype۰InOut(a, cgn, true) -} - -// ---------- func (*rtype) Key() Type ---------- - -// result = Key(t) -type rtypeKeyConstraint struct { - cgn *cgnode - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rtypeKeyConstraint) ptr() nodeid { return c.t } -func (c *rtypeKeyConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result), "rtypeKey.result") -} -func (c *rtypeKeyConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *rtypeKeyConstraint) String() string { - return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t) -} - -func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) { - changed := false - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.nodes[tObj].obj.data.(types.Type) - if tMap, ok := T.Underlying().(*types.Map); ok { - if a.addLabel(c.result, a.makeRtype(tMap.Key())) { - changed = true - } - } - } - if changed { - a.addWork(c.result) - } -} - -func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) { - a.addConstraint(&rtypeKeyConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// ---------- func (*rtype) Method(int) (Method, bool) ---------- -// ---------- func (*rtype) MethodByName(string) (Method, bool) ---------- - -// result = MethodByName(t, name) -// result = Method(t, _) -type rtypeMethodByNameConstraint struct { - cgn *cgnode - name string // name of method; "" for unknown - t nodeid // (ptr) - result nodeid // (indirect) -} - -func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t } -func (c *rtypeMethodByNameConstraint) presolve(h *hvn) { - h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type") - h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func") -} -func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) { - c.t = mapping[c.t] - c.result = mapping[c.result] -} - -func (c *rtypeMethodByNameConstraint) String() string { - return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name) -} - -// changeRecv returns sig with Recv prepended to Params(). -func changeRecv(sig *types.Signature) *types.Signature { - params := sig.Params() - n := params.Len() - p2 := make([]*types.Var, n+1) - p2[0] = sig.Recv() - for i := 0; i < n; i++ { - p2[i+1] = params.At(i) - } - return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic()) -} - -func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - tObj := nodeid(x) - T := a.nodes[tObj].obj.data.(types.Type) - - isIface := isInterface(T) - - // We don't use Lookup(c.name) when c.name != "" to avoid - // ambiguity: >1 unexported methods could match. - mset := a.prog.MethodSets.MethodSet(T) - for i, n := 0, mset.Len(); i < n; i++ { - sel := mset.At(i) - if c.name == "" || c.name == sel.Obj().Name() { - // type Method struct { - // 0 __identity__ - // 1 Name string - // 2 PkgPath string - // 3 Type Type - // 4 Func Value - // 5 Index int - // } - - var sig *types.Signature - var fn *ssa.Function - if isIface { - sig = sel.Type().(*types.Signature) - } else { - fn = a.prog.MethodValue(sel) - // move receiver to params[0] - sig = changeRecv(fn.Signature) - } - - // a.offsetOf(Type) is 3. - if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) { - a.addWork(id) - } - if fn != nil { - // a.offsetOf(Func) is 4. - if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) { - a.addWork(id) - } - } - } - } - } -} - -func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) { - // If we have access to the callsite, - // and the argument is a string constant, - // return only that method. - var name string - if site := cgn.callersite; site != nil { - if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { - name = exact.StringVal(c.Value) - } - } - - a.addConstraint(&rtypeMethodByNameConstraint{ - cgn: cgn, - name: name, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) { - // No-one ever calls Method with a constant argument, - // so we don't specialize that case. - a.addConstraint(&rtypeMethodByNameConstraint{ - cgn: cgn, - t: a.funcParams(cgn.obj), - result: a.funcResults(cgn.obj), - }) -} - -// typeHeight returns the "height" of the type, which is roughly -// speaking the number of chan, map, pointer and slice type constructors -// at the root of T; these are the four type kinds that can be created -// via reflection. Chan and map constructors are counted as double the -// height of slice and pointer constructors since they are less often -// deeply nested. -// -// The solver rules for type constructors must somehow bound the set of -// types they create to ensure termination of the algorithm in cases -// where the output of a type constructor flows to its input, e.g. -// -// func f(t reflect.Type) { -// f(reflect.PtrTo(t)) -// } -// -// It does this by limiting the type height to k, but this still leaves -// a potentially exponential (4^k) number of of types that may be -// enumerated in pathological cases. -// -func typeHeight(T types.Type) int { - switch T := T.(type) { - case *types.Chan: - return 2 + typeHeight(T.Elem()) - case *types.Map: - k := typeHeight(T.Key()) - v := typeHeight(T.Elem()) - if v > k { - k = v // max(k, v) - } - return 2 + k - case *types.Slice: - return 1 + typeHeight(T.Elem()) - case *types.Pointer: - return 1 + typeHeight(T.Elem()) - } - return 0 -} - -func typeTooHigh(T types.Type) bool { - return typeHeight(T) > 3 -} diff --git a/go/pointer/solve14.go b/go/pointer/solve14.go deleted file mode 100644 index 25b52dbedf..0000000000 --- a/go/pointer/solve14.go +++ /dev/null @@ -1,373 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -// This file defines a naive Andersen-style solver for the inclusion -// constraint system. - -import ( - "fmt" - - "golang.org/x/tools/go/types" -) - -type solverState struct { - complex []constraint // complex constraints attached to this node - copyTo nodeset // simple copy constraint edges - pts nodeset // points-to set of this node - prevPTS nodeset // pts(n) in previous iteration (for difference propagation) -} - -func (a *analysis) solve() { - start("Solving") - if a.log != nil { - fmt.Fprintf(a.log, "\n\n==== Solving constraints\n\n") - } - - // Solver main loop. - var delta nodeset - for { - // Add new constraints to the graph: - // static constraints from SSA on round 1, - // dynamic constraints from reflection thereafter. - a.processNewConstraints() - - var x int - if !a.work.TakeMin(&x) { - break // empty - } - id := nodeid(x) - if a.log != nil { - fmt.Fprintf(a.log, "\tnode n%d\n", id) - } - - n := a.nodes[id] - - // Difference propagation. - delta.Difference(&n.solve.pts.Sparse, &n.solve.prevPTS.Sparse) - if delta.IsEmpty() { - continue - } - if a.log != nil { - fmt.Fprintf(a.log, "\t\tpts(n%d : %s) = %s + %s\n", - id, n.typ, &delta, &n.solve.prevPTS) - } - n.solve.prevPTS.Copy(&n.solve.pts.Sparse) - - // Apply all resolution rules attached to n. - a.solveConstraints(n, &delta) - - if a.log != nil { - fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, &n.solve.pts) - } - } - - if !a.nodes[0].solve.pts.IsEmpty() { - panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].solve.pts)) - } - - // Release working state (but keep final PTS). - for _, n := range a.nodes { - n.solve.complex = nil - n.solve.copyTo.Clear() - n.solve.prevPTS.Clear() - } - - if a.log != nil { - fmt.Fprintf(a.log, "Solver done\n") - - // Dump solution. - for i, n := range a.nodes { - if !n.solve.pts.IsEmpty() { - fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, &n.solve.pts, n.typ) - } - } - } - stop("Solving") -} - -// processNewConstraints takes the new constraints from a.constraints -// and adds them to the graph, ensuring -// that new constraints are applied to pre-existing labels and -// that pre-existing constraints are applied to new labels. -// -func (a *analysis) processNewConstraints() { - // Take the slice of new constraints. - // (May grow during call to solveConstraints.) - constraints := a.constraints - a.constraints = nil - - // Initialize points-to sets from addr-of (base) constraints. - for _, c := range constraints { - if c, ok := c.(*addrConstraint); ok { - dst := a.nodes[c.dst] - dst.solve.pts.add(c.src) - - // Populate the worklist with nodes that point to - // something initially (due to addrConstraints) and - // have other constraints attached. - // (A no-op in round 1.) - if !dst.solve.copyTo.IsEmpty() || len(dst.solve.complex) > 0 { - a.addWork(c.dst) - } - } - } - - // Attach simple (copy) and complex constraints to nodes. - var stale nodeset - for _, c := range constraints { - var id nodeid - switch c := c.(type) { - case *addrConstraint: - // base constraints handled in previous loop - continue - case *copyConstraint: - // simple (copy) constraint - id = c.src - a.nodes[id].solve.copyTo.add(c.dst) - default: - // complex constraint - id = c.ptr() - solve := a.nodes[id].solve - solve.complex = append(solve.complex, c) - } - - if n := a.nodes[id]; !n.solve.pts.IsEmpty() { - if !n.solve.prevPTS.IsEmpty() { - stale.add(id) - } - a.addWork(id) - } - } - // Apply new constraints to pre-existing PTS labels. - var space [50]int - for _, id := range stale.AppendTo(space[:0]) { - n := a.nodes[nodeid(id)] - a.solveConstraints(n, &n.solve.prevPTS) - } -} - -// solveConstraints applies each resolution rule attached to node n to -// the set of labels delta. It may generate new constraints in -// a.constraints. -// -func (a *analysis) solveConstraints(n *node, delta *nodeset) { - if delta.IsEmpty() { - return - } - - // Process complex constraints dependent on n. - for _, c := range n.solve.complex { - if a.log != nil { - fmt.Fprintf(a.log, "\t\tconstraint %s\n", c) - } - c.solve(a, delta) - } - - // Process copy constraints. - var copySeen nodeset - for _, x := range n.solve.copyTo.AppendTo(a.deltaSpace) { - mid := nodeid(x) - if copySeen.add(mid) { - if a.nodes[mid].solve.pts.addAll(delta) { - a.addWork(mid) - } - } - } -} - -// addLabel adds label to the points-to set of ptr and reports whether the set grew. -func (a *analysis) addLabel(ptr, label nodeid) bool { - b := a.nodes[ptr].solve.pts.add(label) - if b && a.log != nil { - fmt.Fprintf(a.log, "\t\tpts(n%d) += n%d\n", ptr, label) - } - return b -} - -func (a *analysis) addWork(id nodeid) { - a.work.Insert(int(id)) - if a.log != nil { - fmt.Fprintf(a.log, "\t\twork: n%d\n", id) - } -} - -// onlineCopy adds a copy edge. It is called online, i.e. during -// solving, so it adds edges and pts members directly rather than by -// instantiating a 'constraint'. -// -// The size of the copy is implicitly 1. -// It returns true if pts(dst) changed. -// -func (a *analysis) onlineCopy(dst, src nodeid) bool { - if dst != src { - if nsrc := a.nodes[src]; nsrc.solve.copyTo.add(dst) { - if a.log != nil { - fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src) - } - // TODO(adonovan): most calls to onlineCopy - // are followed by addWork, possibly batched - // via a 'changed' flag; see if there's a - // noticeable penalty to calling addWork here. - return a.nodes[dst].solve.pts.addAll(&nsrc.solve.pts) - } - } - return false -} - -// Returns sizeof. -// Implicitly adds nodes to worklist. -// -// TODO(adonovan): now that we support a.copy() during solving, we -// could eliminate onlineCopyN, but it's much slower. Investigate. -// -func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 { - for i := uint32(0); i < sizeof; i++ { - if a.onlineCopy(dst, src) { - a.addWork(dst) - } - src++ - dst++ - } - return sizeof -} - -func (c *loadConstraint) solve(a *analysis, delta *nodeset) { - var changed bool - for _, x := range delta.AppendTo(a.deltaSpace) { - k := nodeid(x) - koff := k + nodeid(c.offset) - if a.onlineCopy(c.dst, koff) { - changed = true - } - } - if changed { - a.addWork(c.dst) - } -} - -func (c *storeConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - k := nodeid(x) - koff := k + nodeid(c.offset) - if a.onlineCopy(koff, c.src) { - a.addWork(koff) - } - } -} - -func (c *offsetAddrConstraint) solve(a *analysis, delta *nodeset) { - dst := a.nodes[c.dst] - for _, x := range delta.AppendTo(a.deltaSpace) { - k := nodeid(x) - if dst.solve.pts.add(k + nodeid(c.offset)) { - a.addWork(c.dst) - } - } -} - -func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - ifaceObj := nodeid(x) - tDyn, _, indirect := a.taggedValue(ifaceObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - if types.AssignableTo(tDyn, c.typ) { - if a.addLabel(c.dst, ifaceObj) { - a.addWork(c.dst) - } - } - } -} - -func (c *untagConstraint) solve(a *analysis, delta *nodeset) { - predicate := types.AssignableTo - if c.exact { - predicate = types.Identical - } - for _, x := range delta.AppendTo(a.deltaSpace) { - ifaceObj := nodeid(x) - tDyn, v, indirect := a.taggedValue(ifaceObj) - if indirect { - // TODO(adonovan): we'll need to implement this - // when we start creating indirect tagged objects. - panic("indirect tagged object") - } - - if predicate(tDyn, c.typ) { - // Copy payload sans tag to dst. - // - // TODO(adonovan): opt: if tDyn is - // nonpointerlike we can skip this entire - // constraint, perhaps. We only care about - // pointers among the fields. - a.onlineCopyN(c.dst, v, a.sizeof(tDyn)) - } - } -} - -func (c *invokeConstraint) solve(a *analysis, delta *nodeset) { - for _, x := range delta.AppendTo(a.deltaSpace) { - ifaceObj := nodeid(x) - tDyn, v, indirect := a.taggedValue(ifaceObj) - if indirect { - // TODO(adonovan): we may need to implement this if - // we ever apply invokeConstraints to reflect.Value PTSs, - // e.g. for (reflect.Value).Call. - panic("indirect tagged object") - } - - // Look up the concrete method. - fn := a.prog.LookupMethod(tDyn, c.method.Pkg(), c.method.Name()) - if fn == nil { - panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, c.method)) - } - sig := fn.Signature - - fnObj := a.globalobj[fn] // dynamic calls use shared contour - if fnObj == 0 { - // a.objectNode(fn) was not called during gen phase. - panic(fmt.Sprintf("a.globalobj[%s]==nil", fn)) - } - - // Make callsite's fn variable point to identity of - // concrete method. (There's no need to add it to - // worklist since it never has attached constraints.) - a.addLabel(c.params, fnObj) - - // Extract value and connect to method's receiver. - // Copy payload to method's receiver param (arg0). - arg0 := a.funcParams(fnObj) - recvSize := a.sizeof(sig.Recv().Type()) - a.onlineCopyN(arg0, v, recvSize) - - src := c.params + 1 // skip past identity - dst := arg0 + nodeid(recvSize) - - // Copy caller's argument block to method formal parameters. - paramsSize := a.sizeof(sig.Params()) - a.onlineCopyN(dst, src, paramsSize) - src += nodeid(paramsSize) - dst += nodeid(paramsSize) - - // Copy method results to caller's result block. - resultsSize := a.sizeof(sig.Results()) - a.onlineCopyN(src, dst, resultsSize) - } -} - -func (c *addrConstraint) solve(a *analysis, delta *nodeset) { - panic("addr is not a complex constraint") -} - -func (c *copyConstraint) solve(a *analysis, delta *nodeset) { - panic("copy is not a complex constraint") -} diff --git a/go/pointer/util14.go b/go/pointer/util14.go deleted file mode 100644 index d04deeb8d3..0000000000 --- a/go/pointer/util14.go +++ /dev/null @@ -1,316 +0,0 @@ -// 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. - -// +build !go1.5 - -package pointer - -import ( - "bytes" - "fmt" - "log" - "os" - "os/exec" - "runtime" - "time" - - "golang.org/x/tools/container/intsets" - "golang.org/x/tools/go/types" -) - -// CanPoint reports whether the type T is pointerlike, -// for the purposes of this analysis. -func CanPoint(T types.Type) bool { - switch T := T.(type) { - case *types.Named: - if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { - return true // treat reflect.Value like interface{} - } - return CanPoint(T.Underlying()) - - case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice: - return true - } - - return false // array struct tuple builtin basic -} - -// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types, -// i.e. is an interface (incl. reflect.Type) or a reflect.Value. -// -func CanHaveDynamicTypes(T types.Type) bool { - switch T := T.(type) { - case *types.Named: - if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { - return true // reflect.Value - } - return CanHaveDynamicTypes(T.Underlying()) - case *types.Interface: - return true - } - return false -} - -func isInterface(T types.Type) bool { return types.IsInterface(T) } - -// mustDeref returns the element type of its argument, which must be a -// pointer; panic ensues otherwise. -func mustDeref(typ types.Type) types.Type { - return typ.Underlying().(*types.Pointer).Elem() -} - -// deref returns a pointer's element type; otherwise it returns typ. -func deref(typ types.Type) types.Type { - if p, ok := typ.Underlying().(*types.Pointer); ok { - return p.Elem() - } - return typ -} - -// A fieldInfo describes one subelement (node) of the flattening-out -// of a type T: the subelement's type and its path from the root of T. -// -// For example, for this type: -// type line struct{ points []struct{x, y int} } -// flatten() of the inner struct yields the following []fieldInfo: -// struct{ x, y int } "" -// int ".x" -// int ".y" -// and flatten(line) yields: -// struct{ points []struct{x, y int} } "" -// struct{ x, y int } ".points[*]" -// int ".points[*].x -// int ".points[*].y" -// -type fieldInfo struct { - typ types.Type - - // op and tail describe the path to the element (e.g. ".a#2.b[*].c"). - op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil - tail *fieldInfo -} - -// path returns a user-friendly string describing the subelement path. -// -func (fi *fieldInfo) path() string { - var buf bytes.Buffer - for p := fi; p != nil; p = p.tail { - switch op := p.op.(type) { - case bool: - fmt.Fprintf(&buf, "[*]") - case int: - fmt.Fprintf(&buf, "#%d", op) - case *types.Var: - fmt.Fprintf(&buf, ".%s", op.Name()) - } - } - return buf.String() -} - -// flatten returns a list of directly contained fields in the preorder -// traversal of the type tree of t. The resulting elements are all -// scalars (basic types or pointerlike types), except for struct/array -// "identity" nodes, whose type is that of the aggregate. -// -// reflect.Value is considered pointerlike, similar to interface{}. -// -// Callers must not mutate the result. -// -func (a *analysis) flatten(t types.Type) []*fieldInfo { - fl, ok := a.flattenMemo[t] - if !ok { - switch t := t.(type) { - case *types.Named: - u := t.Underlying() - if isInterface(u) { - // Debuggability hack: don't remove - // the named type from interfaces as - // they're very verbose. - fl = append(fl, &fieldInfo{typ: t}) - } else { - fl = a.flatten(u) - } - - case *types.Basic, - *types.Signature, - *types.Chan, - *types.Map, - *types.Interface, - *types.Slice, - *types.Pointer: - fl = append(fl, &fieldInfo{typ: t}) - - case *types.Array: - fl = append(fl, &fieldInfo{typ: t}) // identity node - for _, fi := range a.flatten(t.Elem()) { - fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi}) - } - - case *types.Struct: - fl = append(fl, &fieldInfo{typ: t}) // identity node - for i, n := 0, t.NumFields(); i < n; i++ { - f := t.Field(i) - for _, fi := range a.flatten(f.Type()) { - fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi}) - } - } - - case *types.Tuple: - // No identity node: tuples are never address-taken. - n := t.Len() - if n == 1 { - // Don't add a fieldInfo link for singletons, - // e.g. in params/results. - fl = append(fl, a.flatten(t.At(0).Type())...) - } else { - for i := 0; i < n; i++ { - f := t.At(i) - for _, fi := range a.flatten(f.Type()) { - fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi}) - } - } - } - - default: - panic(t) - } - - a.flattenMemo[t] = fl - } - - return fl -} - -// sizeof returns the number of pointerlike abstractions (nodes) in the type t. -func (a *analysis) sizeof(t types.Type) uint32 { - return uint32(len(a.flatten(t))) -} - -// shouldTrack reports whether object type T contains (recursively) -// any fields whose addresses should be tracked. -func (a *analysis) shouldTrack(T types.Type) bool { - if a.track == trackAll { - return true // fast path - } - track, ok := a.trackTypes[T] - if !ok { - a.trackTypes[T] = true // break cycles conservatively - // NB: reflect.Value, reflect.Type are pre-populated to true. - for _, fi := range a.flatten(T) { - switch ft := fi.typ.Underlying().(type) { - case *types.Interface, *types.Signature: - track = true // needed for callgraph - case *types.Basic: - // no-op - case *types.Chan: - track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem()) - case *types.Map: - track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem()) - case *types.Slice: - track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem()) - case *types.Pointer: - track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem()) - case *types.Array, *types.Struct: - // No need to look at field types since they will follow (flattened). - default: - // Includes *types.Tuple, which are never address-taken. - panic(ft) - } - if track { - break - } - } - a.trackTypes[T] = track - if !track && a.log != nil { - fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T) - } - } - return track -} - -// offsetOf returns the (abstract) offset of field index within struct -// or tuple typ. -func (a *analysis) offsetOf(typ types.Type, index int) uint32 { - var offset uint32 - switch t := typ.Underlying().(type) { - case *types.Tuple: - for i := 0; i < index; i++ { - offset += a.sizeof(t.At(i).Type()) - } - case *types.Struct: - offset++ // the node for the struct itself - for i := 0; i < index; i++ { - offset += a.sizeof(t.Field(i).Type()) - } - default: - panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ)) - } - return offset -} - -// sliceToArray returns the type representing the arrays to which -// slice type slice points. -func sliceToArray(slice types.Type) *types.Array { - return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1) -} - -// Node set ------------------------------------------------------------------- - -type nodeset struct { - intsets.Sparse -} - -func (ns *nodeset) String() string { - var buf bytes.Buffer - buf.WriteRune('{') - var space [50]int - for i, n := range ns.AppendTo(space[:0]) { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteRune('n') - fmt.Fprintf(&buf, "%d", n) - } - buf.WriteRune('}') - return buf.String() -} - -func (ns *nodeset) add(n nodeid) bool { - return ns.Sparse.Insert(int(n)) -} - -func (x *nodeset) addAll(y *nodeset) bool { - return x.UnionWith(&y.Sparse) -} - -// Profiling & debugging ------------------------------------------------------- - -var timers = make(map[string]time.Time) - -func start(name string) { - if debugTimers { - timers[name] = time.Now() - log.Printf("%s...\n", name) - } -} - -func stop(name string) { - if debugTimers { - log.Printf("%s took %s\n", name, time.Since(timers[name])) - } -} - -// diff runs the command "diff a b" and reports its success. -func diff(a, b string) bool { - var cmd *exec.Cmd - switch runtime.GOOS { - case "plan9": - cmd = exec.Command("/bin/diff", "-c", a, b) - default: - cmd = exec.Command("/usr/bin/diff", "-u", a, b) - } - cmd.Stdout = os.Stderr - cmd.Stderr = os.Stderr - return cmd.Run() == nil -} diff --git a/go/ssa/builder14.go b/go/ssa/builder14.go deleted file mode 100644 index fcc4b928e7..0000000000 --- a/go/ssa/builder14.go +++ /dev/null @@ -1,2386 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file implements the BUILD phase of SSA construction. -// -// SSA construction has two phases, CREATE and BUILD. In the CREATE phase -// (create.go), all packages are constructed and type-checked and -// definitions of all package members are created, method-sets are -// computed, and wrapper methods are synthesized. -// ssa.Packages are created in arbitrary order. -// -// In the BUILD phase (builder.go), the builder traverses the AST of -// each Go source function and generates SSA instructions for the -// function body. Initializer expressions for package-level variables -// are emitted to the package's init() function in the order specified -// by go/types.Info.InitOrder, then code for each function in the -// package is generated in lexical order. -// The BUILD phases for distinct packages are independent and are -// executed in parallel. -// -// TODO(adonovan): indeed, building functions is now embarrassingly parallel. -// Audit for concurrency then benchmark using more goroutines. -// -// The builder's and Program's indices (maps) are populated and -// mutated during the CREATE phase, but during the BUILD phase they -// remain constant. The sole exception is Prog.methodSets and its -// related maps, which are protected by a dedicated mutex. - -import ( - "fmt" - "go/ast" - "go/token" - "os" - "sync" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -type opaqueType struct { - types.Type - name string -} - -func (t *opaqueType) String() string { return t.name } - -var ( - varOk = newVar("ok", tBool) - varIndex = newVar("index", tInt) - - // Type constants. - tBool = types.Typ[types.Bool] - tByte = types.Typ[types.Byte] - tInt = types.Typ[types.Int] - tInvalid = types.Typ[types.Invalid] - tString = types.Typ[types.String] - tUntypedNil = types.Typ[types.UntypedNil] - tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators - tEface = new(types.Interface) - - // SSA Value constants. - vZero = intConst(0) - vOne = intConst(1) - vTrue = NewConst(exact.MakeBool(true), tBool) -) - -// builder holds state associated with the package currently being built. -// Its methods contain all the logic for AST-to-SSA conversion. -type builder struct{} - -// cond emits to fn code to evaluate boolean condition e and jump -// to t or f depending on its value, performing various simplifications. -// -// Postcondition: fn.currentBlock is nil. -// -func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) { - switch e := e.(type) { - case *ast.ParenExpr: - b.cond(fn, e.X, t, f) - return - - case *ast.BinaryExpr: - switch e.Op { - case token.LAND: - ltrue := fn.newBasicBlock("cond.true") - b.cond(fn, e.X, ltrue, f) - fn.currentBlock = ltrue - b.cond(fn, e.Y, t, f) - return - - case token.LOR: - lfalse := fn.newBasicBlock("cond.false") - b.cond(fn, e.X, t, lfalse) - fn.currentBlock = lfalse - b.cond(fn, e.Y, t, f) - return - } - - case *ast.UnaryExpr: - if e.Op == token.NOT { - b.cond(fn, e.X, f, t) - return - } - } - - // A traditional compiler would simplify "if false" (etc) here - // but we do not, for better fidelity to the source code. - // - // The value of a constant condition may be platform-specific, - // and may cause blocks that are reachable in some configuration - // to be hidden from subsequent analyses such as bug-finding tools. - emitIf(fn, b.expr(fn, e), t, f) -} - -// logicalBinop emits code to fn to evaluate e, a &&- or -// ||-expression whose reified boolean value is wanted. -// The value is returned. -// -func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { - rhs := fn.newBasicBlock("binop.rhs") - done := fn.newBasicBlock("binop.done") - - // T(e) = T(e.X) = T(e.Y) after untyped constants have been - // eliminated. - // TODO(adonovan): not true; MyBool==MyBool yields UntypedBool. - t := fn.Pkg.typeOf(e) - - var short Value // value of the short-circuit path - switch e.Op { - case token.LAND: - b.cond(fn, e.X, rhs, done) - short = NewConst(exact.MakeBool(false), t) - - case token.LOR: - b.cond(fn, e.X, done, rhs) - short = NewConst(exact.MakeBool(true), t) - } - - // Is rhs unreachable? - if rhs.Preds == nil { - // Simplify false&&y to false, true||y to true. - fn.currentBlock = done - return short - } - - // Is done unreachable? - if done.Preds == nil { - // Simplify true&&y (or false||y) to y. - fn.currentBlock = rhs - return b.expr(fn, e.Y) - } - - // All edges from e.X to done carry the short-circuit value. - var edges []Value - for _ = range done.Preds { - edges = append(edges, short) - } - - // The edge from e.Y to done carries the value of e.Y. - fn.currentBlock = rhs - edges = append(edges, b.expr(fn, e.Y)) - emitJump(fn, done) - fn.currentBlock = done - - phi := &Phi{Edges: edges, Comment: e.Op.String()} - phi.pos = e.OpPos - phi.typ = t - return done.emit(phi) -} - -// exprN lowers a multi-result expression e to SSA form, emitting code -// to fn and returning a single Value whose type is a *types.Tuple. -// The caller must access the components via Extract. -// -// Multi-result expressions include CallExprs in a multi-value -// assignment or return statement, and "value,ok" uses of -// TypeAssertExpr, IndexExpr (when X is a map), and UnaryExpr (when Op -// is token.ARROW). -// -func (b *builder) exprN(fn *Function, e ast.Expr) Value { - typ := fn.Pkg.typeOf(e).(*types.Tuple) - switch e := e.(type) { - case *ast.ParenExpr: - return b.exprN(fn, e.X) - - case *ast.CallExpr: - // Currently, no built-in function nor type conversion - // has multiple results, so we can avoid some of the - // cases for single-valued CallExpr. - var c Call - b.setCall(fn, e, &c.Call) - c.typ = typ - return fn.emit(&c) - - case *ast.IndexExpr: - mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map) - lookup := &Lookup{ - X: b.expr(fn, e.X), - Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), - CommaOk: true, - } - lookup.setType(typ) - lookup.setPos(e.Lbrack) - return fn.emit(lookup) - - case *ast.TypeAssertExpr: - return emitTypeTest(fn, b.expr(fn, e.X), typ.At(0).Type(), e.Lparen) - - case *ast.UnaryExpr: // must be receive <- - unop := &UnOp{ - Op: token.ARROW, - X: b.expr(fn, e.X), - CommaOk: true, - } - unop.setType(typ) - unop.setPos(e.OpPos) - return fn.emit(unop) - } - panic(fmt.Sprintf("exprN(%T) in %s", e, fn)) -} - -// builtin emits to fn SSA instructions to implement a call to the -// built-in function obj with the specified arguments -// and return type. It returns the value defined by the result. -// -// The result is nil if no special handling was required; in this case -// the caller should treat this like an ordinary library function -// call. -// -func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, pos token.Pos) Value { - switch obj.Name() { - case "make": - switch typ.Underlying().(type) { - case *types.Slice: - n := b.expr(fn, args[1]) - m := n - if len(args) == 3 { - m = b.expr(fn, args[2]) - } - if m, ok := m.(*Const); ok { - // treat make([]T, n, m) as new([m]T)[:n] - cap, _ := exact.Int64Val(m.Value) - at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap) - alloc := emitNew(fn, at, pos) - alloc.Comment = "makeslice" - v := &Slice{ - X: alloc, - High: n, - } - v.setPos(pos) - v.setType(typ) - return fn.emit(v) - } - v := &MakeSlice{ - Len: n, - Cap: m, - } - v.setPos(pos) - v.setType(typ) - return fn.emit(v) - - case *types.Map: - var res Value - if len(args) == 2 { - res = b.expr(fn, args[1]) - } - v := &MakeMap{Reserve: res} - v.setPos(pos) - v.setType(typ) - return fn.emit(v) - - case *types.Chan: - var sz Value = vZero - if len(args) == 2 { - sz = b.expr(fn, args[1]) - } - v := &MakeChan{Size: sz} - v.setPos(pos) - v.setType(typ) - return fn.emit(v) - } - - case "new": - alloc := emitNew(fn, deref(typ), pos) - alloc.Comment = "new" - return alloc - - case "len", "cap": - // Special case: len or cap of an array or *array is - // based on the type, not the value which may be nil. - // We must still evaluate the value, though. (If it - // was side-effect free, the whole call would have - // been constant-folded.) - t := deref(fn.Pkg.typeOf(args[0])).Underlying() - if at, ok := t.(*types.Array); ok { - b.expr(fn, args[0]) // for effects only - return intConst(at.Len()) - } - // Otherwise treat as normal. - - case "panic": - fn.emit(&Panic{ - X: emitConv(fn, b.expr(fn, args[0]), tEface), - pos: pos, - }) - fn.currentBlock = fn.newBasicBlock("unreachable") - return vTrue // any non-nil Value will do - } - return nil // treat all others as a regular function call -} - -// addr lowers a single-result addressable expression e to SSA form, -// emitting code to fn and returning the location (an lvalue) defined -// by the expression. -// -// If escaping is true, addr marks the base variable of the -// addressable expression e as being a potentially escaping pointer -// value. For example, in this code: -// -// a := A{ -// b: [1]B{B{c: 1}} -// } -// return &a.b[0].c -// -// the application of & causes a.b[0].c to have its address taken, -// which means that ultimately the local variable a must be -// heap-allocated. This is a simple but very conservative escape -// analysis. -// -// Operations forming potentially escaping pointers include: -// - &x, including when implicit in method call or composite literals. -// - a[:] iff a is an array (not *array) -// - references to variables in lexically enclosing functions. -// -func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { - switch e := e.(type) { - case *ast.Ident: - if isBlankIdent(e) { - return blank{} - } - obj := fn.Pkg.objectOf(e) - v := fn.Prog.packageLevelValue(obj) // var (address) - if v == nil { - v = fn.lookup(obj, escaping) - } - return &address{addr: v, pos: e.Pos(), expr: e} - - case *ast.CompositeLit: - t := deref(fn.Pkg.typeOf(e)) - var v *Alloc - if escaping { - v = emitNew(fn, t, e.Lbrace) - } else { - v = fn.addLocal(t, e.Lbrace) - } - v.Comment = "complit" - var sb storebuf - b.compLit(fn, v, e, true, &sb) - sb.emit(fn) - return &address{addr: v, pos: e.Lbrace, expr: e} - - case *ast.ParenExpr: - return b.addr(fn, e.X, escaping) - - case *ast.SelectorExpr: - sel, ok := fn.Pkg.info.Selections[e] - if !ok { - // qualified identifier - return b.addr(fn, e.Sel, escaping) - } - if sel.Kind() != types.FieldVal { - panic(sel) - } - wantAddr := true - v := b.receiver(fn, e.X, wantAddr, escaping, sel) - last := len(sel.Index()) - 1 - return &address{ - addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel), - pos: e.Sel.Pos(), - expr: e.Sel, - } - - case *ast.IndexExpr: - var x Value - var et types.Type - switch t := fn.Pkg.typeOf(e.X).Underlying().(type) { - case *types.Array: - x = b.addr(fn, e.X, escaping).address(fn) - et = types.NewPointer(t.Elem()) - case *types.Pointer: // *array - x = b.expr(fn, e.X) - et = types.NewPointer(t.Elem().Underlying().(*types.Array).Elem()) - case *types.Slice: - x = b.expr(fn, e.X) - et = types.NewPointer(t.Elem()) - case *types.Map: - return &element{ - m: b.expr(fn, e.X), - k: emitConv(fn, b.expr(fn, e.Index), t.Key()), - t: t.Elem(), - pos: e.Lbrack, - } - default: - panic("unexpected container type in IndexExpr: " + t.String()) - } - v := &IndexAddr{ - X: x, - Index: emitConv(fn, b.expr(fn, e.Index), tInt), - } - v.setPos(e.Lbrack) - v.setType(et) - return &address{addr: fn.emit(v), pos: e.Lbrack, expr: e} - - case *ast.StarExpr: - return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e} - } - - panic(fmt.Sprintf("unexpected address expression: %T", e)) -} - -type store struct { - lhs lvalue - rhs Value -} - -type storebuf struct{ stores []store } - -func (sb *storebuf) store(lhs lvalue, rhs Value) { - sb.stores = append(sb.stores, store{lhs, rhs}) -} - -func (sb *storebuf) emit(fn *Function) { - for _, s := range sb.stores { - s.lhs.store(fn, s.rhs) - } -} - -// assign emits to fn code to initialize the lvalue loc with the value -// of expression e. If isZero is true, assign assumes that loc holds -// the zero value for its type. -// -// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate -// better code in some cases, e.g., for composite literals in an -// addressable location. -// -// If sb is not nil, assign generates code to evaluate expression e, but -// not to update loc. Instead, the necessary stores are appended to the -// storebuf sb so that they can be executed later. This allows correct -// in-place update of existing variables when the RHS is a composite -// literal that may reference parts of the LHS. -// -func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf) { - // Can we initialize it in place? - if e, ok := unparen(e).(*ast.CompositeLit); ok { - // A CompositeLit never evaluates to a pointer, - // so if the type of the location is a pointer, - // an &-operation is implied. - if _, ok := loc.(blank); !ok { // avoid calling blank.typ() - if isPointer(loc.typ()) { - ptr := b.addr(fn, e, true).address(fn) - // copy address - if sb != nil { - sb.store(loc, ptr) - } else { - loc.store(fn, ptr) - } - return - } - } - - if _, ok := loc.(*address); ok { - if isInterface(loc.typ()) { - // e.g. var x interface{} = T{...} - // Can't in-place initialize an interface value. - // Fall back to copying. - } else { - // x = T{...} or x := T{...} - addr := loc.address(fn) - if sb != nil { - b.compLit(fn, addr, e, isZero, sb) - } else { - var sb storebuf - b.compLit(fn, addr, e, isZero, &sb) - sb.emit(fn) - } - - // Subtle: emit debug ref for aggregate types only; - // slice and map are handled by store ops in compLit. - switch loc.typ().Underlying().(type) { - case *types.Struct, *types.Array: - emitDebugRef(fn, e, addr, true) - } - - return - } - } - } - - // simple case: just copy - rhs := b.expr(fn, e) - if sb != nil { - sb.store(loc, rhs) - } else { - loc.store(fn, rhs) - } -} - -// expr lowers a single-result expression e to SSA form, emitting code -// to fn and returning the Value defined by the expression. -// -func (b *builder) expr(fn *Function, e ast.Expr) Value { - e = unparen(e) - - tv := fn.Pkg.info.Types[e] - - // Is expression a constant? - if tv.Value != nil { - return NewConst(tv.Value, tv.Type) - } - - var v Value - if tv.Addressable() { - // Prefer pointer arithmetic ({Index,Field}Addr) followed - // by Load over subelement extraction (e.g. Index, Field), - // to avoid large copies. - v = b.addr(fn, e, false).load(fn) - } else { - v = b.expr0(fn, e, tv) - } - if fn.debugInfo() { - emitDebugRef(fn, e, v, false) - } - return v -} - -func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { - switch e := e.(type) { - case *ast.BasicLit: - panic("non-constant BasicLit") // unreachable - - case *ast.FuncLit: - fn2 := &Function{ - name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)), - Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature), - pos: e.Type.Func, - parent: fn, - Pkg: fn.Pkg, - Prog: fn.Prog, - syntax: e, - } - fn.AnonFuncs = append(fn.AnonFuncs, fn2) - b.buildFunction(fn2) - if fn2.FreeVars == nil { - return fn2 - } - v := &MakeClosure{Fn: fn2} - v.setType(tv.Type) - for _, fv := range fn2.FreeVars { - v.Bindings = append(v.Bindings, fv.outer) - fv.outer = nil - } - return fn.emit(v) - - case *ast.TypeAssertExpr: // single-result form only - return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e.Lparen) - - case *ast.CallExpr: - if fn.Pkg.info.Types[e.Fun].IsType() { - // Explicit type conversion, e.g. string(x) or big.Int(x) - x := b.expr(fn, e.Args[0]) - y := emitConv(fn, x, tv.Type) - if y != x { - switch y := y.(type) { - case *Convert: - y.pos = e.Lparen - case *ChangeType: - y.pos = e.Lparen - case *MakeInterface: - y.pos = e.Lparen - } - } - return y - } - // Call to "intrinsic" built-ins, e.g. new, make, panic. - if id, ok := unparen(e.Fun).(*ast.Ident); ok { - if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok { - if v := b.builtin(fn, obj, e.Args, tv.Type, e.Lparen); v != nil { - return v - } - } - } - // Regular function call. - var v Call - b.setCall(fn, e, &v.Call) - v.setType(tv.Type) - return fn.emit(&v) - - case *ast.UnaryExpr: - switch e.Op { - case token.AND: // &X --- potentially escaping. - addr := b.addr(fn, e.X, true) - if _, ok := unparen(e.X).(*ast.StarExpr); ok { - // &*p must panic if p is nil (http://golang.org/s/go12nil). - // For simplicity, we'll just (suboptimally) rely - // on the side effects of a load. - // TODO(adonovan): emit dedicated nilcheck. - addr.load(fn) - } - return addr.address(fn) - case token.ADD: - return b.expr(fn, e.X) - case token.NOT, token.ARROW, token.SUB, token.XOR: // ! <- - ^ - v := &UnOp{ - Op: e.Op, - X: b.expr(fn, e.X), - } - v.setPos(e.OpPos) - v.setType(tv.Type) - return fn.emit(v) - default: - panic(e.Op) - } - - case *ast.BinaryExpr: - switch e.Op { - case token.LAND, token.LOR: - return b.logicalBinop(fn, e) - case token.SHL, token.SHR: - fallthrough - case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: - return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e.OpPos) - - case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ: - cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos) - // The type of x==y may be UntypedBool. - return emitConv(fn, cmp, DefaultType(tv.Type)) - default: - panic("illegal op in BinaryExpr: " + e.Op.String()) - } - - case *ast.SliceExpr: - var low, high, max Value - var x Value - switch fn.Pkg.typeOf(e.X).Underlying().(type) { - case *types.Array: - // Potentially escaping. - x = b.addr(fn, e.X, true).address(fn) - case *types.Basic, *types.Slice, *types.Pointer: // *array - x = b.expr(fn, e.X) - default: - panic("unreachable") - } - if e.High != nil { - high = b.expr(fn, e.High) - } - if e.Low != nil { - low = b.expr(fn, e.Low) - } - if e.Slice3 { - max = b.expr(fn, e.Max) - } - v := &Slice{ - X: x, - Low: low, - High: high, - Max: max, - } - v.setPos(e.Lbrack) - v.setType(tv.Type) - return fn.emit(v) - - case *ast.Ident: - obj := fn.Pkg.info.Uses[e] - // Universal built-in or nil? - switch obj := obj.(type) { - case *types.Builtin: - return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)} - case *types.Nil: - return nilConst(tv.Type) - } - // Package-level func or var? - if v := fn.Prog.packageLevelValue(obj); v != nil { - if _, ok := obj.(*types.Var); ok { - return emitLoad(fn, v) // var (address) - } - return v // (func) - } - // Local var. - return emitLoad(fn, fn.lookup(obj, false)) // var (address) - - case *ast.SelectorExpr: - sel, ok := fn.Pkg.info.Selections[e] - if !ok { - // qualified identifier - return b.expr(fn, e.Sel) - } - switch sel.Kind() { - case types.MethodExpr: - // (*T).f or T.f, the method f from the method-set of type T. - // The result is a "thunk". - return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type) - - case types.MethodVal: - // e.f where e is an expression and f is a method. - // The result is a "bound". - obj := sel.Obj().(*types.Func) - rt := recvType(obj) - wantAddr := isPointer(rt) - escaping := true - v := b.receiver(fn, e.X, wantAddr, escaping, sel) - if isInterface(rt) { - // If v has interface type I, - // we must emit a check that v is non-nil. - // We use: typeassert v.(I). - emitTypeAssert(fn, v, rt, token.NoPos) - } - c := &MakeClosure{ - Fn: makeBound(fn.Prog, obj), - Bindings: []Value{v}, - } - c.setPos(e.Sel.Pos()) - c.setType(tv.Type) - return fn.emit(c) - - case types.FieldVal: - indices := sel.Index() - last := len(indices) - 1 - v := b.expr(fn, e.X) - v = emitImplicitSelections(fn, v, indices[:last]) - v = emitFieldSelection(fn, v, indices[last], false, e.Sel) - return v - } - - panic("unexpected expression-relative selector") - - case *ast.IndexExpr: - switch t := fn.Pkg.typeOf(e.X).Underlying().(type) { - case *types.Array: - // Non-addressable array (in a register). - v := &Index{ - X: b.expr(fn, e.X), - Index: emitConv(fn, b.expr(fn, e.Index), tInt), - } - v.setPos(e.Lbrack) - v.setType(t.Elem()) - return fn.emit(v) - - case *types.Map: - // Maps are not addressable. - mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map) - v := &Lookup{ - X: b.expr(fn, e.X), - Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), - } - v.setPos(e.Lbrack) - v.setType(mapt.Elem()) - return fn.emit(v) - - case *types.Basic: // => string - // Strings are not addressable. - v := &Lookup{ - X: b.expr(fn, e.X), - Index: b.expr(fn, e.Index), - } - v.setPos(e.Lbrack) - v.setType(tByte) - return fn.emit(v) - - case *types.Slice, *types.Pointer: // *array - // Addressable slice/array; use IndexAddr and Load. - return b.addr(fn, e, false).load(fn) - - default: - panic("unexpected container type in IndexExpr: " + t.String()) - } - - case *ast.CompositeLit, *ast.StarExpr: - // Addressable types (lvalues) - return b.addr(fn, e, false).load(fn) - } - - panic(fmt.Sprintf("unexpected expr: %T", e)) -} - -// stmtList emits to fn code for all statements in list. -func (b *builder) stmtList(fn *Function, list []ast.Stmt) { - for _, s := range list { - b.stmt(fn, s) - } -} - -// receiver emits to fn code for expression e in the "receiver" -// position of selection e.f (where f may be a field or a method) and -// returns the effective receiver after applying the implicit field -// selections of sel. -// -// wantAddr requests that the result is an an address. If -// !sel.Indirect(), this may require that e be built in addr() mode; it -// must thus be addressable. -// -// escaping is defined as per builder.addr(). -// -func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection) Value { - var v Value - if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) { - v = b.addr(fn, e, escaping).address(fn) - } else { - v = b.expr(fn, e) - } - - last := len(sel.Index()) - 1 - v = emitImplicitSelections(fn, v, sel.Index()[:last]) - if !wantAddr && isPointer(v.Type()) { - v = emitLoad(fn, v) - } - return v -} - -// setCallFunc populates the function parts of a CallCommon structure -// (Func, Method, Recv, Args[0]) based on the kind of invocation -// occurring in e. -// -func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { - c.pos = e.Lparen - - // Is this a method call? - if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok { - sel, ok := fn.Pkg.info.Selections[selector] - if ok && sel.Kind() == types.MethodVal { - obj := sel.Obj().(*types.Func) - recv := recvType(obj) - wantAddr := isPointer(recv) - escaping := true - v := b.receiver(fn, selector.X, wantAddr, escaping, sel) - if isInterface(recv) { - // Invoke-mode call. - c.Value = v - c.Method = obj - } else { - // "Call"-mode call. - c.Value = fn.Prog.declaredFunc(obj) - c.Args = append(c.Args, v) - } - return - } - - // sel.Kind()==MethodExpr indicates T.f() or (*T).f(): - // a statically dispatched call to the method f in the - // method-set of T or *T. T may be an interface. - // - // e.Fun would evaluate to a concrete method, interface - // wrapper function, or promotion wrapper. - // - // For now, we evaluate it in the usual way. - // - // TODO(adonovan): opt: inline expr() here, to make the - // call static and to avoid generation of wrappers. - // It's somewhat tricky as it may consume the first - // actual parameter if the call is "invoke" mode. - // - // Examples: - // type T struct{}; func (T) f() {} // "call" mode - // type T interface { f() } // "invoke" mode - // - // type S struct{ T } - // - // var s S - // S.f(s) - // (*S).f(&s) - // - // Suggested approach: - // - consume the first actual parameter expression - // and build it with b.expr(). - // - apply implicit field selections. - // - use MethodVal logic to populate fields of c. - } - - // Evaluate the function operand in the usual way. - c.Value = b.expr(fn, e.Fun) -} - -// emitCallArgs emits to f code for the actual parameters of call e to -// a (possibly built-in) function of effective type sig. -// The argument values are appended to args, which is then returned. -// -func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value { - // f(x, y, z...): pass slice z straight through. - if e.Ellipsis != 0 { - for i, arg := range e.Args { - v := emitConv(fn, b.expr(fn, arg), sig.Params().At(i).Type()) - args = append(args, v) - } - return args - } - - offset := len(args) // 1 if call has receiver, 0 otherwise - - // Evaluate actual parameter expressions. - // - // If this is a chained call of the form f(g()) where g has - // multiple return values (MRV), they are flattened out into - // args; a suffix of them may end up in a varargs slice. - for _, arg := range e.Args { - v := b.expr(fn, arg) - if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain - for i, n := 0, ttuple.Len(); i < n; i++ { - args = append(args, emitExtract(fn, v, i)) - } - } else { - args = append(args, v) - } - } - - // Actual->formal assignability conversions for normal parameters. - np := sig.Params().Len() // number of normal parameters - if sig.Variadic() { - np-- - } - for i := 0; i < np; i++ { - args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type()) - } - - // Actual->formal assignability conversions for variadic parameter, - // and construction of slice. - if sig.Variadic() { - varargs := args[offset+np:] - st := sig.Params().At(np).Type().(*types.Slice) - vt := st.Elem() - if len(varargs) == 0 { - args = append(args, nilConst(st)) - } else { - // Replace a suffix of args with a slice containing it. - at := types.NewArray(vt, int64(len(varargs))) - a := emitNew(fn, at, token.NoPos) - a.setPos(e.Rparen) - a.Comment = "varargs" - for i, arg := range varargs { - iaddr := &IndexAddr{ - X: a, - Index: intConst(int64(i)), - } - iaddr.setType(types.NewPointer(vt)) - fn.emit(iaddr) - emitStore(fn, iaddr, arg, arg.Pos()) - } - s := &Slice{X: a} - s.setType(st) - args[offset+np] = fn.emit(s) - args = args[:offset+np+1] - } - } - return args -} - -// setCall emits to fn code to evaluate all the parameters of a function -// call e, and populates *c with those values. -// -func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { - // First deal with the f(...) part and optional receiver. - b.setCallFunc(fn, e, c) - - // Then append the other actual parameters. - sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature) - if sig == nil { - panic(fmt.Sprintf("no signature for call of %s", e.Fun)) - } - c.Args = b.emitCallArgs(fn, sig, e, c.Args) -} - -// assignOp emits to fn code to perform loc += incr or loc -= incr. -func (b *builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token) { - oldv := loc.load(fn) - loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ(), token.NoPos)) -} - -// localValueSpec emits to fn code to define all of the vars in the -// function-local ValueSpec, spec. -// -func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { - switch { - case len(spec.Values) == len(spec.Names): - // e.g. var x, y = 0, 1 - // 1:1 assignment - for i, id := range spec.Names { - if !isBlankIdent(id) { - fn.addLocalForIdent(id) - } - lval := b.addr(fn, id, false) // non-escaping - b.assign(fn, lval, spec.Values[i], true, nil) - } - - case len(spec.Values) == 0: - // e.g. var x, y int - // Locals are implicitly zero-initialized. - for _, id := range spec.Names { - if !isBlankIdent(id) { - lhs := fn.addLocalForIdent(id) - if fn.debugInfo() { - emitDebugRef(fn, id, lhs, true) - } - } - } - - default: - // e.g. var x, y = pos() - tuple := b.exprN(fn, spec.Values[0]) - for i, id := range spec.Names { - if !isBlankIdent(id) { - fn.addLocalForIdent(id) - lhs := b.addr(fn, id, false) // non-escaping - lhs.store(fn, emitExtract(fn, tuple, i)) - } - } - } -} - -// assignStmt emits code to fn for a parallel assignment of rhss to lhss. -// isDef is true if this is a short variable declaration (:=). -// -// Note the similarity with localValueSpec. -// -func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) { - // Side effects of all LHSs and RHSs must occur in left-to-right order. - lvals := make([]lvalue, len(lhss)) - isZero := make([]bool, len(lhss)) - for i, lhs := range lhss { - var lval lvalue = blank{} - if !isBlankIdent(lhs) { - if isDef { - if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil { - fn.addNamedLocal(obj) - isZero[i] = true - } - } - lval = b.addr(fn, lhs, false) // non-escaping - } - lvals[i] = lval - } - if len(lhss) == len(rhss) { - // Simple assignment: x = f() (!isDef) - // Parallel assignment: x, y = f(), g() (!isDef) - // or short var decl: x, y := f(), g() (isDef) - // - // In all cases, the RHSs may refer to the LHSs, - // so we need a storebuf. - var sb storebuf - for i := range rhss { - b.assign(fn, lvals[i], rhss[i], isZero[i], &sb) - } - sb.emit(fn) - } else { - // e.g. x, y = pos() - tuple := b.exprN(fn, rhss[0]) - emitDebugRef(fn, rhss[0], tuple, false) - for i, lval := range lvals { - lval.store(fn, emitExtract(fn, tuple, i)) - } - } -} - -// arrayLen returns the length of the array whose composite literal elements are elts. -func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 { - var max int64 = -1 - var i int64 = -1 - for _, e := range elts { - if kv, ok := e.(*ast.KeyValueExpr); ok { - i = b.expr(fn, kv.Key).(*Const).Int64() - } else { - i++ - } - if i > max { - max = i - } - } - return max + 1 -} - -// compLit emits to fn code to initialize a composite literal e at -// address addr with type typ. -// -// Nested composite literals are recursively initialized in place -// where possible. If isZero is true, compLit assumes that addr -// holds the zero value for typ. -// -// Because the elements of a composite literal may refer to the -// variables being updated, as in the second line below, -// x := T{a: 1} -// x = T{a: x.a} -// all the reads must occur before all the writes. Thus all stores to -// loc are emitted to the storebuf sb for later execution. -// -// A CompositeLit may have pointer type only in the recursive (nested) -// case when the type name is implicit. e.g. in []*T{{}}, the inner -// literal has type *T behaves like &T{}. -// In that case, addr must hold a T, not a *T. -// -func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) { - typ := deref(fn.Pkg.typeOf(e)) - switch t := typ.Underlying().(type) { - case *types.Struct: - if !isZero && len(e.Elts) != t.NumFields() { - // memclear - sb.store(&address{addr, e.Lbrace, nil}, - zeroValue(fn, deref(addr.Type()))) - isZero = true - } - for i, e := range e.Elts { - fieldIndex := i - pos := e.Pos() - if kv, ok := e.(*ast.KeyValueExpr); ok { - fname := kv.Key.(*ast.Ident).Name - for i, n := 0, t.NumFields(); i < n; i++ { - sf := t.Field(i) - if sf.Name() == fname { - fieldIndex = i - pos = kv.Colon - e = kv.Value - break - } - } - } - sf := t.Field(fieldIndex) - faddr := &FieldAddr{ - X: addr, - Field: fieldIndex, - } - faddr.setType(types.NewPointer(sf.Type())) - fn.emit(faddr) - b.assign(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero, sb) - } - - case *types.Array, *types.Slice: - var at *types.Array - var array Value - switch t := t.(type) { - case *types.Slice: - at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts)) - alloc := emitNew(fn, at, e.Lbrace) - alloc.Comment = "slicelit" - array = alloc - case *types.Array: - at = t - array = addr - - if !isZero && int64(len(e.Elts)) != at.Len() { - // memclear - sb.store(&address{array, e.Lbrace, nil}, - zeroValue(fn, deref(array.Type()))) - } - } - - var idx *Const - for _, e := range e.Elts { - pos := e.Pos() - if kv, ok := e.(*ast.KeyValueExpr); ok { - idx = b.expr(fn, kv.Key).(*Const) - pos = kv.Colon - e = kv.Value - } else { - var idxval int64 - if idx != nil { - idxval = idx.Int64() + 1 - } - idx = intConst(idxval) - } - iaddr := &IndexAddr{ - X: array, - Index: idx, - } - iaddr.setType(types.NewPointer(at.Elem())) - fn.emit(iaddr) - if t != at { // slice - // backing array is unaliased => storebuf not needed. - b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, nil) - } else { - b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, sb) - } - } - - if t != at { // slice - s := &Slice{X: array} - s.setPos(e.Lbrace) - s.setType(typ) - sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, fn.emit(s)) - } - - case *types.Map: - m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))} - m.setPos(e.Lbrace) - m.setType(typ) - fn.emit(m) - for _, e := range e.Elts { - e := e.(*ast.KeyValueExpr) - - // If a key expression in a map literal is itself a - // composite literal, the type may be omitted. - // For example: - // map[*struct{}]bool{{}: true} - // An &-operation may be implied: - // map[*struct{}]bool{&struct{}{}: true} - var key Value - if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) { - // A CompositeLit never evaluates to a pointer, - // so if the type of the location is a pointer, - // an &-operation is implied. - key = b.addr(fn, e.Key, true).address(fn) - } else { - key = b.expr(fn, e.Key) - } - - loc := element{ - m: m, - k: emitConv(fn, key, t.Key()), - t: t.Elem(), - pos: e.Colon, - } - - // We call assign() only because it takes care - // of any &-operation required in the recursive - // case, e.g., - // map[int]*struct{}{0: {}} implies &struct{}{}. - // In-place update is of course impossible, - // and no storebuf is needed. - b.assign(fn, &loc, e.Value, true, nil) - } - sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m) - - default: - panic("unexpected CompositeLit type: " + t.String()) - } -} - -// switchStmt emits to fn code for the switch statement s, optionally -// labelled by label. -// -func (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) { - // We treat SwitchStmt like a sequential if-else chain. - // Multiway dispatch can be recovered later by ssautil.Switches() - // to those cases that are free of side effects. - if s.Init != nil { - b.stmt(fn, s.Init) - } - var tag Value = vTrue - if s.Tag != nil { - tag = b.expr(fn, s.Tag) - } - done := fn.newBasicBlock("switch.done") - if label != nil { - label._break = done - } - // We pull the default case (if present) down to the end. - // But each fallthrough label must point to the next - // body block in source order, so we preallocate a - // body block (fallthru) for the next case. - // Unfortunately this makes for a confusing block order. - var dfltBody *[]ast.Stmt - var dfltFallthrough *BasicBlock - var fallthru, dfltBlock *BasicBlock - ncases := len(s.Body.List) - for i, clause := range s.Body.List { - body := fallthru - if body == nil { - body = fn.newBasicBlock("switch.body") // first case only - } - - // Preallocate body block for the next case. - fallthru = done - if i+1 < ncases { - fallthru = fn.newBasicBlock("switch.body") - } - - cc := clause.(*ast.CaseClause) - if cc.List == nil { - // Default case. - dfltBody = &cc.Body - dfltFallthrough = fallthru - dfltBlock = body - continue - } - - var nextCond *BasicBlock - for _, cond := range cc.List { - nextCond = fn.newBasicBlock("switch.next") - // TODO(adonovan): opt: when tag==vTrue, we'd - // get better code if we use b.cond(cond) - // instead of BinOp(EQL, tag, b.expr(cond)) - // followed by If. Don't forget conversions - // though. - cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond), token.NoPos) - emitIf(fn, cond, body, nextCond) - fn.currentBlock = nextCond - } - fn.currentBlock = body - fn.targets = &targets{ - tail: fn.targets, - _break: done, - _fallthrough: fallthru, - } - b.stmtList(fn, cc.Body) - fn.targets = fn.targets.tail - emitJump(fn, done) - fn.currentBlock = nextCond - } - if dfltBlock != nil { - emitJump(fn, dfltBlock) - fn.currentBlock = dfltBlock - fn.targets = &targets{ - tail: fn.targets, - _break: done, - _fallthrough: dfltFallthrough, - } - b.stmtList(fn, *dfltBody) - fn.targets = fn.targets.tail - } - emitJump(fn, done) - fn.currentBlock = done -} - -// typeSwitchStmt emits to fn code for the type switch statement s, optionally -// labelled by label. -// -func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) { - // We treat TypeSwitchStmt like a sequential if-else chain. - // Multiway dispatch can be recovered later by ssautil.Switches(). - - // Typeswitch lowering: - // - // var x X - // switch y := x.(type) { - // case T1, T2: S1 // >1 (y := x) - // case nil: SN // nil (y := x) - // default: SD // 0 types (y := x) - // case T3: S3 // 1 type (y := x.(T3)) - // } - // - // ...s.Init... - // x := eval x - // .caseT1: - // t1, ok1 := typeswitch,ok x - // if ok1 then goto S1 else goto .caseT2 - // .caseT2: - // t2, ok2 := typeswitch,ok x - // if ok2 then goto S1 else goto .caseNil - // .S1: - // y := x - // ...S1... - // goto done - // .caseNil: - // if t2, ok2 := typeswitch,ok x - // if x == nil then goto SN else goto .caseT3 - // .SN: - // y := x - // ...SN... - // goto done - // .caseT3: - // t3, ok3 := typeswitch,ok x - // if ok3 then goto S3 else goto default - // .S3: - // y := t3 - // ...S3... - // goto done - // .default: - // y := x - // ...SD... - // goto done - // .done: - - if s.Init != nil { - b.stmt(fn, s.Init) - } - - var x Value - switch ass := s.Assign.(type) { - case *ast.ExprStmt: // x.(type) - x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X) - case *ast.AssignStmt: // y := x.(type) - x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X) - } - - done := fn.newBasicBlock("typeswitch.done") - if label != nil { - label._break = done - } - var default_ *ast.CaseClause - for _, clause := range s.Body.List { - cc := clause.(*ast.CaseClause) - if cc.List == nil { - default_ = cc - continue - } - body := fn.newBasicBlock("typeswitch.body") - var next *BasicBlock - var casetype types.Type - var ti Value // ti, ok := typeassert,ok x - for _, cond := range cc.List { - next = fn.newBasicBlock("typeswitch.next") - casetype = fn.Pkg.typeOf(cond) - var condv Value - if casetype == tUntypedNil { - condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos) - ti = x - } else { - yok := emitTypeTest(fn, x, casetype, cc.Case) - ti = emitExtract(fn, yok, 0) - condv = emitExtract(fn, yok, 1) - } - emitIf(fn, condv, body, next) - fn.currentBlock = next - } - if len(cc.List) != 1 { - ti = x - } - fn.currentBlock = body - b.typeCaseBody(fn, cc, ti, done) - fn.currentBlock = next - } - if default_ != nil { - b.typeCaseBody(fn, default_, x, done) - } else { - emitJump(fn, done) - } - fn.currentBlock = done -} - -func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) { - if obj := fn.Pkg.info.Implicits[cc]; obj != nil { - // In a switch y := x.(type), each case clause - // implicitly declares a distinct object y. - // In a single-type case, y has that type. - // In multi-type cases, 'case nil' and default, - // y has the same type as the interface operand. - emitStore(fn, fn.addNamedLocal(obj), x, obj.Pos()) - } - fn.targets = &targets{ - tail: fn.targets, - _break: done, - } - b.stmtList(fn, cc.Body) - fn.targets = fn.targets.tail - emitJump(fn, done) -} - -// selectStmt emits to fn code for the select statement s, optionally -// labelled by label. -// -func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { - // A blocking select of a single case degenerates to a - // simple send or receive. - // TODO(adonovan): opt: is this optimization worth its weight? - if len(s.Body.List) == 1 { - clause := s.Body.List[0].(*ast.CommClause) - if clause.Comm != nil { - b.stmt(fn, clause.Comm) - done := fn.newBasicBlock("select.done") - if label != nil { - label._break = done - } - fn.targets = &targets{ - tail: fn.targets, - _break: done, - } - b.stmtList(fn, clause.Body) - fn.targets = fn.targets.tail - emitJump(fn, done) - fn.currentBlock = done - return - } - } - - // First evaluate all channels in all cases, and find - // the directions of each state. - var states []*SelectState - blocking := true - debugInfo := fn.debugInfo() - for _, clause := range s.Body.List { - var st *SelectState - switch comm := clause.(*ast.CommClause).Comm.(type) { - case nil: // default case - blocking = false - continue - - case *ast.SendStmt: // ch<- i - ch := b.expr(fn, comm.Chan) - st = &SelectState{ - Dir: types.SendOnly, - Chan: ch, - Send: emitConv(fn, b.expr(fn, comm.Value), - ch.Type().Underlying().(*types.Chan).Elem()), - Pos: comm.Arrow, - } - if debugInfo { - st.DebugNode = comm - } - - case *ast.AssignStmt: // x := <-ch - recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr) - st = &SelectState{ - Dir: types.RecvOnly, - Chan: b.expr(fn, recv.X), - Pos: recv.OpPos, - } - if debugInfo { - st.DebugNode = recv - } - - case *ast.ExprStmt: // <-ch - recv := unparen(comm.X).(*ast.UnaryExpr) - st = &SelectState{ - Dir: types.RecvOnly, - Chan: b.expr(fn, recv.X), - Pos: recv.OpPos, - } - if debugInfo { - st.DebugNode = recv - } - } - states = append(states, st) - } - - // We dispatch on the (fair) result of Select using a - // sequential if-else chain, in effect: - // - // idx, recvOk, r0...r_n-1 := select(...) - // if idx == 0 { // receive on channel 0 (first receive => r0) - // x, ok := r0, recvOk - // ...state0... - // } else if v == 1 { // send on channel 1 - // ...state1... - // } else { - // ...default... - // } - sel := &Select{ - States: states, - Blocking: blocking, - } - sel.setPos(s.Select) - var vars []*types.Var - vars = append(vars, varIndex, varOk) - for _, st := range states { - if st.Dir == types.RecvOnly { - tElem := st.Chan.Type().Underlying().(*types.Chan).Elem() - vars = append(vars, anonVar(tElem)) - } - } - sel.setType(types.NewTuple(vars...)) - - fn.emit(sel) - idx := emitExtract(fn, sel, 0) - - done := fn.newBasicBlock("select.done") - if label != nil { - label._break = done - } - - var defaultBody *[]ast.Stmt - state := 0 - r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV - for _, cc := range s.Body.List { - clause := cc.(*ast.CommClause) - if clause.Comm == nil { - defaultBody = &clause.Body - continue - } - body := fn.newBasicBlock("select.body") - next := fn.newBasicBlock("select.next") - emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next) - fn.currentBlock = body - fn.targets = &targets{ - tail: fn.targets, - _break: done, - } - switch comm := clause.Comm.(type) { - case *ast.ExprStmt: // <-ch - if debugInfo { - v := emitExtract(fn, sel, r) - emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false) - } - r++ - - case *ast.AssignStmt: // x := <-states[state].Chan - if comm.Tok == token.DEFINE { - fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident)) - } - x := b.addr(fn, comm.Lhs[0], false) // non-escaping - v := emitExtract(fn, sel, r) - if debugInfo { - emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false) - } - x.store(fn, v) - - if len(comm.Lhs) == 2 { // x, ok := ... - if comm.Tok == token.DEFINE { - fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident)) - } - ok := b.addr(fn, comm.Lhs[1], false) // non-escaping - ok.store(fn, emitExtract(fn, sel, 1)) - } - r++ - } - b.stmtList(fn, clause.Body) - fn.targets = fn.targets.tail - emitJump(fn, done) - fn.currentBlock = next - state++ - } - if defaultBody != nil { - fn.targets = &targets{ - tail: fn.targets, - _break: done, - } - b.stmtList(fn, *defaultBody) - fn.targets = fn.targets.tail - } else { - // A blocking select must match some case. - // (This should really be a runtime.errorString, not a string.) - fn.emit(&Panic{ - X: emitConv(fn, stringConst("blocking select matched no case"), tEface), - }) - fn.currentBlock = fn.newBasicBlock("unreachable") - } - emitJump(fn, done) - fn.currentBlock = done -} - -// forStmt emits to fn code for the for statement s, optionally -// labelled by label. -// -func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) { - // ...init... - // jump loop - // loop: - // if cond goto body else done - // body: - // ...body... - // jump post - // post: (target of continue) - // ...post... - // jump loop - // done: (target of break) - if s.Init != nil { - b.stmt(fn, s.Init) - } - body := fn.newBasicBlock("for.body") - done := fn.newBasicBlock("for.done") // target of 'break' - loop := body // target of back-edge - if s.Cond != nil { - loop = fn.newBasicBlock("for.loop") - } - cont := loop // target of 'continue' - if s.Post != nil { - cont = fn.newBasicBlock("for.post") - } - if label != nil { - label._break = done - label._continue = cont - } - emitJump(fn, loop) - fn.currentBlock = loop - if loop != body { - b.cond(fn, s.Cond, body, done) - fn.currentBlock = body - } - fn.targets = &targets{ - tail: fn.targets, - _break: done, - _continue: cont, - } - b.stmt(fn, s.Body) - fn.targets = fn.targets.tail - emitJump(fn, cont) - - if s.Post != nil { - fn.currentBlock = cont - b.stmt(fn, s.Post) - emitJump(fn, loop) // back-edge - } - fn.currentBlock = done -} - -// rangeIndexed emits to fn the header for an integer-indexed loop -// over array, *array or slice value x. -// The v result is defined only if tv is non-nil. -// forPos is the position of the "for" token. -// -func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) { - // - // length = len(x) - // index = -1 - // loop: (target of continue) - // index++ - // if index < length goto body else done - // body: - // k = index - // v = x[index] - // ...body... - // jump loop - // done: (target of break) - - // Determine number of iterations. - var length Value - if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok { - // For array or *array, the number of iterations is - // known statically thanks to the type. We avoid a - // data dependence upon x, permitting later dead-code - // elimination if x is pure, static unrolling, etc. - // Ranging over a nil *array may have >0 iterations. - // We still generate code for x, in case it has effects. - length = intConst(arr.Len()) - } else { - // length = len(x). - var c Call - c.Call.Value = makeLen(x.Type()) - c.Call.Args = []Value{x} - c.setType(tInt) - length = fn.emit(&c) - } - - index := fn.addLocal(tInt, token.NoPos) - emitStore(fn, index, intConst(-1), pos) - - loop = fn.newBasicBlock("rangeindex.loop") - emitJump(fn, loop) - fn.currentBlock = loop - - incr := &BinOp{ - Op: token.ADD, - X: emitLoad(fn, index), - Y: vOne, - } - incr.setType(tInt) - emitStore(fn, index, fn.emit(incr), pos) - - body := fn.newBasicBlock("rangeindex.body") - done = fn.newBasicBlock("rangeindex.done") - emitIf(fn, emitCompare(fn, token.LSS, incr, length, token.NoPos), body, done) - fn.currentBlock = body - - k = emitLoad(fn, index) - if tv != nil { - switch t := x.Type().Underlying().(type) { - case *types.Array: - instr := &Index{ - X: x, - Index: k, - } - instr.setType(t.Elem()) - v = fn.emit(instr) - - case *types.Pointer: // *array - instr := &IndexAddr{ - X: x, - Index: k, - } - instr.setType(types.NewPointer(t.Elem().Underlying().(*types.Array).Elem())) - v = emitLoad(fn, fn.emit(instr)) - - case *types.Slice: - instr := &IndexAddr{ - X: x, - Index: k, - } - instr.setType(types.NewPointer(t.Elem())) - v = emitLoad(fn, fn.emit(instr)) - - default: - panic("rangeIndexed x:" + t.String()) - } - } - return -} - -// rangeIter emits to fn the header for a loop using -// Range/Next/Extract to iterate over map or string value x. -// tk and tv are the types of the key/value results k and v, or nil -// if the respective component is not wanted. -// -func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) { - // - // it = range x - // loop: (target of continue) - // okv = next it (ok, key, value) - // ok = extract okv #0 - // if ok goto body else done - // body: - // k = extract okv #1 - // v = extract okv #2 - // ...body... - // jump loop - // done: (target of break) - // - - if tk == nil { - tk = tInvalid - } - if tv == nil { - tv = tInvalid - } - - rng := &Range{X: x} - rng.setPos(pos) - rng.setType(tRangeIter) - it := fn.emit(rng) - - loop = fn.newBasicBlock("rangeiter.loop") - emitJump(fn, loop) - fn.currentBlock = loop - - _, isString := x.Type().Underlying().(*types.Basic) - - okv := &Next{ - Iter: it, - IsString: isString, - } - okv.setType(types.NewTuple( - varOk, - newVar("k", tk), - newVar("v", tv), - )) - fn.emit(okv) - - body := fn.newBasicBlock("rangeiter.body") - done = fn.newBasicBlock("rangeiter.done") - emitIf(fn, emitExtract(fn, okv, 0), body, done) - fn.currentBlock = body - - if tk != tInvalid { - k = emitExtract(fn, okv, 1) - } - if tv != tInvalid { - v = emitExtract(fn, okv, 2) - } - return -} - -// rangeChan emits to fn the header for a loop that receives from -// channel x until it fails. -// tk is the channel's element type, or nil if the k result is -// not wanted -// pos is the position of the '=' or ':=' token. -// -func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, pos token.Pos) (k Value, loop, done *BasicBlock) { - // - // loop: (target of continue) - // ko = <-x (key, ok) - // ok = extract ko #1 - // if ok goto body else done - // body: - // k = extract ko #0 - // ... - // goto loop - // done: (target of break) - - loop = fn.newBasicBlock("rangechan.loop") - emitJump(fn, loop) - fn.currentBlock = loop - recv := &UnOp{ - Op: token.ARROW, - X: x, - CommaOk: true, - } - recv.setPos(pos) - recv.setType(types.NewTuple( - newVar("k", x.Type().Underlying().(*types.Chan).Elem()), - varOk, - )) - ko := fn.emit(recv) - body := fn.newBasicBlock("rangechan.body") - done = fn.newBasicBlock("rangechan.done") - emitIf(fn, emitExtract(fn, ko, 1), body, done) - fn.currentBlock = body - if tk != nil { - k = emitExtract(fn, ko, 0) - } - return -} - -// rangeStmt emits to fn code for the range statement s, optionally -// labelled by label. -// -func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { - var tk, tv types.Type - if s.Key != nil && !isBlankIdent(s.Key) { - tk = fn.Pkg.typeOf(s.Key) - } - if s.Value != nil && !isBlankIdent(s.Value) { - tv = fn.Pkg.typeOf(s.Value) - } - - // If iteration variables are defined (:=), this - // occurs once outside the loop. - // - // Unlike a short variable declaration, a RangeStmt - // using := never redeclares an existing variable; it - // always creates a new one. - if s.Tok == token.DEFINE { - if tk != nil { - fn.addLocalForIdent(s.Key.(*ast.Ident)) - } - if tv != nil { - fn.addLocalForIdent(s.Value.(*ast.Ident)) - } - } - - x := b.expr(fn, s.X) - - var k, v Value - var loop, done *BasicBlock - switch rt := x.Type().Underlying().(type) { - case *types.Slice, *types.Array, *types.Pointer: // *array - k, v, loop, done = b.rangeIndexed(fn, x, tv, s.For) - - case *types.Chan: - k, loop, done = b.rangeChan(fn, x, tk, s.For) - - case *types.Map, *types.Basic: // string - k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For) - - default: - panic("Cannot range over: " + rt.String()) - } - - // Evaluate both LHS expressions before we update either. - var kl, vl lvalue - if tk != nil { - kl = b.addr(fn, s.Key, false) // non-escaping - } - if tv != nil { - vl = b.addr(fn, s.Value, false) // non-escaping - } - if tk != nil { - kl.store(fn, k) - } - if tv != nil { - vl.store(fn, v) - } - - if label != nil { - label._break = done - label._continue = loop - } - - fn.targets = &targets{ - tail: fn.targets, - _break: done, - _continue: loop, - } - b.stmt(fn, s.Body) - fn.targets = fn.targets.tail - emitJump(fn, loop) // back-edge - fn.currentBlock = done -} - -// stmt lowers statement s to SSA form, emitting code to fn. -func (b *builder) stmt(fn *Function, _s ast.Stmt) { - // The label of the current statement. If non-nil, its _goto - // target is always set; its _break and _continue are set only - // within the body of switch/typeswitch/select/for/range. - // It is effectively an additional default-nil parameter of stmt(). - var label *lblock -start: - switch s := _s.(type) { - case *ast.EmptyStmt: - // ignore. (Usually removed by gofmt.) - - case *ast.DeclStmt: // Con, Var or Typ - d := s.Decl.(*ast.GenDecl) - if d.Tok == token.VAR { - for _, spec := range d.Specs { - if vs, ok := spec.(*ast.ValueSpec); ok { - b.localValueSpec(fn, vs) - } - } - } - - case *ast.LabeledStmt: - label = fn.labelledBlock(s.Label) - emitJump(fn, label._goto) - fn.currentBlock = label._goto - _s = s.Stmt - goto start // effectively: tailcall stmt(fn, s.Stmt, label) - - case *ast.ExprStmt: - b.expr(fn, s.X) - - case *ast.SendStmt: - fn.emit(&Send{ - Chan: b.expr(fn, s.Chan), - X: emitConv(fn, b.expr(fn, s.Value), - fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()), - pos: s.Arrow, - }) - - case *ast.IncDecStmt: - op := token.ADD - if s.Tok == token.DEC { - op = token.SUB - } - loc := b.addr(fn, s.X, false) - b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op) - - case *ast.AssignStmt: - switch s.Tok { - case token.ASSIGN, token.DEFINE: - b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE) - - default: // +=, etc. - op := s.Tok + token.ADD - token.ADD_ASSIGN - b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op) - } - - case *ast.GoStmt: - // The "intrinsics" new/make/len/cap are forbidden here. - // panic is treated like an ordinary function call. - v := Go{pos: s.Go} - b.setCall(fn, s.Call, &v.Call) - fn.emit(&v) - - case *ast.DeferStmt: - // The "intrinsics" new/make/len/cap are forbidden here. - // panic is treated like an ordinary function call. - v := Defer{pos: s.Defer} - b.setCall(fn, s.Call, &v.Call) - fn.emit(&v) - - // A deferred call can cause recovery from panic, - // and control resumes at the Recover block. - createRecoverBlock(fn) - - case *ast.ReturnStmt: - var results []Value - if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 { - // Return of one expression in a multi-valued function. - tuple := b.exprN(fn, s.Results[0]) - ttuple := tuple.Type().(*types.Tuple) - for i, n := 0, ttuple.Len(); i < n; i++ { - results = append(results, - emitConv(fn, emitExtract(fn, tuple, i), - fn.Signature.Results().At(i).Type())) - } - } else { - // 1:1 return, or no-arg return in non-void function. - for i, r := range s.Results { - v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type()) - results = append(results, v) - } - } - if fn.namedResults != nil { - // Function has named result parameters (NRPs). - // Perform parallel assignment of return operands to NRPs. - for i, r := range results { - emitStore(fn, fn.namedResults[i], r, s.Return) - } - } - // Run function calls deferred in this - // function when explicitly returning from it. - fn.emit(new(RunDefers)) - if fn.namedResults != nil { - // Reload NRPs to form the result tuple. - results = results[:0] - for _, r := range fn.namedResults { - results = append(results, emitLoad(fn, r)) - } - } - fn.emit(&Return{Results: results, pos: s.Return}) - fn.currentBlock = fn.newBasicBlock("unreachable") - - case *ast.BranchStmt: - var block *BasicBlock - switch s.Tok { - case token.BREAK: - if s.Label != nil { - block = fn.labelledBlock(s.Label)._break - } else { - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._break - } - } - - case token.CONTINUE: - if s.Label != nil { - block = fn.labelledBlock(s.Label)._continue - } else { - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._continue - } - } - - case token.FALLTHROUGH: - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._fallthrough - } - - case token.GOTO: - block = fn.labelledBlock(s.Label)._goto - } - emitJump(fn, block) - fn.currentBlock = fn.newBasicBlock("unreachable") - - case *ast.BlockStmt: - b.stmtList(fn, s.List) - - case *ast.IfStmt: - if s.Init != nil { - b.stmt(fn, s.Init) - } - then := fn.newBasicBlock("if.then") - done := fn.newBasicBlock("if.done") - els := done - if s.Else != nil { - els = fn.newBasicBlock("if.else") - } - b.cond(fn, s.Cond, then, els) - fn.currentBlock = then - b.stmt(fn, s.Body) - emitJump(fn, done) - - if s.Else != nil { - fn.currentBlock = els - b.stmt(fn, s.Else) - emitJump(fn, done) - } - - fn.currentBlock = done - - case *ast.SwitchStmt: - b.switchStmt(fn, s, label) - - case *ast.TypeSwitchStmt: - b.typeSwitchStmt(fn, s, label) - - case *ast.SelectStmt: - b.selectStmt(fn, s, label) - - case *ast.ForStmt: - b.forStmt(fn, s, label) - - case *ast.RangeStmt: - b.rangeStmt(fn, s, label) - - default: - panic(fmt.Sprintf("unexpected statement kind: %T", s)) - } -} - -// buildFunction builds SSA code for the body of function fn. Idempotent. -func (b *builder) buildFunction(fn *Function) { - if fn.Blocks != nil { - return // building already started - } - - var recvField *ast.FieldList - var body *ast.BlockStmt - var functype *ast.FuncType - switch n := fn.syntax.(type) { - case nil: - return // not a Go source function. (Synthetic, or from object file.) - case *ast.FuncDecl: - functype = n.Type - recvField = n.Recv - body = n.Body - case *ast.FuncLit: - functype = n.Type - body = n.Body - default: - panic(n) - } - - if body == nil { - // External function. - if fn.Params == nil { - // This condition ensures we add a non-empty - // params list once only, but we may attempt - // the degenerate empty case repeatedly. - // TODO(adonovan): opt: don't do that. - - // We set Function.Params even though there is no body - // code to reference them. This simplifies clients. - if recv := fn.Signature.Recv(); recv != nil { - fn.addParamObj(recv) - } - params := fn.Signature.Params() - for i, n := 0, params.Len(); i < n; i++ { - fn.addParamObj(params.At(i)) - } - } - return - } - if fn.Prog.mode&LogSource != 0 { - defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))() - } - fn.startBody() - fn.createSyntacticParams(recvField, functype) - b.stmt(fn, body) - if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) { - // Control fell off the end of the function's body block. - // - // Block optimizations eliminate the current block, if - // unreachable. It is a builder invariant that - // if this no-arg return is ill-typed for - // fn.Signature.Results, this block must be - // unreachable. The sanity checker checks this. - fn.emit(new(RunDefers)) - fn.emit(new(Return)) - } - fn.finishBody() -} - -// buildFuncDecl builds SSA code for the function or method declared -// by decl in package pkg. -// -func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) { - id := decl.Name - if isBlankIdent(id) { - return // discard - } - fn := pkg.values[pkg.info.Defs[id]].(*Function) - if decl.Recv == nil && id.Name == "init" { - var v Call - v.Call.Value = fn - v.setType(types.NewTuple()) - pkg.init.emit(&v) - } - b.buildFunction(fn) -} - -// BuildAll calls Package.Build() for each package in prog. -// Building occurs in parallel unless the BuildSerially mode flag was set. -// -// BuildAll is intended for whole-program analysis; a typical compiler -// need only build a single package. -// -// BuildAll is idempotent and thread-safe. -// -func (prog *Program) Build() { - var wg sync.WaitGroup - for _, p := range prog.packages { - if prog.mode&BuildSerially != 0 { - p.Build() - } else { - wg.Add(1) - go func(p *Package) { - p.Build() - wg.Done() - }(p) - } - } - wg.Wait() -} - -// Build builds SSA code for all functions and vars in package p. -// -// Precondition: CreatePackage must have been called for all of p's -// direct imports (and hence its direct imports must have been -// error-free). -// -// Build is idempotent and thread-safe. -// -func (p *Package) Build() { p.buildOnce.Do(p.build) } - -func (p *Package) build() { - if p.info == nil { - return // synthetic package, e.g. "testmain" - } - if p.files == nil { - p.info = nil - return // package loaded from export data - } - - // Ensure we have runtime type info for all exported members. - // TODO(adonovan): ideally belongs in memberFromObject, but - // that would require package creation in topological order. - for name, mem := range p.Members { - if ast.IsExported(name) { - p.Prog.needMethodsOf(mem.Type()) - } - } - if p.Prog.mode&LogSource != 0 { - defer logStack("build %s", p)() - } - init := p.init - init.startBody() - - var done *BasicBlock - - if p.Prog.mode&BareInits == 0 { - // Make init() skip if package is already initialized. - initguard := p.Var("init$guard") - doinit := init.newBasicBlock("init.start") - done = init.newBasicBlock("init.done") - emitIf(init, emitLoad(init, initguard), done, doinit) - init.currentBlock = doinit - emitStore(init, initguard, vTrue, token.NoPos) - - // Call the init() function of each package we import. - for _, pkg := range p.Pkg.Imports() { - prereq := p.Prog.packages[pkg] - if prereq == nil { - panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path())) - } - var v Call - v.Call.Value = prereq.init - v.Call.pos = init.pos - v.setType(types.NewTuple()) - init.emit(&v) - } - } - - var b builder - - // Initialize package-level vars in correct order. - for _, varinit := range p.info.InitOrder { - if init.Prog.mode&LogSource != 0 { - fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n", - varinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos())) - } - if len(varinit.Lhs) == 1 { - // 1:1 initialization: var x, y = a(), b() - var lval lvalue - if v := varinit.Lhs[0]; v.Name() != "_" { - lval = &address{addr: p.values[v].(*Global), pos: v.Pos()} - } else { - lval = blank{} - } - b.assign(init, lval, varinit.Rhs, true, nil) - } else { - // n:1 initialization: var x, y := f() - tuple := b.exprN(init, varinit.Rhs) - for i, v := range varinit.Lhs { - if v.Name() == "_" { - continue - } - emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i), v.Pos()) - } - } - } - - // Build all package-level functions, init functions - // and methods, including unreachable/blank ones. - // We build them in source order, but it's not significant. - for _, file := range p.files { - for _, decl := range file.Decls { - if decl, ok := decl.(*ast.FuncDecl); ok { - b.buildFuncDecl(p, decl) - } - } - } - - // Finish up init(). - if p.Prog.mode&BareInits == 0 { - emitJump(init, done) - init.currentBlock = done - } - init.emit(new(Return)) - init.finishBody() - - p.info = nil // We no longer need ASTs or go/types deductions. - - if p.Prog.mode&SanityCheckFunctions != 0 { - sanityCheckPackage(p) - } -} - -// Like ObjectOf, but panics instead of returning nil. -// Only valid during p's create and build phases. -func (p *Package) objectOf(id *ast.Ident) types.Object { - if o := p.info.ObjectOf(id); o != nil { - return o - } - panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", - id.Name, p.Prog.Fset.Position(id.Pos()))) -} - -// Like TypeOf, but panics instead of returning nil. -// Only valid during p's create and build phases. -func (p *Package) typeOf(e ast.Expr) types.Type { - if T := p.info.TypeOf(e); T != nil { - return T - } - panic(fmt.Sprintf("no type for %T @ %s", - e, p.Prog.Fset.Position(e.Pos()))) -} diff --git a/go/ssa/builder14_test.go b/go/ssa/builder14_test.go deleted file mode 100644 index 3eaa825e2b..0000000000 --- a/go/ssa/builder14_test.go +++ /dev/null @@ -1,421 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa_test - -import ( - "bytes" - "go/ast" - "go/parser" - "go/token" - "reflect" - "sort" - "strings" - "testing" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" - - _ "golang.org/x/tools/go/gcimporter" -) - -func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } - -// Tests that programs partially loaded from gc object files contain -// functions with no code for the external portions, but are otherwise ok. -func TestBuildPackage(t *testing.T) { - input := ` -package main - -import ( - "bytes" - "io" - "testing" -) - -func main() { - var t testing.T - t.Parallel() // static call to external declared method - t.Fail() // static call to promoted external declared method - testing.Short() // static call to external package-level function - - var w io.Writer = new(bytes.Buffer) - w.Write(nil) // interface invoke of external declared method -} -` - - // Parse the file. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "input.go", input, 0) - if err != nil { - t.Error(err) - return - } - - // Build an SSA program from the parsed file. - // Load its dependencies from gc binary export data. - mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset, - types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions) - if err != nil { - t.Error(err) - return - } - - // The main package, its direct and indirect dependencies are loaded. - deps := []string{ - // directly imported dependencies: - "bytes", "io", "testing", - // indirect dependencies (partial list): - "errors", "fmt", "os", "runtime", - } - - prog := mainPkg.Prog - all := prog.AllPackages() - if len(all) <= len(deps) { - t.Errorf("unexpected set of loaded packages: %q", all) - } - for _, path := range deps { - pkg := prog.ImportedPackage(path) - if pkg == nil { - t.Errorf("package not loaded: %q", path) - continue - } - - // External packages should have no function bodies (except for wrappers). - isExt := pkg != mainPkg - - // init() - if isExt && !isEmpty(pkg.Func("init")) { - t.Errorf("external package %s has non-empty init", pkg) - } else if !isExt && isEmpty(pkg.Func("init")) { - t.Errorf("main package %s has empty init", pkg) - } - - for _, mem := range pkg.Members { - switch mem := mem.(type) { - case *ssa.Function: - // Functions at package level. - if isExt && !isEmpty(mem) { - t.Errorf("external function %s is non-empty", mem) - } else if !isExt && isEmpty(mem) { - t.Errorf("function %s is empty", mem) - } - - case *ssa.Type: - // Methods of named types T. - // (In this test, all exported methods belong to *T not T.) - if !isExt { - t.Fatalf("unexpected name type in main package: %s", mem) - } - mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) - for i, n := 0, mset.Len(); i < n; i++ { - m := prog.MethodValue(mset.At(i)) - // For external types, only synthetic wrappers have code. - expExt := !strings.Contains(m.Synthetic, "wrapper") - if expExt && !isEmpty(m) { - t.Errorf("external method %s is non-empty: %s", - m, m.Synthetic) - } else if !expExt && isEmpty(m) { - t.Errorf("method function %s is empty: %s", - m, m.Synthetic) - } - } - } - } - } - - expectedCallee := []string{ - "(*testing.T).Parallel", - "(*testing.common).Fail", - "testing.Short", - "N/A", - } - callNum := 0 - for _, b := range mainPkg.Func("main").Blocks { - for _, instr := range b.Instrs { - switch instr := instr.(type) { - case ssa.CallInstruction: - call := instr.Common() - if want := expectedCallee[callNum]; want != "N/A" { - got := call.StaticCallee().String() - if want != got { - t.Errorf("call #%d from main.main: got callee %s, want %s", - callNum, got, want) - } - } - callNum++ - } - } - } - if callNum != 4 { - t.Errorf("in main.main: got %d calls, want %d", callNum, 4) - } -} - -// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. -func TestRuntimeTypes(t *testing.T) { - tests := []struct { - input string - want []string - }{ - // An exported package-level type is needed. - {`package A; type T struct{}; func (T) f() {}`, - []string{"*p.T", "p.T"}, - }, - // An unexported package-level type is not needed. - {`package B; type t struct{}; func (t) f() {}`, - nil, - }, - // Subcomponents of type of exported package-level var are needed. - {`package C; import "bytes"; var V struct {*bytes.Buffer}`, - []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, - }, - // Subcomponents of type of unexported package-level var are not needed. - {`package D; import "bytes"; var v struct {*bytes.Buffer}`, - nil, - }, - // Subcomponents of type of exported package-level function are needed. - {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`, - []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, - }, - // Subcomponents of type of unexported package-level function are not needed. - {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`, - nil, - }, - // Subcomponents of type of exported method of uninstantiated unexported type are not needed. - {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, - nil, - }, - // ...unless used by MakeInterface. - {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, - []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, - }, - // Subcomponents of type of unexported method are not needed. - {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, - []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, - }, - // Local types aren't needed. - {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, - nil, - }, - // ...unless used by MakeInterface. - {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, - []string{"*bytes.Buffer", "*p.T", "p.T"}, - }, - // Types used as operand of MakeInterface are needed. - {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, - []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, - }, - // MakeInterface is optimized away when storing to a blank. - {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, - nil, - }, - } - for _, test := range tests { - // Parse the file. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "input.go", test.input, 0) - if err != nil { - t.Errorf("test %q: %s", test.input[:15], err) - continue - } - - // Create a single-file main package. - // Load dependencies from gc binary export data. - ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, - types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions) - if err != nil { - t.Errorf("test %q: %s", test.input[:15], err) - continue - } - - var typstrs []string - for _, T := range ssapkg.Prog.RuntimeTypes() { - typstrs = append(typstrs, T.String()) - } - sort.Strings(typstrs) - - if !reflect.DeepEqual(typstrs, test.want) { - t.Errorf("test 'package %s': got %q, want %q", - f.Name.Name, typstrs, test.want) - } - } -} - -// TestInit tests that synthesized init functions are correctly formed. -// Bare init functions omit calls to dependent init functions and the use of -// an init guard. They are useful in cases where the client uses a different -// calling convention for init functions, or cases where it is easier for a -// client to analyze bare init functions. Both of these aspects are used by -// the llgo compiler for simpler integration with gccgo's runtime library, -// and to simplify the analysis whereby it deduces which stores to globals -// can be lowered to global initializers. -func TestInit(t *testing.T) { - tests := []struct { - mode ssa.BuilderMode - input, want string - }{ - {0, `package A; import _ "errors"; var i int = 42`, - `# Name: A.init -# Package: A -# Synthetic: package initializer -func init(): -0: entry P:0 S:2 - t0 = *init$guard bool - if t0 goto 2 else 1 -1: init.start P:1 S:1 - *init$guard = true:bool - t1 = errors.init() () - *i = 42:int - jump 2 -2: init.done P:2 S:0 - return - -`}, - {ssa.BareInits, `package B; import _ "errors"; var i int = 42`, - `# Name: B.init -# Package: B -# Synthetic: package initializer -func init(): -0: entry P:0 S:0 - *i = 42:int - return - -`}, - } - for _, test := range tests { - // Create a single-file main package. - var conf loader.Config - f, err := conf.ParseFile("", test.input) - if err != nil { - t.Errorf("test %q: %s", test.input[:15], err) - continue - } - conf.CreateFromFiles(f.Name.Name, f) - - lprog, err := conf.Load() - if err != nil { - t.Errorf("test 'package %s': Load: %s", f.Name.Name, err) - continue - } - prog := ssautil.CreateProgram(lprog, test.mode) - mainPkg := prog.Package(lprog.Created[0].Pkg) - prog.Build() - initFunc := mainPkg.Func("init") - if initFunc == nil { - t.Errorf("test 'package %s': no init function", f.Name.Name) - continue - } - - var initbuf bytes.Buffer - _, err = initFunc.WriteTo(&initbuf) - if err != nil { - t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err) - continue - } - - if initbuf.String() != test.want { - t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want) - } - } -} - -// TestSyntheticFuncs checks that the expected synthetic functions are -// created, reachable, and not duplicated. -func TestSyntheticFuncs(t *testing.T) { - const input = `package P -type T int -func (T) f() int -func (*T) g() int -var ( - // thunks - a = T.f - b = T.f - c = (struct{T}).f - d = (struct{T}).f - e = (*T).g - f = (*T).g - g = (struct{*T}).g - h = (struct{*T}).g - - // bounds - i = T(0).f - j = T(0).f - k = new(T).g - l = new(T).g - - // wrappers - m interface{} = struct{T}{} - n interface{} = struct{T}{} - o interface{} = struct{*T}{} - p interface{} = struct{*T}{} - q interface{} = new(struct{T}) - r interface{} = new(struct{T}) - s interface{} = new(struct{*T}) - t interface{} = new(struct{*T}) -) -` - // Parse - var conf loader.Config - f, err := conf.ParseFile("", input) - if err != nil { - t.Fatalf("parse: %v", err) - } - conf.CreateFromFiles(f.Name.Name, f) - - // Load - lprog, err := conf.Load() - if err != nil { - t.Fatalf("Load: %v", err) - } - - // Create and build SSA - prog := ssautil.CreateProgram(lprog, 0) - prog.Build() - - // Enumerate reachable synthetic functions - want := map[string]string{ - "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int", - "(P.T).f$bound": "bound method wrapper for func (P.T).f() int", - - "(*P.T).g$thunk": "thunk for func (*P.T).g() int", - "(P.T).f$thunk": "thunk for func (P.T).f() int", - "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int", - "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int", - - "(*P.T).f": "wrapper for func (P.T).f() int", - "(*struct{*P.T}).f": "wrapper for func (P.T).f() int", - "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int", - "(*struct{P.T}).f": "wrapper for func (P.T).f() int", - "(*struct{P.T}).g": "wrapper for func (*P.T).g() int", - "(struct{*P.T}).f": "wrapper for func (P.T).f() int", - "(struct{*P.T}).g": "wrapper for func (*P.T).g() int", - "(struct{P.T}).f": "wrapper for func (P.T).f() int", - - "P.init": "package initializer", - } - for fn := range ssautil.AllFunctions(prog) { - if fn.Synthetic == "" { - continue - } - name := fn.String() - wantDescr, ok := want[name] - if !ok { - t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic) - continue - } - delete(want, name) - - if wantDescr != fn.Synthetic { - t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr) - } - } - for fn, descr := range want { - t.Errorf("want func: %q: %q", fn, descr) - } -} diff --git a/go/ssa/const14.go b/go/ssa/const14.go deleted file mode 100644 index 0ec43b6f86..0000000000 --- a/go/ssa/const14.go +++ /dev/null @@ -1,170 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines the Const SSA value type. - -import ( - "fmt" - "go/token" - "strconv" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// NewConst returns a new constant of the specified value and type. -// val must be valid according to the specification of Const.Value. -// -func NewConst(val exact.Value, typ types.Type) *Const { - return &Const{typ, val} -} - -// intConst returns an 'int' constant that evaluates to i. -// (i is an int64 in case the host is narrower than the target.) -func intConst(i int64) *Const { - return NewConst(exact.MakeInt64(i), tInt) -} - -// nilConst returns a nil constant of the specified type, which may -// be any reference type, including interfaces. -// -func nilConst(typ types.Type) *Const { - return NewConst(nil, typ) -} - -// stringConst returns a 'string' constant that evaluates to s. -func stringConst(s string) *Const { - return NewConst(exact.MakeString(s), tString) -} - -// zeroConst returns a new "zero" constant of the specified type, -// which must not be an array or struct type: the zero values of -// aggregates are well-defined but cannot be represented by Const. -// -func zeroConst(t types.Type) *Const { - switch t := t.(type) { - case *types.Basic: - switch { - case t.Info()&types.IsBoolean != 0: - return NewConst(exact.MakeBool(false), t) - case t.Info()&types.IsNumeric != 0: - return NewConst(exact.MakeInt64(0), t) - case t.Info()&types.IsString != 0: - return NewConst(exact.MakeString(""), t) - case t.Kind() == types.UnsafePointer: - fallthrough - case t.Kind() == types.UntypedNil: - return nilConst(t) - default: - panic(fmt.Sprint("zeroConst for unexpected type:", t)) - } - case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: - return nilConst(t) - case *types.Named: - return NewConst(zeroConst(t.Underlying()).Value, t) - case *types.Array, *types.Struct, *types.Tuple: - panic(fmt.Sprint("zeroConst applied to aggregate:", t)) - } - panic(fmt.Sprint("zeroConst: unexpected ", t)) -} - -func (c *Const) RelString(from *types.Package) string { - var s string - if c.Value == nil { - s = "nil" - } else if c.Value.Kind() == exact.String { - s = exact.StringVal(c.Value) - const max = 20 - // TODO(adonovan): don't cut a rune in half. - if len(s) > max { - s = s[:max-3] + "..." // abbreviate - } - s = strconv.Quote(s) - } else { - s = c.Value.String() - } - return s + ":" + relType(c.Type(), from) -} - -func (c *Const) Name() string { - return c.RelString(nil) -} - -func (c *Const) String() string { - return c.Name() -} - -func (c *Const) Type() types.Type { - return c.typ -} - -func (c *Const) Referrers() *[]Instruction { - return nil -} - -func (c *Const) Parent() *Function { return nil } - -func (c *Const) Pos() token.Pos { - return token.NoPos -} - -// IsNil returns true if this constant represents a typed or untyped nil value. -func (c *Const) IsNil() bool { - return c.Value == nil -} - -// Int64 returns the numeric value of this constant truncated to fit -// a signed 64-bit integer. -// -func (c *Const) Int64() int64 { - switch x := c.Value; x.Kind() { - case exact.Int: - if i, ok := exact.Int64Val(x); ok { - return i - } - return 0 - case exact.Float: - f, _ := exact.Float64Val(x) - return int64(f) - } - panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) -} - -// Uint64 returns the numeric value of this constant truncated to fit -// an unsigned 64-bit integer. -// -func (c *Const) Uint64() uint64 { - switch x := c.Value; x.Kind() { - case exact.Int: - if u, ok := exact.Uint64Val(x); ok { - return u - } - return 0 - case exact.Float: - f, _ := exact.Float64Val(x) - return uint64(f) - } - panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) -} - -// Float64 returns the numeric value of this constant truncated to fit -// a float64. -// -func (c *Const) Float64() float64 { - f, _ := exact.Float64Val(c.Value) - return f -} - -// Complex128 returns the complex value of this constant truncated to -// fit a complex128. -// -func (c *Const) Complex128() complex128 { - re, _ := exact.Float64Val(exact.Real(c.Value)) - im, _ := exact.Float64Val(exact.Imag(c.Value)) - return complex(re, im) -} diff --git a/go/ssa/create14.go b/go/ssa/create14.go deleted file mode 100644 index 58be47e9ad..0000000000 --- a/go/ssa/create14.go +++ /dev/null @@ -1,259 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file implements the CREATE phase of SSA construction. -// See builder.go for explanation. - -import ( - "fmt" - "go/ast" - "go/token" - "os" - "sync" - - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -// NewProgram returns a new SSA Program. -// -// mode controls diagnostics and checking during SSA construction. -// -func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { - prog := &Program{ - Fset: fset, - imported: make(map[string]*Package), - packages: make(map[*types.Package]*Package), - thunks: make(map[selectionKey]*Function), - bounds: make(map[*types.Func]*Function), - mode: mode, - } - - h := typeutil.MakeHasher() // protected by methodsMu, in effect - prog.methodSets.SetHasher(h) - prog.canon.SetHasher(h) - - return prog -} - -// memberFromObject populates package pkg with a member for the -// typechecker object obj. -// -// For objects from Go source code, syntax is the associated syntax -// tree (for funcs and vars only); it will be used during the build -// phase. -// -func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { - name := obj.Name() - switch obj := obj.(type) { - case *types.TypeName: - pkg.Members[name] = &Type{ - object: obj, - pkg: pkg, - } - - case *types.Const: - c := &NamedConst{ - object: obj, - Value: NewConst(obj.Val(), obj.Type()), - pkg: pkg, - } - pkg.values[obj] = c.Value - pkg.Members[name] = c - - case *types.Var: - g := &Global{ - Pkg: pkg, - name: name, - object: obj, - typ: types.NewPointer(obj.Type()), // address - pos: obj.Pos(), - } - pkg.values[obj] = g - pkg.Members[name] = g - - case *types.Func: - sig := obj.Type().(*types.Signature) - if sig.Recv() == nil && name == "init" { - pkg.ninit++ - name = fmt.Sprintf("init#%d", pkg.ninit) - } - fn := &Function{ - name: name, - object: obj, - Signature: sig, - syntax: syntax, - pos: obj.Pos(), - Pkg: pkg, - Prog: pkg.Prog, - } - if syntax == nil { - fn.Synthetic = "loaded from gc object file" - } - - pkg.values[obj] = fn - if sig.Recv() == nil { - pkg.Members[name] = fn // package-level function - } - - default: // (incl. *types.Package) - panic("unexpected Object type: " + obj.String()) - } -} - -// membersFromDecl populates package pkg with members for each -// typechecker object (var, func, const or type) associated with the -// specified decl. -// -func membersFromDecl(pkg *Package, decl ast.Decl) { - switch decl := decl.(type) { - case *ast.GenDecl: // import, const, type or var - switch decl.Tok { - case token.CONST: - for _, spec := range decl.Specs { - for _, id := range spec.(*ast.ValueSpec).Names { - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], nil) - } - } - } - - case token.VAR: - for _, spec := range decl.Specs { - for _, id := range spec.(*ast.ValueSpec).Names { - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], spec) - } - } - } - - case token.TYPE: - for _, spec := range decl.Specs { - id := spec.(*ast.TypeSpec).Name - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], nil) - } - } - } - - case *ast.FuncDecl: - id := decl.Name - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], decl) - } - } -} - -// CreatePackage constructs and returns an SSA Package from the -// specified type-checked, error-free file ASTs, and populates its -// Members mapping. -// -// importable determines whether this package should be returned by a -// subsequent call to ImportedPackage(pkg.Path()). -// -// The real work of building SSA form for each function is not done -// until a subsequent call to Package.Build(). -// -func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { - p := &Package{ - Prog: prog, - Members: make(map[string]Member), - values: make(map[types.Object]Value), - Pkg: pkg, - info: info, // transient (CREATE and BUILD phases) - files: files, // transient (CREATE and BUILD phases) - } - - // Add init() function. - p.init = &Function{ - name: "init", - Signature: new(types.Signature), - Synthetic: "package initializer", - Pkg: p, - Prog: prog, - } - p.Members[p.init.name] = p.init - - // CREATE phase. - // Allocate all package members: vars, funcs, consts and types. - if len(files) > 0 { - // Go source package. - for _, file := range files { - for _, decl := range file.Decls { - membersFromDecl(p, decl) - } - } - } else { - // GC-compiled binary package. - // No code. - // No position information. - scope := p.Pkg.Scope() - for _, name := range scope.Names() { - obj := scope.Lookup(name) - memberFromObject(p, obj, nil) - if obj, ok := obj.(*types.TypeName); ok { - named := obj.Type().(*types.Named) - for i, n := 0, named.NumMethods(); i < n; i++ { - memberFromObject(p, named.Method(i), nil) - } - } - } - } - - if prog.mode&BareInits == 0 { - // Add initializer guard variable. - initguard := &Global{ - Pkg: p, - name: "init$guard", - typ: types.NewPointer(tBool), - } - p.Members[initguard.Name()] = initguard - } - - if prog.mode&GlobalDebug != 0 { - p.SetDebugMode(true) - } - - if prog.mode&PrintPackages != 0 { - printMu.Lock() - p.WriteTo(os.Stdout) - printMu.Unlock() - } - - if importable { - prog.imported[p.Pkg.Path()] = p - } - prog.packages[p.Pkg] = p - - return p -} - -// printMu serializes printing of Packages/Functions to stdout. -var printMu sync.Mutex - -// AllPackages returns a new slice containing all packages in the -// program prog in unspecified order. -// -func (prog *Program) AllPackages() []*Package { - pkgs := make([]*Package, 0, len(prog.packages)) - for _, pkg := range prog.packages { - pkgs = append(pkgs, pkg) - } - return pkgs -} - -// ImportedPackage returns the importable SSA Package whose import -// path is path, or nil if no such SSA package has been created. -// -// Not all packages are importable. For example, no import -// declaration can resolve to the x_test package created by 'go test' -// or the ad-hoc main package created 'go build foo.go'. -// -func (prog *Program) ImportedPackage(path string) *Package { - return prog.imported[path] -} diff --git a/go/ssa/emit14.go b/go/ssa/emit14.go deleted file mode 100644 index 454aea0505..0000000000 --- a/go/ssa/emit14.go +++ /dev/null @@ -1,471 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// Helpers for emitting SSA instructions. - -import ( - "fmt" - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -// emitNew emits to f a new (heap Alloc) instruction allocating an -// object of type typ. pos is the optional source location. -// -func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc { - v := &Alloc{Heap: true} - v.setType(types.NewPointer(typ)) - v.setPos(pos) - f.emit(v) - return v -} - -// emitLoad emits to f an instruction to load the address addr into a -// new temporary, and returns the value so defined. -// -func emitLoad(f *Function, addr Value) *UnOp { - v := &UnOp{Op: token.MUL, X: addr} - v.setType(deref(addr.Type())) - f.emit(v) - return v -} - -// emitDebugRef emits to f a DebugRef pseudo-instruction associating -// expression e with value v. -// -func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) { - if !f.debugInfo() { - return // debugging not enabled - } - if v == nil || e == nil { - panic("nil") - } - var obj types.Object - e = unparen(e) - if id, ok := e.(*ast.Ident); ok { - if isBlankIdent(id) { - return - } - obj = f.Pkg.objectOf(id) - switch obj.(type) { - case *types.Nil, *types.Const, *types.Builtin: - return - } - } - f.emit(&DebugRef{ - X: v, - Expr: e, - IsAddr: isAddr, - object: obj, - }) -} - -// emitArith emits to f code to compute the binary operation op(x, y) -// where op is an eager shift, logical or arithmetic operation. -// (Use emitCompare() for comparisons and Builder.logicalBinop() for -// non-eager operations.) -// -func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value { - switch op { - case token.SHL, token.SHR: - x = emitConv(f, x, t) - // y may be signed or an 'untyped' constant. - // TODO(adonovan): whence signed values? - if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 { - y = emitConv(f, y, types.Typ[types.Uint64]) - } - - case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: - x = emitConv(f, x, t) - y = emitConv(f, y, t) - - default: - panic("illegal op in emitArith: " + op.String()) - - } - v := &BinOp{ - Op: op, - X: x, - Y: y, - } - v.setPos(pos) - v.setType(t) - return f.emit(v) -} - -// emitCompare emits to f code compute the boolean result of -// comparison comparison 'x op y'. -// -func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { - xt := x.Type().Underlying() - yt := y.Type().Underlying() - - // Special case to optimise a tagless SwitchStmt so that - // these are equivalent - // switch { case e: ...} - // switch true { case e: ... } - // if e==true { ... } - // 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 { - return y - } - } - - if types.Identical(xt, yt) { - // no conversion necessary - } else if _, ok := xt.(*types.Interface); ok { - y = emitConv(f, y, x.Type()) - } else if _, ok := yt.(*types.Interface); ok { - x = emitConv(f, x, y.Type()) - } else if _, ok := x.(*Const); ok { - x = emitConv(f, x, y.Type()) - } else if _, ok := y.(*Const); ok { - y = emitConv(f, y, x.Type()) - } else { - // other cases, e.g. channels. No-op. - } - - v := &BinOp{ - Op: op, - X: x, - Y: y, - } - v.setPos(pos) - v.setType(tBool) - return f.emit(v) -} - -// 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. -// -func isValuePreserving(ut_src, ut_dst types.Type) bool { - // Identical underlying types? - if types.Identical(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 - } - 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. Conversions cannot fail dynamically. -// -func emitConv(f *Function, val Value, typ types.Type) Value { - t_src := val.Type() - - // Identical types? Conversion is a no-op. - if types.Identical(t_src, typ) { - return val - } - - ut_dst := typ.Underlying() - ut_src := t_src.Underlying() - - // 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) - } - - // Conversion to, or construction of a value of, an interface type? - if _, ok := ut_dst.(*types.Interface); ok { - // Assignment from one interface type to another? - if _, ok := ut_src.(*types.Interface); ok { - c := &ChangeInterface{X: val} - c.setType(typ) - return f.emit(c) - } - - // Untyped nil constant? Return interface-typed nil constant. - if ut_src == tUntypedNil { - return nilConst(typ) - } - - // Convert (non-nil) "untyped" literals to their default type. - if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 { - val = emitConv(f, val, DefaultType(ut_src)) - } - - f.Pkg.Prog.needMethodsOf(val.Type()) - mi := &MakeInterface{X: val} - mi.setType(typ) - return f.emit(mi) - } - - // Conversion of a compile-time constant value? - if c, ok := val.(*Const); ok { - if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() { - // Conversion of a compile-time constant to - // another constant type results in a new - // constant of the destination type and - // (initially) the same abstract value. - // We don't truncate the value yet. - return NewConst(c.Value, typ) - } - - // We're converting from constant to non-constant type, - // e.g. string -> []byte/[]rune. - } - - // A representation-changing conversion? - // At least one of {ut_src,ut_dst} must be *Basic. - // (The other may be []byte or []rune.) - _, ok1 := ut_src.(*types.Basic) - _, ok2 := ut_dst.(*types.Basic) - if ok1 || ok2 { - c := &Convert{X: val} - c.setType(typ) - return f.emit(c) - } - - panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ)) -} - -// emitStore emits to f an instruction to store value val at location -// addr, applying implicit conversions as required by assignability rules. -// -func emitStore(f *Function, addr, val Value, pos token.Pos) *Store { - s := &Store{ - Addr: addr, - Val: emitConv(f, val, deref(addr.Type())), - pos: pos, - } - f.emit(s) - return s -} - -// emitJump emits to f a jump to target, and updates the control-flow graph. -// Postcondition: f.currentBlock is nil. -// -func emitJump(f *Function, target *BasicBlock) { - b := f.currentBlock - b.emit(new(Jump)) - addEdge(b, target) - f.currentBlock = nil -} - -// emitIf emits to f a conditional jump to tblock or fblock based on -// cond, and updates the control-flow graph. -// Postcondition: f.currentBlock is nil. -// -func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) { - b := f.currentBlock - b.emit(&If{Cond: cond}) - addEdge(b, tblock) - addEdge(b, fblock) - f.currentBlock = nil -} - -// emitExtract emits to f an instruction to extract the index'th -// component of tuple. It returns the extracted value. -// -func emitExtract(f *Function, tuple Value, index int) Value { - e := &Extract{Tuple: tuple, Index: index} - e.setType(tuple.Type().(*types.Tuple).At(index).Type()) - return f.emit(e) -} - -// emitTypeAssert emits to f a type assertion value := x.(t) and -// returns the value. x.Type() must be an interface. -// -func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value { - a := &TypeAssert{X: x, AssertedType: t} - a.setPos(pos) - a.setType(t) - return f.emit(a) -} - -// emitTypeTest emits to f a type test value,ok := x.(t) and returns -// a (value, ok) tuple. x.Type() must be an interface. -// -func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value { - a := &TypeAssert{ - X: x, - AssertedType: t, - CommaOk: true, - } - a.setPos(pos) - a.setType(types.NewTuple( - newVar("value", t), - varOk, - )) - return f.emit(a) -} - -// emitTailCall emits to f a function call in tail position. The -// caller is responsible for all fields of 'call' except its type. -// Intended for wrapper methods. -// Precondition: f does/will not use deferred procedure calls. -// Postcondition: f.currentBlock is nil. -// -func emitTailCall(f *Function, call *Call) { - tresults := f.Signature.Results() - nr := tresults.Len() - if nr == 1 { - call.typ = tresults.At(0).Type() - } else { - call.typ = tresults - } - tuple := f.emit(call) - var ret Return - switch nr { - case 0: - // no-op - case 1: - ret.Results = []Value{tuple} - default: - for i := 0; i < nr; i++ { - v := emitExtract(f, tuple, i) - // 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 - // the types exactly match. - ret.Results = append(ret.Results, v) - } - } - f.emit(&ret) - f.currentBlock = nil -} - -// emitImplicitSelections emits to f code to apply the sequence of -// implicit field selections specified by indices to base value v, and -// returns the selected value. -// -// If v is the address of a struct, the result will be the address of -// a field; if it is the value of a struct, the result will be the -// value of a field. -// -func emitImplicitSelections(f *Function, v Value, indices []int) Value { - for _, index := range indices { - fld := deref(v.Type()).Underlying().(*types.Struct).Field(index) - - if isPointer(v.Type()) { - instr := &FieldAddr{ - X: v, - Field: index, - } - instr.setType(types.NewPointer(fld.Type())) - v = f.emit(instr) - // Load the field's value iff indirectly embedded. - if isPointer(fld.Type()) { - v = emitLoad(f, v) - } - } else { - instr := &Field{ - X: v, - Field: index, - } - instr.setType(fld.Type()) - v = f.emit(instr) - } - } - return v -} - -// emitFieldSelection emits to f code to select the index'th field of v. -// -// If wantAddr, the input must be a pointer-to-struct and the result -// will be the field's address; otherwise the result will be the -// field's value. -// Ident id is used for position and debug info. -// -func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value { - fld := deref(v.Type()).Underlying().(*types.Struct).Field(index) - if isPointer(v.Type()) { - instr := &FieldAddr{ - X: v, - Field: index, - } - instr.setPos(id.Pos()) - instr.setType(types.NewPointer(fld.Type())) - v = f.emit(instr) - // Load the field's value iff we don't want its address. - if !wantAddr { - v = emitLoad(f, v) - } - } else { - instr := &Field{ - X: v, - Field: index, - } - instr.setPos(id.Pos()) - instr.setType(fld.Type()) - v = f.emit(instr) - } - emitDebugRef(f, id, v, wantAddr) - return v -} - -// zeroValue emits to f code to produce a zero value of type t, -// and returns it. -// -func zeroValue(f *Function, t types.Type) Value { - switch t.Underlying().(type) { - case *types.Struct, *types.Array: - return emitLoad(f, f.addLocal(t, token.NoPos)) - default: - return zeroConst(t) - } -} - -// createRecoverBlock emits to f a block of code to return after a -// recovered panic, and sets f.Recover to it. -// -// If f's result parameters are named, the code loads and returns -// their current values, otherwise it returns the zero values of their -// type. -// -// Idempotent. -// -func createRecoverBlock(f *Function) { - if f.Recover != nil { - return // already created - } - saved := f.currentBlock - - f.Recover = f.newBasicBlock("recover") - f.currentBlock = f.Recover - - var results []Value - if f.namedResults != nil { - // Reload NRPs to form value tuple. - for _, r := range f.namedResults { - results = append(results, emitLoad(f, r)) - } - } else { - R := f.Signature.Results() - for i, n := 0, R.Len(); i < n; i++ { - T := R.At(i).Type() - - // Return zero value of each result type. - results = append(results, zeroValue(f, T)) - } - } - f.emit(&Return{Results: results}) - - f.currentBlock = saved -} diff --git a/go/ssa/example14_test.go b/go/ssa/example14_test.go deleted file mode 100644 index 0171d4fb18..0000000000 --- a/go/ssa/example14_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa_test - -import ( - "fmt" - "os" - - "go/ast" - "go/parser" - "go/token" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -const hello = ` -package main - -import "fmt" - -const message = "Hello, World!" - -func main() { - fmt.Println(message) -} -` - -// This program demonstrates how to run the SSA builder on a single -// package of one or more already-parsed files. Its dependencies are -// loaded from compiler export data. This is what you'd typically use -// for a compiler; it does not depend on golang.org/x/tools/go/loader. -// -// It shows the printed representation of packages, functions, and -// instructions. Within the function listing, the name of each -// BasicBlock such as ".0.entry" is printed left-aligned, followed by -// the block's 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. -// -// Build and run the ssadump.go program if you want a standalone tool -// with similar functionality. It is located at -// golang.org/x/tools/cmd/ssadump. -// -func ExampleBuildPackage() { - // Parse the source files. - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments) - if err != nil { - fmt.Print(err) // parse error - return - } - files := []*ast.File{f} - - // Create the type-checker's package. - pkg := types.NewPackage("hello", "") - - // Type-check the package, load dependencies. - // Create and build the SSA program. - hello, _, err := ssautil.BuildPackage( - new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions) - if err != nil { - fmt.Print(err) // type error in some package - return - } - - // Print out the package. - hello.WriteTo(os.Stdout) - - // Print out the package-level functions. - hello.Func("init").WriteTo(os.Stdout) - hello.Func("main").WriteTo(os.Stdout) - - // Output: - // - // package hello: - // func init func() - // var init$guard bool - // func main func() - // const message message = "Hello, World!":untyped string - // - // # Name: hello.init - // # Package: hello - // # Synthetic: package initializer - // func init(): - // 0: entry P:0 S:2 - // t0 = *init$guard bool - // if t0 goto 2 else 1 - // 1: init.start P:1 S:1 - // *init$guard = true:bool - // t1 = fmt.init() () - // jump 2 - // 2: init.done P:2 S:0 - // return - // - // # Name: hello.main - // # Package: hello - // # Location: hello.go:8:6 - // func main(): - // 0: entry P:0 S:0 - // t0 = new [1]interface{} (varargs) *[1]interface{} - // t1 = &t0[0:int] *interface{} - // t2 = make interface{} <- string ("Hello, World!":string) interface{} - // *t1 = t2 - // t3 = slice t0[:] []interface{} - // t4 = fmt.Println(t3...) (n int, err error) - // return -} - -// This program shows how to load a main package (cmd/cover) and all its -// dependencies from source, using the loader, and then build SSA code -// for the entire program. This is what you'd typically use for a -// whole-program analysis. -// -func ExampleLoadProgram() { - // Load cmd/cover and its dependencies. - var conf loader.Config - conf.Import("cmd/cover") - lprog, err := conf.Load() - if err != nil { - fmt.Print(err) // type error in some package - return - } - - // Create SSA-form program representation. - prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions) - - // Build SSA code for the entire cmd/cover program. - prog.Build() - - // Output: -} diff --git a/go/ssa/func14.go b/go/ssa/func14.go deleted file mode 100644 index 528f66e2db..0000000000 --- a/go/ssa/func14.go +++ /dev/null @@ -1,692 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file implements the Function and BasicBlock types. - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - "io" - "os" - "strings" - - "golang.org/x/tools/go/types" -) - -// addEdge adds a control-flow graph edge from from to to. -func addEdge(from, to *BasicBlock) { - from.Succs = append(from.Succs, to) - to.Preds = append(to.Preds, from) -} - -// Parent returns the function that contains block b. -func (b *BasicBlock) Parent() *Function { return b.parent } - -// String returns a human-readable label of this block. -// It is not guaranteed unique within the function. -// -func (b *BasicBlock) String() string { - return fmt.Sprintf("%d", b.Index) -} - -// emit appends an instruction to the current basic block. -// If the instruction defines a Value, it is returned. -// -func (b *BasicBlock) emit(i Instruction) Value { - i.setBlock(b) - b.Instrs = append(b.Instrs, i) - v, _ := i.(Value) - return v -} - -// predIndex returns the i such that b.Preds[i] == c or panics if -// there is none. -func (b *BasicBlock) predIndex(c *BasicBlock) int { - for i, pred := range b.Preds { - if pred == c { - return i - } - } - panic(fmt.Sprintf("no edge %s -> %s", c, b)) -} - -// hasPhi returns true if b.Instrs contains φ-nodes. -func (b *BasicBlock) hasPhi() bool { - _, ok := b.Instrs[0].(*Phi) - return ok -} - -// phis returns the prefix of b.Instrs containing all the block's φ-nodes. -func (b *BasicBlock) phis() []Instruction { - for i, instr := range b.Instrs { - if _, ok := instr.(*Phi); !ok { - return b.Instrs[:i] - } - } - return nil // unreachable in well-formed blocks -} - -// replacePred replaces all occurrences of p in b's predecessor list with q. -// Ordinarily there should be at most one. -// -func (b *BasicBlock) replacePred(p, q *BasicBlock) { - for i, pred := range b.Preds { - if pred == p { - b.Preds[i] = q - } - } -} - -// replaceSucc replaces all occurrences of p in b's successor list with q. -// Ordinarily there should be at most one. -// -func (b *BasicBlock) replaceSucc(p, q *BasicBlock) { - for i, succ := range b.Succs { - if succ == p { - b.Succs[i] = q - } - } -} - -// removePred removes all occurrences of p in b's -// predecessor list and φ-nodes. -// Ordinarily there should be at most one. -// -func (b *BasicBlock) removePred(p *BasicBlock) { - phis := b.phis() - - // We must preserve edge order for φ-nodes. - j := 0 - for i, pred := range b.Preds { - if pred != p { - b.Preds[j] = b.Preds[i] - // Strike out φ-edge too. - for _, instr := range phis { - phi := instr.(*Phi) - phi.Edges[j] = phi.Edges[i] - } - j++ - } - } - // Nil out b.Preds[j:] and φ-edges[j:] to aid GC. - for i := j; i < len(b.Preds); i++ { - b.Preds[i] = nil - for _, instr := range phis { - instr.(*Phi).Edges[i] = nil - } - } - b.Preds = b.Preds[:j] - for _, instr := range phis { - phi := instr.(*Phi) - phi.Edges = phi.Edges[:j] - } -} - -// Destinations associated with unlabelled for/switch/select stmts. -// We push/pop one of these as we enter/leave each construct and for -// each BranchStmt we scan for the innermost target of the right type. -// -type targets struct { - tail *targets // rest of stack - _break *BasicBlock - _continue *BasicBlock - _fallthrough *BasicBlock -} - -// Destinations associated with a labelled block. -// We populate these as labels are encountered in forward gotos or -// labelled statements. -// -type lblock struct { - _goto *BasicBlock - _break *BasicBlock - _continue *BasicBlock -} - -// labelledBlock returns the branch target associated with the -// specified label, creating it if needed. -// -func (f *Function) labelledBlock(label *ast.Ident) *lblock { - lb := f.lblocks[label.Obj] - if lb == nil { - lb = &lblock{_goto: f.newBasicBlock(label.Name)} - if f.lblocks == nil { - f.lblocks = make(map[*ast.Object]*lblock) - } - f.lblocks[label.Obj] = lb - } - return lb -} - -// addParam adds a (non-escaping) parameter to f.Params of the -// specified name, type and source position. -// -func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter { - v := &Parameter{ - name: name, - typ: typ, - pos: pos, - parent: f, - } - f.Params = append(f.Params, v) - return v -} - -func (f *Function) addParamObj(obj types.Object) *Parameter { - name := obj.Name() - if name == "" { - name = fmt.Sprintf("arg%d", len(f.Params)) - } - param := f.addParam(name, obj.Type(), obj.Pos()) - param.object = obj - return param -} - -// addSpilledParam declares a parameter that is pre-spilled to the -// stack; the function body will load/store the spilled location. -// Subsequent lifting will eliminate spills where possible. -// -func (f *Function) addSpilledParam(obj types.Object) { - param := f.addParamObj(obj) - spill := &Alloc{Comment: obj.Name()} - spill.setType(types.NewPointer(obj.Type())) - spill.setPos(obj.Pos()) - f.objects[obj] = spill - f.Locals = append(f.Locals, spill) - f.emit(spill) - f.emit(&Store{Addr: spill, Val: param}) -} - -// startBody initializes the function prior to generating SSA code for its body. -// Precondition: f.Type() already set. -// -func (f *Function) startBody() { - f.currentBlock = f.newBasicBlock("entry") - f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init -} - -// createSyntacticParams populates f.Params and generates code (spills -// and named result locals) for all the parameters declared in the -// syntax. In addition it populates the f.objects mapping. -// -// Preconditions: -// f.startBody() was called. -// Postcondition: -// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0) -// -func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) { - // Receiver (at most one inner iteration). - if recv != nil { - for _, field := range recv.List { - for _, n := range field.Names { - f.addSpilledParam(f.Pkg.info.Defs[n]) - } - // Anonymous receiver? No need to spill. - if field.Names == nil { - f.addParamObj(f.Signature.Recv()) - } - } - } - - // Parameters. - if functype.Params != nil { - n := len(f.Params) // 1 if has recv, 0 otherwise - for _, field := range functype.Params.List { - for _, n := range field.Names { - f.addSpilledParam(f.Pkg.info.Defs[n]) - } - // Anonymous parameter? No need to spill. - if field.Names == nil { - f.addParamObj(f.Signature.Params().At(len(f.Params) - n)) - } - } - } - - // Named results. - if functype.Results != nil { - for _, field := range functype.Results.List { - // Implicit "var" decl of locals for named results. - for _, n := range field.Names { - f.namedResults = append(f.namedResults, f.addLocalForIdent(n)) - } - } - } -} - -// numberRegisters assigns numbers to all SSA registers -// (value-defining Instructions) in f, to aid debugging. -// (Non-Instruction Values are named at construction.) -// -func numberRegisters(f *Function) { - v := 0 - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - switch instr.(type) { - case Value: - instr.(interface { - setNum(int) - }).setNum(v) - v++ - } - } - } -} - -// buildReferrers populates the def/use information in all non-nil -// Value.Referrers slice. -// Precondition: all such slices are initially empty. -func buildReferrers(f *Function) { - var rands []*Value - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - rands = instr.Operands(rands[:0]) // recycle storage - for _, rand := range rands { - if r := *rand; r != nil { - if ref := r.Referrers(); ref != nil { - *ref = append(*ref, instr) - } - } - } - } - } -} - -// finishBody() finalizes the function after SSA code generation of its body. -func (f *Function) finishBody() { - f.objects = nil - f.currentBlock = nil - f.lblocks = nil - - // Don't pin the AST in memory (except in debug mode). - if n := f.syntax; n != nil && !f.debugInfo() { - f.syntax = extentNode{n.Pos(), n.End()} - } - - // Remove from f.Locals any Allocs that escape to the heap. - j := 0 - for _, l := range f.Locals { - if !l.Heap { - f.Locals[j] = l - j++ - } - } - // Nil out f.Locals[j:] to aid GC. - for i := j; i < len(f.Locals); i++ { - f.Locals[i] = nil - } - f.Locals = f.Locals[:j] - - optimizeBlocks(f) - - buildReferrers(f) - - buildDomTree(f) - - if f.Prog.mode&NaiveForm == 0 { - // For debugging pre-state of lifting pass: - // numberRegisters(f) - // f.WriteTo(os.Stderr) - lift(f) - } - - f.namedResults = nil // (used by lifting) - - numberRegisters(f) - - if f.Prog.mode&PrintFunctions != 0 { - printMu.Lock() - f.WriteTo(os.Stdout) - printMu.Unlock() - } - - if f.Prog.mode&SanityCheckFunctions != 0 { - mustSanityCheck(f, nil) - } -} - -// removeNilBlocks eliminates nils from f.Blocks and updates each -// BasicBlock.Index. Use this after any pass that may delete blocks. -// -func (f *Function) removeNilBlocks() { - j := 0 - for _, b := range f.Blocks { - if b != nil { - b.Index = j - f.Blocks[j] = b - j++ - } - } - // Nil out f.Blocks[j:] to aid GC. - for i := j; i < len(f.Blocks); i++ { - f.Blocks[i] = nil - } - f.Blocks = f.Blocks[:j] -} - -// SetDebugMode sets the debug mode for package pkg. If true, all its -// functions will include full debug info. This greatly increases the -// size of the instruction stream, and causes Functions to depend upon -// the ASTs, potentially keeping them live in memory for longer. -// -func (pkg *Package) SetDebugMode(debug bool) { - // TODO(adonovan): do we want ast.File granularity? - pkg.debug = debug -} - -// debugInfo reports whether debug info is wanted for this function. -func (f *Function) debugInfo() bool { - return f.Pkg != nil && f.Pkg.debug -} - -// addNamedLocal creates a local variable, adds it to function f and -// returns it. Its name and type are taken from obj. Subsequent -// calls to f.lookup(obj) will return the same local. -// -func (f *Function) addNamedLocal(obj types.Object) *Alloc { - l := f.addLocal(obj.Type(), obj.Pos()) - l.Comment = obj.Name() - f.objects[obj] = l - return l -} - -func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc { - return f.addNamedLocal(f.Pkg.info.Defs[id]) -} - -// addLocal creates an anonymous local variable of type typ, adds it -// 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{} - v.setType(types.NewPointer(typ)) - v.setPos(pos) - f.Locals = append(f.Locals, v) - f.emit(v) - return v -} - -// lookup returns the address of the named variable identified by obj -// that is local to function f or one of its enclosing functions. -// If escaping, the reference comes from a potentially escaping pointer -// expression and the referent must be heap-allocated. -// -func (f *Function) lookup(obj types.Object, escaping bool) Value { - if v, ok := f.objects[obj]; ok { - if alloc, ok := v.(*Alloc); ok && escaping { - alloc.Heap = true - } - return v // function-local var (address) - } - - // Definition must be in an enclosing function; - // plumb it through intervening closures. - if f.parent == nil { - panic("no ssa.Value for " + obj.String()) - } - outer := f.parent.lookup(obj, true) // escaping - v := &FreeVar{ - name: obj.Name(), - typ: outer.Type(), - pos: outer.Pos(), - outer: outer, - parent: f, - } - f.objects[obj] = v - f.FreeVars = append(f.FreeVars, v) - return v -} - -// emit emits the specified instruction to function f. -func (f *Function) emit(instr Instruction) Value { - return f.currentBlock.emit(instr) -} - -// RelString returns the full name of this function, qualified by -// package name, receiver type, etc. -// -// The specific formatting rules are not guaranteed and may change. -// -// Examples: -// "math.IsNaN" // a package-level function -// "(*bytes.Buffer).Bytes" // a declared method or a wrapper -// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0) -// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure) -// "main.main$1" // an anonymous function in main -// "main.init#1" // a declared init function -// "main.init" // the synthesized package initializer -// -// When these functions are referred to from within the same package -// (i.e. from == f.Pkg.Object), they are rendered without the package path. -// For example: "IsNaN", "(*Buffer).Bytes", etc. -// -// All non-synthetic functions have distinct package-qualified names. -// (But two methods may have the same name "(T).f" if one is a synthetic -// wrapper promoting a non-exported method "f" from another package; in -// that case, the strings are equal but the identifiers "f" are distinct.) -// -func (f *Function) RelString(from *types.Package) string { - // Anonymous? - if f.parent != nil { - // An anonymous function's Name() looks like "parentName$1", - // but its String() should include the type/package/etc. - parent := f.parent.RelString(from) - for i, anon := range f.parent.AnonFuncs { - if anon == f { - return fmt.Sprintf("%s$%d", parent, 1+i) - } - } - - return f.name // should never happen - } - - // Method (declared or wrapper)? - if recv := f.Signature.Recv(); recv != nil { - return f.relMethod(from, recv.Type()) - } - - // Thunk? - if f.method != nil { - return f.relMethod(from, f.method.Recv()) - } - - // Bound? - if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") { - return f.relMethod(from, f.FreeVars[0].Type()) - } - - // Package-level function? - // Prefix with package name for cross-package references only. - if p := f.pkg(); p != nil && p != from { - return fmt.Sprintf("%s.%s", p.Path(), f.name) - } - - // Unknown. - return f.name -} - -func (f *Function) relMethod(from *types.Package, recv types.Type) string { - return fmt.Sprintf("(%s).%s", relType(recv, from), f.name) -} - -// writeSignature writes to buf the signature sig in declaration syntax. -func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) { - buf.WriteString("func ") - if recv := sig.Recv(); recv != nil { - buf.WriteString("(") - if n := params[0].Name(); n != "" { - buf.WriteString(n) - buf.WriteString(" ") - } - types.WriteType(buf, params[0].Type(), types.RelativeTo(from)) - buf.WriteString(") ") - } - buf.WriteString(name) - types.WriteSignature(buf, sig, types.RelativeTo(from)) -} - -func (f *Function) pkg() *types.Package { - if f.Pkg != nil { - return f.Pkg.Pkg - } - return nil -} - -var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer - -func (f *Function) WriteTo(w io.Writer) (int64, error) { - var buf bytes.Buffer - WriteFunction(&buf, f) - n, err := w.Write(buf.Bytes()) - return int64(n), err -} - -// WriteFunction writes to buf a human-readable "disassembly" of f. -func WriteFunction(buf *bytes.Buffer, f *Function) { - fmt.Fprintf(buf, "# Name: %s\n", f.String()) - if f.Pkg != nil { - fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path()) - } - if syn := f.Synthetic; syn != "" { - fmt.Fprintln(buf, "# Synthetic:", syn) - } - if pos := f.Pos(); pos.IsValid() { - fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos)) - } - - if f.parent != nil { - fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name()) - } - - if f.Recover != nil { - fmt.Fprintf(buf, "# Recover: %s\n", f.Recover) - } - - from := f.pkg() - - if f.FreeVars != nil { - buf.WriteString("# Free variables:\n") - for i, fv := range f.FreeVars { - fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from)) - } - } - - if len(f.Locals) > 0 { - buf.WriteString("# Locals:\n") - for i, l := range f.Locals { - fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from)) - } - } - writeSignature(buf, from, f.Name(), f.Signature, f.Params) - buf.WriteString(":\n") - - if f.Blocks == nil { - buf.WriteString("\t(external)\n") - } - - // NB. column calculations are confused by non-ASCII - // characters and assume 8-space tabs. - const punchcard = 80 // for old time's sake. - const tabwidth = 8 - for _, b := range f.Blocks { - if b == nil { - // Corrupt CFG. - fmt.Fprintf(buf, ".nil:\n") - continue - } - n, _ := fmt.Fprintf(buf, "%d:", b.Index) - bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs)) - fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg) - - if false { // CFG debugging - fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs) - } - for _, instr := range b.Instrs { - buf.WriteString("\t") - switch v := instr.(type) { - case Value: - l := punchcard - tabwidth - // Left-align the instruction. - if name := v.Name(); name != "" { - n, _ := fmt.Fprintf(buf, "%s = ", name) - l -= n - } - n, _ := buf.WriteString(instr.String()) - l -= n - // Right-align the type if there's space. - if t := v.Type(); t != nil { - buf.WriteByte(' ') - ts := relType(t, from) - l -= len(ts) + len(" ") // (spaces before and after type) - if l > 0 { - fmt.Fprintf(buf, "%*s", l, "") - } - buf.WriteString(ts) - } - case nil: - // Be robust against bad transforms. - buf.WriteString("") - default: - buf.WriteString(instr.String()) - } - buf.WriteString("\n") - } - } - fmt.Fprintf(buf, "\n") -} - -// newBasicBlock adds to f a new basic block and returns it. It does -// not automatically become the current block for subsequent calls to emit. -// comment is an optional string for more readable debugging output. -// -func (f *Function) newBasicBlock(comment string) *BasicBlock { - b := &BasicBlock{ - Index: len(f.Blocks), - Comment: comment, - parent: f, - } - b.Succs = b.succs2[:0] - f.Blocks = append(f.Blocks, b) - return b -} - -// NewFunction returns a new synthetic Function instance belonging to -// prog, with its name and signature fields set as specified. -// -// The caller is responsible for initializing the remaining fields of -// the function object, e.g. Pkg, Params, Blocks. -// -// It is practically impossible for clients to construct well-formed -// SSA functions/packages/programs directly, so we assume this is the -// job of the Builder alone. NewFunction exists to provide clients a -// little flexibility. For example, analysis tools may wish to -// construct fake Functions for the root of the callgraph, a fake -// "reflect" package, etc. -// -// TODO(adonovan): think harder about the API here. -// -func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function { - return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance} -} - -type extentNode [2]token.Pos - -func (n extentNode) Pos() token.Pos { return n[0] } -func (n extentNode) End() token.Pos { return n[1] } - -// Syntax returns an ast.Node whose Pos/End methods provide the -// lexical extent of the function if it was defined by Go source code -// (f.Synthetic==""), or nil otherwise. -// -// If f was built with debug information (see Package.SetDebugRef), -// the result is the *ast.FuncDecl or *ast.FuncLit that declared the -// function. Otherwise, it is an opaque Node providing only position -// information; this avoids pinning the AST in memory. -// -func (f *Function) Syntax() ast.Node { return f.syntax } diff --git a/go/ssa/interp/external14.go b/go/ssa/interp/external14.go deleted file mode 100644 index c07c562e49..0000000000 --- a/go/ssa/interp/external14.go +++ /dev/null @@ -1,526 +0,0 @@ -// 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. - -// +build !go1.5 - -package interp - -// Emulated functions that we cannot interpret because they are -// external or because they use "unsafe" or "reflect" operations. - -import ( - "math" - "os" - "runtime" - "strings" - "syscall" - "time" - "unsafe" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -type externalFn func(fr *frame, args []value) value - -// TODO(adonovan): fix: reflect.Value abstracts an lvalue or an -// rvalue; Set() causes mutations that can be observed via aliases. -// We have not captured that correctly here. - -// Key strings are from Function.String(). -var externals map[string]externalFn - -func init() { - // That little dot ۰ is an Arabic zero numeral (U+06F0), categories [Nd]. - externals = map[string]externalFn{ - "(*sync.Pool).Get": ext۰sync۰Pool۰Get, - "(*sync.Pool).Put": ext۰sync۰Pool۰Put, - "(reflect.Value).Bool": ext۰reflect۰Value۰Bool, - "(reflect.Value).CanAddr": ext۰reflect۰Value۰CanAddr, - "(reflect.Value).CanInterface": ext۰reflect۰Value۰CanInterface, - "(reflect.Value).Elem": ext۰reflect۰Value۰Elem, - "(reflect.Value).Field": ext۰reflect۰Value۰Field, - "(reflect.Value).Float": ext۰reflect۰Value۰Float, - "(reflect.Value).Index": ext۰reflect۰Value۰Index, - "(reflect.Value).Int": ext۰reflect۰Value۰Int, - "(reflect.Value).Interface": ext۰reflect۰Value۰Interface, - "(reflect.Value).IsNil": ext۰reflect۰Value۰IsNil, - "(reflect.Value).IsValid": ext۰reflect۰Value۰IsValid, - "(reflect.Value).Kind": ext۰reflect۰Value۰Kind, - "(reflect.Value).Len": ext۰reflect۰Value۰Len, - "(reflect.Value).MapIndex": ext۰reflect۰Value۰MapIndex, - "(reflect.Value).MapKeys": ext۰reflect۰Value۰MapKeys, - "(reflect.Value).NumField": ext۰reflect۰Value۰NumField, - "(reflect.Value).NumMethod": ext۰reflect۰Value۰NumMethod, - "(reflect.Value).Pointer": ext۰reflect۰Value۰Pointer, - "(reflect.Value).Set": ext۰reflect۰Value۰Set, - "(reflect.Value).String": ext۰reflect۰Value۰String, - "(reflect.Value).Type": ext۰reflect۰Value۰Type, - "(reflect.Value).Uint": ext۰reflect۰Value۰Uint, - "(reflect.error).Error": ext۰reflect۰error۰Error, - "(reflect.rtype).Bits": ext۰reflect۰rtype۰Bits, - "(reflect.rtype).Elem": ext۰reflect۰rtype۰Elem, - "(reflect.rtype).Field": ext۰reflect۰rtype۰Field, - "(reflect.rtype).In": ext۰reflect۰rtype۰In, - "(reflect.rtype).Kind": ext۰reflect۰rtype۰Kind, - "(reflect.rtype).NumField": ext۰reflect۰rtype۰NumField, - "(reflect.rtype).NumIn": ext۰reflect۰rtype۰NumIn, - "(reflect.rtype).NumMethod": ext۰reflect۰rtype۰NumMethod, - "(reflect.rtype).NumOut": ext۰reflect۰rtype۰NumOut, - "(reflect.rtype).Out": ext۰reflect۰rtype۰Out, - "(reflect.rtype).Size": ext۰reflect۰rtype۰Size, - "(reflect.rtype).String": ext۰reflect۰rtype۰String, - "bytes.Equal": ext۰bytes۰Equal, - "bytes.IndexByte": ext۰bytes۰IndexByte, - "hash/crc32.haveSSE42": ext۰crc32۰haveSSE42, - "math.Abs": ext۰math۰Abs, - "math.Exp": ext۰math۰Exp, - "math.Float32bits": ext۰math۰Float32bits, - "math.Float32frombits": ext۰math۰Float32frombits, - "math.Float64bits": ext۰math۰Float64bits, - "math.Float64frombits": ext۰math۰Float64frombits, - "math.Ldexp": ext۰math۰Ldexp, - "math.Log": ext۰math۰Log, - "math.Min": ext۰math۰Min, - "math.hasSSE4": ext۰math۰hasSSE4, - "os.Pipe": ext۰os۰Pipe, - "os.runtime_args": ext۰os۰runtime_args, - "os.runtime_beforeExit": ext۰os۰runtime_beforeExit, - "reflect.New": ext۰reflect۰New, - "reflect.SliceOf": ext۰reflect۰SliceOf, - "reflect.TypeOf": ext۰reflect۰TypeOf, - "reflect.ValueOf": ext۰reflect۰ValueOf, - "reflect.Zero": ext۰reflect۰Zero, - "reflect.init": ext۰reflect۰Init, - "reflect.valueInterface": ext۰reflect۰valueInterface, - "runtime.Breakpoint": ext۰runtime۰Breakpoint, - "runtime.Caller": ext۰runtime۰Caller, - "runtime.Callers": ext۰runtime۰Callers, - "runtime.FuncForPC": ext۰runtime۰FuncForPC, - "runtime.GC": ext۰runtime۰GC, - "runtime.GOMAXPROCS": ext۰runtime۰GOMAXPROCS, - "runtime.Goexit": ext۰runtime۰Goexit, - "runtime.Gosched": ext۰runtime۰Gosched, - "runtime.init": ext۰runtime۰init, - "runtime.NumCPU": ext۰runtime۰NumCPU, - "runtime.ReadMemStats": ext۰runtime۰ReadMemStats, - "runtime.SetFinalizer": ext۰runtime۰SetFinalizer, - "(*runtime.Func).Entry": ext۰runtime۰Func۰Entry, - "(*runtime.Func).FileLine": ext۰runtime۰Func۰FileLine, - "(*runtime.Func).Name": ext۰runtime۰Func۰Name, - "runtime.environ": ext۰runtime۰environ, - "runtime.getgoroot": ext۰runtime۰getgoroot, - "strings.Index": ext۰strings۰Index, - "strings.IndexByte": ext۰strings۰IndexByte, - "sync.runtime_Semacquire": ext۰sync۰runtime_Semacquire, - "sync.runtime_Semrelease": ext۰sync۰runtime_Semrelease, - "sync.runtime_Syncsemcheck": ext۰sync۰runtime_Syncsemcheck, - "sync.runtime_registerPoolCleanup": ext۰sync۰runtime_registerPoolCleanup, - "sync/atomic.AddInt32": ext۰atomic۰AddInt32, - "sync/atomic.AddUint32": ext۰atomic۰AddUint32, - "sync/atomic.AddUint64": ext۰atomic۰AddUint64, - "sync/atomic.CompareAndSwapInt32": ext۰atomic۰CompareAndSwapInt32, - "sync/atomic.LoadInt32": ext۰atomic۰LoadInt32, - "sync/atomic.LoadUint32": ext۰atomic۰LoadUint32, - "sync/atomic.StoreInt32": ext۰atomic۰StoreInt32, - "sync/atomic.StoreUint32": ext۰atomic۰StoreUint32, - "syscall.Close": ext۰syscall۰Close, - "syscall.Exit": ext۰syscall۰Exit, - "syscall.Fstat": ext۰syscall۰Fstat, - "syscall.Getpid": ext۰syscall۰Getpid, - "syscall.Getwd": ext۰syscall۰Getwd, - "syscall.Kill": ext۰syscall۰Kill, - "syscall.Lstat": ext۰syscall۰Lstat, - "syscall.Open": ext۰syscall۰Open, - "syscall.ParseDirent": ext۰syscall۰ParseDirent, - "syscall.RawSyscall": ext۰syscall۰RawSyscall, - "syscall.Read": ext۰syscall۰Read, - "syscall.ReadDirent": ext۰syscall۰ReadDirent, - "syscall.Stat": ext۰syscall۰Stat, - "syscall.Write": ext۰syscall۰Write, - "syscall.runtime_envs": ext۰runtime۰environ, - "testing.runExample": ext۰testing۰runExample, - "time.Sleep": ext۰time۰Sleep, - "time.now": ext۰time۰now, - } -} - -// wrapError returns an interpreted 'error' interface value for err. -func wrapError(err error) value { - if err == nil { - return iface{} - } - return iface{t: errorType, v: err.Error()} -} - -func ext۰sync۰Pool۰Get(fr *frame, args []value) value { - Pool := fr.i.prog.ImportedPackage("sync").Type("Pool").Object() - _, newIndex, _ := types.LookupFieldOrMethod(Pool.Type(), false, Pool.Pkg(), "New") - - if New := (*args[0].(*value)).(structure)[newIndex[0]]; New != nil { - return call(fr.i, fr, 0, New, nil) - } - return nil -} - -func ext۰sync۰Pool۰Put(fr *frame, args []value) value { - return nil -} - -func ext۰bytes۰Equal(fr *frame, args []value) value { - // func Equal(a, b []byte) bool - a := args[0].([]value) - b := args[1].([]value) - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -func ext۰bytes۰IndexByte(fr *frame, args []value) value { - // func IndexByte(s []byte, c byte) int - s := args[0].([]value) - c := args[1].(byte) - for i, b := range s { - if b.(byte) == c { - return i - } - } - return -1 -} - -func ext۰crc32۰haveSSE42(fr *frame, args []value) value { - return false -} - -func ext۰math۰Float64frombits(fr *frame, args []value) value { - return math.Float64frombits(args[0].(uint64)) -} - -func ext۰math۰Float64bits(fr *frame, args []value) value { - return math.Float64bits(args[0].(float64)) -} - -func ext۰math۰Float32frombits(fr *frame, args []value) value { - return math.Float32frombits(args[0].(uint32)) -} - -func ext۰math۰Abs(fr *frame, args []value) value { - return math.Abs(args[0].(float64)) -} - -func ext۰math۰Exp(fr *frame, args []value) value { - return math.Exp(args[0].(float64)) -} - -func ext۰math۰Float32bits(fr *frame, args []value) value { - return math.Float32bits(args[0].(float32)) -} - -func ext۰math۰Min(fr *frame, args []value) value { - return math.Min(args[0].(float64), args[1].(float64)) -} - -func ext۰math۰hasSSE4(fr *frame, args []value) value { - return false -} - -func ext۰math۰Ldexp(fr *frame, args []value) value { - return math.Ldexp(args[0].(float64), args[1].(int)) -} - -func ext۰math۰Log(fr *frame, args []value) value { - return math.Log(args[0].(float64)) -} - -func ext۰os۰runtime_args(fr *frame, args []value) value { - return fr.i.osArgs -} - -func ext۰os۰runtime_beforeExit(fr *frame, args []value) value { - return nil -} - -func ext۰runtime۰Breakpoint(fr *frame, args []value) value { - runtime.Breakpoint() - return nil -} - -func ext۰runtime۰Caller(fr *frame, args []value) value { - // func Caller(skip int) (pc uintptr, file string, line int, ok bool) - skip := 1 + args[0].(int) - for i := 0; i < skip; i++ { - if fr != nil { - fr = fr.caller - } - } - var pc uintptr - var file string - var line int - var ok bool - if fr != nil { - fn := fr.fn - // TODO(adonovan): use pc/posn of current instruction, not start of fn. - // (Required to interpret the log package's tests.) - pc = uintptr(unsafe.Pointer(fn)) - posn := fn.Prog.Fset.Position(fn.Pos()) - file = posn.Filename - line = posn.Line - ok = true - } - return tuple{pc, file, line, ok} -} - -func ext۰runtime۰Callers(fr *frame, args []value) value { - // Callers(skip int, pc []uintptr) int - skip := args[0].(int) - pc := args[1].([]value) - for i := 0; i < skip; i++ { - if fr != nil { - fr = fr.caller - } - } - i := 0 - for fr != nil { - pc[i] = uintptr(unsafe.Pointer(fr.fn)) - i++ - fr = fr.caller - } - return i -} - -func ext۰runtime۰FuncForPC(fr *frame, args []value) value { - // FuncForPC(pc uintptr) *Func - pc := args[0].(uintptr) - var fn *ssa.Function - if pc != 0 { - fn = (*ssa.Function)(unsafe.Pointer(pc)) // indeed unsafe! - } - var Func value - Func = structure{fn} // a runtime.Func - return &Func -} - -func ext۰runtime۰environ(fr *frame, args []value) value { - // This function also implements syscall.runtime_envs. - return environ -} - -func ext۰runtime۰getgoroot(fr *frame, args []value) value { - return os.Getenv("GOROOT") -} - -func ext۰strings۰IndexByte(fr *frame, args []value) value { - // func IndexByte(s string, c byte) int - s := args[0].(string) - c := args[1].(byte) - for i := 0; i < len(s); i++ { - if s[i] == c { - return i - } - } - return -1 -} - -func ext۰strings۰Index(fr *frame, args []value) value { - // Call compiled version to avoid tricky asm dependency. - return strings.Index(args[0].(string), args[1].(string)) -} - -func ext۰sync۰runtime_Syncsemcheck(fr *frame, args []value) value { - // TODO(adonovan): fix: implement. - return nil -} - -func ext۰sync۰runtime_registerPoolCleanup(fr *frame, args []value) value { - return nil -} - -func ext۰sync۰runtime_Semacquire(fr *frame, args []value) value { - // TODO(adonovan): fix: implement. - return nil -} - -func ext۰sync۰runtime_Semrelease(fr *frame, args []value) value { - // TODO(adonovan): fix: implement. - return nil -} - -func ext۰runtime۰GOMAXPROCS(fr *frame, args []value) value { - // Ignore args[0]; don't let the interpreted program - // set the interpreter's GOMAXPROCS! - return runtime.GOMAXPROCS(0) -} - -func ext۰runtime۰Goexit(fr *frame, args []value) value { - // TODO(adonovan): don't kill the interpreter's main goroutine. - runtime.Goexit() - return nil -} - -func ext۰runtime۰GC(fr *frame, args []value) value { - runtime.GC() - return nil -} - -func ext۰runtime۰Gosched(fr *frame, args []value) value { - runtime.Gosched() - return nil -} - -func ext۰runtime۰init(fr *frame, args []value) value { - return nil -} - -func ext۰runtime۰NumCPU(fr *frame, args []value) value { - return runtime.NumCPU() -} - -func ext۰runtime۰ReadMemStats(fr *frame, args []value) value { - // TODO(adonovan): populate args[0].(Struct) - return nil -} - -func ext۰atomic۰LoadUint32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - return (*args[0].(*value)).(uint32) -} - -func ext۰atomic۰StoreUint32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - *args[0].(*value) = args[1].(uint32) - return nil -} - -func ext۰atomic۰LoadInt32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - return (*args[0].(*value)).(int32) -} - -func ext۰atomic۰StoreInt32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - *args[0].(*value) = args[1].(int32) - return nil -} - -func ext۰atomic۰CompareAndSwapInt32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - p := args[0].(*value) - if (*p).(int32) == args[1].(int32) { - *p = args[2].(int32) - return true - } - return false -} - -func ext۰atomic۰AddInt32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - p := args[0].(*value) - newv := (*p).(int32) + args[1].(int32) - *p = newv - return newv -} - -func ext۰atomic۰AddUint32(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - p := args[0].(*value) - newv := (*p).(uint32) + args[1].(uint32) - *p = newv - return newv -} - -func ext۰atomic۰AddUint64(fr *frame, args []value) value { - // TODO(adonovan): fix: not atomic! - p := args[0].(*value) - newv := (*p).(uint64) + args[1].(uint64) - *p = newv - return newv -} - -func ext۰runtime۰SetFinalizer(fr *frame, args []value) value { - return nil // ignore -} - -// Pretend: type runtime.Func struct { entry *ssa.Function } - -func ext۰runtime۰Func۰FileLine(fr *frame, args []value) value { - // func (*runtime.Func) FileLine(uintptr) (string, int) - f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) - pc := args[1].(uintptr) - _ = pc - if f != nil { - // TODO(adonovan): use position of current instruction, not fn. - posn := f.Prog.Fset.Position(f.Pos()) - return tuple{posn.Filename, posn.Line} - } - return tuple{"", 0} -} - -func ext۰runtime۰Func۰Name(fr *frame, args []value) value { - // func (*runtime.Func) Name() string - f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) - if f != nil { - return f.String() - } - return "" -} - -func ext۰runtime۰Func۰Entry(fr *frame, args []value) value { - // func (*runtime.Func) Entry() uintptr - f, _ := (*args[0].(*value)).(structure)[0].(*ssa.Function) - return uintptr(unsafe.Pointer(f)) -} - -// This is a workaround for a bug in go/ssa/testmain.go: it creates -// InternalExamples even for Example functions with no Output comment. -// TODO(adonovan): fix (and redesign) testmain.go after Go 1.6. -func ext۰testing۰runExample(fr *frame, args []value) value { - // This is a stripped down runExample that simply calls the function. - // It does not capture and compare output nor recover from panic. - // - // func runExample(eg testing.InternalExample) bool { - // eg.F() - // return true - // } - F := args[0].(structure)[1] - call(fr.i, fr, 0, F, nil) - return true -} - -func ext۰time۰now(fr *frame, args []value) value { - nano := time.Now().UnixNano() - return tuple{int64(nano / 1e9), int32(nano % 1e9)} -} - -func ext۰time۰Sleep(fr *frame, args []value) value { - time.Sleep(time.Duration(args[0].(int64))) - return nil -} - -func ext۰syscall۰Exit(fr *frame, args []value) value { - panic(exitPanic(args[0].(int))) -} - -func ext۰syscall۰Getwd(fr *frame, args []value) value { - s, err := syscall.Getwd() - return tuple{s, wrapError(err)} -} - -func ext۰syscall۰Getpid(fr *frame, args []value) value { - return syscall.Getpid() -} - -func valueToBytes(v value) []byte { - in := v.([]value) - b := make([]byte, len(in)) - for i := range in { - b[i] = in[i].(byte) - } - return b -} diff --git a/go/ssa/interp/interp14.go b/go/ssa/interp/interp14.go deleted file mode 100644 index dbd4dacd79..0000000000 --- a/go/ssa/interp/interp14.go +++ /dev/null @@ -1,752 +0,0 @@ -// 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. - -// +build !go1.5 - -// Package ssa/interp defines an interpreter for the SSA -// representation of Go programs. -// -// This interpreter is provided as an adjunct for testing the SSA -// construction algorithm. Its purpose is to provide a minimal -// metacircular implementation of the dynamic semantics of each SSA -// instruction. It is not, and will never be, a production-quality Go -// interpreter. -// -// The following is a partial list of Go features that are currently -// unsupported or incomplete in the interpreter. -// -// * Unsafe operations, including all uses of unsafe.Pointer, are -// impossible to support given the "boxed" value representation we -// have chosen. -// -// * The reflect package is only partially implemented. -// -// * "sync/atomic" operations are not currently atomic due to the -// "boxed" value representation: it is not possible to read, modify -// and write an interface value atomically. As a consequence, Mutexes -// are currently broken. TODO(adonovan): provide a metacircular -// implementation of Mutex avoiding the broken atomic primitives. -// -// * recover is only partially implemented. Also, the interpreter -// makes no attempt to distinguish target panics from interpreter -// crashes. -// -// * map iteration is asymptotically inefficient. -// -// * the sizes of the int, uint and uintptr types in the target -// program are assumed to be the same as those of the interpreter -// itself. -// -// * all values occupy space, even those of types defined by the spec -// to have zero size, e.g. struct{}. This can cause asymptotic -// performance degradation. -// -// * os.Exit is implemented using panic, causing deferred functions to -// run. -package interp // import "golang.org/x/tools/go/ssa/interp" - -import ( - "fmt" - "go/token" - "os" - "reflect" - "runtime" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -type continuation int - -const ( - kNext continuation = iota - kReturn - kJump -) - -// Mode is a bitmask of options affecting the interpreter. -type Mode uint - -const ( - DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead. - EnableTracing // Print a trace of all instructions as they are interpreted. -) - -type methodSet map[string]*ssa.Function - -// State shared between all interpreted goroutines. -type interpreter struct { - osArgs []value // the value of os.Args - prog *ssa.Program // the SSA program - globals map[ssa.Value]*value // addresses of global variables (immutable) - mode Mode // interpreter options - reflectPackage *ssa.Package // the fake reflect package - errorMethods methodSet // the method set of reflect.error, which implements the error interface. - rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface. - runtimeErrorString types.Type // the runtime.errorString type - sizes types.Sizes // the effective type-sizing function -} - -type deferred struct { - fn value - args []value - instr *ssa.Defer - tail *deferred -} - -type frame struct { - i *interpreter - caller *frame - fn *ssa.Function - block, prevBlock *ssa.BasicBlock - env map[ssa.Value]value // dynamic values of SSA variables - locals []value - defers *deferred - result value - panicking bool - panic interface{} -} - -func (fr *frame) get(key ssa.Value) value { - switch key := key.(type) { - case nil: - // Hack; simplifies handling of optional attributes - // such as ssa.Slice.{Low,High}. - return nil - case *ssa.Function, *ssa.Builtin: - return key - case *ssa.Const: - return constValue(key) - case *ssa.Global: - if r, ok := fr.i.globals[key]; ok { - return r - } - } - if r, ok := fr.env[key]; ok { - return r - } - panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) -} - -// runDefer runs a deferred call d. -// It always returns normally, but may set or clear fr.panic. -// -func (fr *frame) runDefer(d *deferred) { - if fr.i.mode&EnableTracing != 0 { - fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n", - fr.i.prog.Fset.Position(d.instr.Pos())) - } - var ok bool - defer func() { - if !ok { - // Deferred call created a new state of panic. - fr.panicking = true - fr.panic = recover() - } - }() - call(fr.i, fr, d.instr.Pos(), d.fn, d.args) - ok = true -} - -// runDefers executes fr's deferred function calls in LIFO order. -// -// On entry, fr.panicking indicates a state of panic; if -// true, fr.panic contains the panic value. -// -// On completion, if a deferred call started a panic, or if no -// deferred call recovered from a previous state of panic, then -// runDefers itself panics after the last deferred call has run. -// -// If there was no initial state of panic, or it was recovered from, -// runDefers returns normally. -// -func (fr *frame) runDefers() { - for d := fr.defers; d != nil; d = d.tail { - fr.runDefer(d) - } - fr.defers = nil - if fr.panicking { - panic(fr.panic) // new panic, or still panicking - } -} - -// lookupMethod returns the method set for type typ, which may be one -// of the interpreter's fake types. -func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function { - switch typ { - case rtypeType: - return i.rtypeMethods[meth.Id()] - case errorType: - return i.errorMethods[meth.Id()] - } - return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name()) -} - -// visitInstr interprets a single ssa.Instruction within the activation -// record frame. It returns a continuation value indicating where to -// read the next instruction from. -func visitInstr(fr *frame, instr ssa.Instruction) continuation { - switch instr := instr.(type) { - case *ssa.DebugRef: - // no-op - - case *ssa.UnOp: - fr.env[instr] = unop(instr, fr.get(instr.X)) - - case *ssa.BinOp: - fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y)) - - case *ssa.Call: - fn, args := prepareCall(fr, &instr.Call) - fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args) - - case *ssa.ChangeInterface: - fr.env[instr] = fr.get(instr.X) - - 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)} - - case *ssa.Extract: - fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index] - - case *ssa.Slice: - fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max)) - - case *ssa.Return: - switch len(instr.Results) { - case 0: - case 1: - fr.result = fr.get(instr.Results[0]) - default: - var res []value - for _, r := range instr.Results { - res = append(res, fr.get(r)) - } - fr.result = tuple(res) - } - fr.block = nil - return kReturn - - case *ssa.RunDefers: - fr.runDefers() - - case *ssa.Panic: - panic(targetPanic{fr.get(instr.X)}) - - case *ssa.Send: - fr.get(instr.Chan).(chan value) <- fr.get(instr.X) - - case *ssa.Store: - store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val)) - - case *ssa.If: - succ := 1 - if fr.get(instr.Cond).(bool) { - succ = 0 - } - fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ] - return kJump - - case *ssa.Jump: - fr.prevBlock, fr.block = fr.block, fr.block.Succs[0] - return kJump - - case *ssa.Defer: - fn, args := prepareCall(fr, &instr.Call) - fr.defers = &deferred{ - fn: fn, - args: args, - instr: instr, - tail: fr.defers, - } - - case *ssa.Go: - fn, args := prepareCall(fr, &instr.Call) - go call(fr.i, nil, instr.Pos(), fn, args) - - case *ssa.MakeChan: - fr.env[instr] = make(chan value, asInt(fr.get(instr.Size))) - - case *ssa.Alloc: - var addr *value - if instr.Heap { - // new - addr = new(value) - fr.env[instr] = addr - } else { - // local - addr = fr.env[instr].(*value) - } - *addr = zero(deref(instr.Type())) - - case *ssa.MakeSlice: - slice := make([]value, asInt(fr.get(instr.Cap))) - tElt := instr.Type().Underlying().(*types.Slice).Elem() - for i := range slice { - slice[i] = zero(tElt) - } - fr.env[instr] = slice[:asInt(fr.get(instr.Len))] - - case *ssa.MakeMap: - reserve := 0 - if instr.Reserve != nil { - reserve = asInt(fr.get(instr.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()) - - case *ssa.Next: - fr.env[instr] = fr.get(instr.Iter).(iter).next() - - case *ssa.FieldAddr: - x := fr.get(instr.X) - // FIXME wrong! &global.f must not change if we do *global = zero! - fr.env[instr] = &(*x.(*value)).(structure)[instr.Field] - - case *ssa.Field: - fr.env[instr] = fr.get(instr.X).(structure)[instr.Field] - - case *ssa.IndexAddr: - x := fr.get(instr.X) - idx := fr.get(instr.Index) - switch x := x.(type) { - case []value: - fr.env[instr] = &x[asInt(idx)] - case *value: // *array - fr.env[instr] = &(*x).(array)[asInt(idx)] - default: - panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x)) - } - - case *ssa.Index: - fr.env[instr] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))] - - case *ssa.Lookup: - fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index)) - - case *ssa.MapUpdate: - m := fr.get(instr.Map) - key := fr.get(instr.Key) - v := fr.get(instr.Value) - switch m := m.(type) { - case map[value]value: - m[key] = v - case *hashmap: - m.insert(key.(hashable), v) - default: - panic(fmt.Sprintf("illegal map type: %T", m)) - } - - case *ssa.TypeAssert: - fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface)) - - case *ssa.MakeClosure: - var bindings []value - for _, binding := range instr.Bindings { - bindings = append(bindings, fr.get(binding)) - } - fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} - - case *ssa.Phi: - for i, pred := range instr.Block().Preds { - if fr.prevBlock == pred { - fr.env[instr] = fr.get(instr.Edges[i]) - break - } - } - - case *ssa.Select: - var cases []reflect.SelectCase - if !instr.Blocking { - cases = append(cases, reflect.SelectCase{ - Dir: reflect.SelectDefault, - }) - } - for _, state := range instr.States { - var dir reflect.SelectDir - if state.Dir == types.RecvOnly { - dir = reflect.SelectRecv - } else { - dir = reflect.SelectSend - } - var send reflect.Value - if state.Send != nil { - send = reflect.ValueOf(fr.get(state.Send)) - } - cases = append(cases, reflect.SelectCase{ - Dir: dir, - Chan: reflect.ValueOf(fr.get(state.Chan)), - Send: send, - }) - } - chosen, recv, recvOk := reflect.Select(cases) - if !instr.Blocking { - chosen-- // default case should have index -1. - } - r := tuple{chosen, recvOk} - for i, st := range instr.States { - if st.Dir == types.RecvOnly { - var v value - if i == chosen && recvOk { - // No need to copy since send makes an unaliased copy. - v = recv.Interface().(value) - } else { - v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem()) - } - r = append(r, v) - } - } - fr.env[instr] = r - - default: - panic(fmt.Sprintf("unexpected instruction: %T", instr)) - } - - // if val, ok := instr.(ssa.Value); ok { - // fmt.Println(toString(fr.env[val])) // debugging - // } - - return kNext -} - -// prepareCall determines the function value and argument values for a -// function call in a Call, Go or Defer instruction, performing -// interface method lookup if needed. -// -func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { - v := fr.get(call.Value) - if call.Method == nil { - // Function call. - fn = v - } else { - // Interface method invocation. - recv := v.(iface) - if recv.t == nil { - panic("method invoked on nil interface") - } - if f := lookupMethod(fr.i, recv.t, call.Method); f == nil { - // Unreachable in well-typed programs. - panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method)) - } else { - fn = f - } - args = append(args, recv.v) - } - for _, arg := range call.Args { - args = append(args, fr.get(arg)) - } - return -} - -// call interprets a call to a function (function, builtin or closure) -// fn with arguments args, returning its result. -// callpos is the position of the callsite. -// -func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value { - switch fn := fn.(type) { - case *ssa.Function: - if fn == nil { - panic("call of nil function") // nil of func type - } - return callSSA(i, caller, callpos, fn, args, nil) - case *closure: - return callSSA(i, caller, callpos, fn.Fn, args, fn.Env) - case *ssa.Builtin: - return callBuiltin(caller, callpos, fn, args) - } - panic(fmt.Sprintf("cannot call %T", fn)) -} - -func loc(fset *token.FileSet, pos token.Pos) string { - if pos == token.NoPos { - return "" - } - return " at " + fset.Position(pos).String() -} - -// callSSA interprets a call to function fn with arguments args, -// and lexical environment env, returning its result. -// callpos is the position of the callsite. -// -func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value { - if i.mode&EnableTracing != 0 { - fset := fn.Prog.Fset - // TODO(adonovan): fix: loc() lies for external functions. - fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos())) - suffix := "" - if caller != nil { - suffix = ", resuming " + caller.fn.String() + loc(fset, callpos) - } - defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix) - } - fr := &frame{ - i: i, - caller: caller, // for panic/recover - fn: fn, - } - if fn.Parent() == nil { - name := fn.String() - if ext := externals[name]; ext != nil { - if i.mode&EnableTracing != 0 { - fmt.Fprintln(os.Stderr, "\t(external)") - } - return ext(fr, args) - } - if fn.Blocks == nil { - panic("no code for function: " + name) - } - } - fr.env = make(map[ssa.Value]value) - fr.block = fn.Blocks[0] - fr.locals = make([]value, len(fn.Locals)) - for i, l := range fn.Locals { - fr.locals[i] = zero(deref(l.Type())) - fr.env[l] = &fr.locals[i] - } - for i, p := range fn.Params { - fr.env[p] = args[i] - } - for i, fv := range fn.FreeVars { - fr.env[fv] = env[i] - } - for fr.block != nil { - runFrame(fr) - } - // Destroy the locals to avoid accidental use after return. - for i := range fn.Locals { - fr.locals[i] = bad{} - } - return fr.result -} - -// runFrame executes SSA instructions starting at fr.block and -// continuing until a return, a panic, or a recovered panic. -// -// After a panic, runFrame panics. -// -// After a normal return, fr.result contains the result of the call -// and fr.block is nil. -// -// A recovered panic in a function without named return parameters -// (NRPs) becomes a normal return of the zero value of the function's -// result type. -// -// After a recovered panic in a function with NRPs, fr.result is -// undefined and fr.block contains the block at which to resume -// control. -// -func runFrame(fr *frame) { - defer func() { - if fr.block == nil { - return // normal return - } - if fr.i.mode&DisableRecover != 0 { - return // let interpreter crash - } - fr.panicking = true - fr.panic = recover() - if fr.i.mode&EnableTracing != 0 { - fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic) - } - fr.runDefers() - fr.block = fr.fn.Recover - }() - - for { - if fr.i.mode&EnableTracing != 0 { - fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) - } - block: - for _, instr := range fr.block.Instrs { - if fr.i.mode&EnableTracing != 0 { - if v, ok := instr.(ssa.Value); ok { - fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr) - } else { - fmt.Fprintln(os.Stderr, "\t", instr) - } - } - switch visitInstr(fr, instr) { - case kReturn: - return - case kNext: - // no-op - case kJump: - break block - } - } - } -} - -// doRecover implements the recover() built-in. -func doRecover(caller *frame) value { - // recover() must be exactly one level beneath the deferred - // function (two levels beneath the panicking function) to - // have any effect. Thus we ignore both "defer recover()" and - // "defer f() -> g() -> recover()". - if caller.i.mode&DisableRecover == 0 && - caller != nil && !caller.panicking && - caller.caller != nil && caller.caller.panicking { - caller.caller.panicking = false - p := caller.caller.panic - caller.caller.panic = nil - switch p := p.(type) { - case targetPanic: - // The target program explicitly called panic(). - return p.v - case runtime.Error: - // The interpreter encountered a runtime error. - return iface{caller.i.runtimeErrorString, p.Error()} - case string: - // The interpreter explicitly called panic(). - return iface{caller.i.runtimeErrorString, p} - default: - panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p)) - } - } - return iface{} -} - -// setGlobal sets the value of a system-initialized global variable. -func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) { - if g, ok := i.globals[pkg.Var(name)]; ok { - *g = v - return - } - panic("no global variable: " + pkg.Pkg.Path() + "." + name) -} - -var environ []value - -func init() { - for _, s := range os.Environ() { - environ = append(environ, s) - } - environ = append(environ, "GOSSAINTERP=1") - environ = append(environ, "GOARCH="+runtime.GOARCH) -} - -// deleteBodies delete the bodies of all standalone functions except the -// specified ones. A missing intrinsic leads to a clear runtime error. -func deleteBodies(pkg *ssa.Package, except ...string) { - keep := make(map[string]bool) - for _, e := range except { - keep[e] = true - } - for _, mem := range pkg.Members { - if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] { - fn.Blocks = nil - } - } -} - -// Interpret interprets the Go program whose main package is mainpkg. -// mode specifies various interpreter options. filename and args are -// the initial values of os.Args for the target program. sizes is the -// effective type-sizing function for this program. -// -// Interpret returns the exit code of the program: 2 for panic (like -// gc does), or the argument to os.Exit for normal termination. -// -// The SSA program must include the "runtime" package. -// -func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { - i := &interpreter{ - prog: mainpkg.Prog, - globals: make(map[ssa.Value]*value), - mode: mode, - sizes: sizes, - } - runtimePkg := i.prog.ImportedPackage("runtime") - if runtimePkg == nil { - panic("ssa.Program doesn't include runtime package") - } - i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type() - - initReflect(i) - - i.osArgs = append(i.osArgs, filename) - for _, arg := range args { - i.osArgs = append(i.osArgs, arg) - } - - for _, pkg := range i.prog.AllPackages() { - // Initialize global storage. - for _, m := range pkg.Members { - switch v := m.(type) { - case *ssa.Global: - cell := zero(deref(v.Type())) - i.globals[v] = &cell - } - } - - // Ad-hoc initialization for magic system variables. - switch pkg.Pkg.Path() { - case "syscall": - setGlobal(i, pkg, "envs", environ) - - case "reflect": - deleteBodies(pkg, "DeepEqual", "deepValueEqual") - - case "runtime": - sz := sizes.Sizeof(pkg.Pkg.Scope().Lookup("MemStats").Type()) - setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz)) - deleteBodies(pkg, "GOROOT", "gogetenv") - } - } - - // Top-level error handler. - exitCode = 2 - defer func() { - if exitCode != 2 || i.mode&DisableRecover != 0 { - return - } - switch p := recover().(type) { - case exitPanic: - exitCode = int(p) - return - case targetPanic: - fmt.Fprintln(os.Stderr, "panic:", toString(p.v)) - case runtime.Error: - fmt.Fprintln(os.Stderr, "panic:", p.Error()) - case string: - fmt.Fprintln(os.Stderr, "panic:", p) - default: - fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) - } - - // TODO(adonovan): dump panicking interpreter goroutine? - // buf := make([]byte, 0x10000) - // runtime.Stack(buf, false) - // fmt.Fprintln(os.Stderr, string(buf)) - // (Or dump panicking target goroutine?) - }() - - // Run! - call(i, nil, token.NoPos, mainpkg.Func("init"), nil) - if mainFn := mainpkg.Func("main"); mainFn != nil { - call(i, nil, token.NoPos, mainFn, nil) - exitCode = 0 - } else { - fmt.Fprintln(os.Stderr, "No main function.") - exitCode = 1 - } - return -} - -// deref returns a pointer's element type; otherwise it returns typ. -// TODO(adonovan): Import from ssa? -func deref(typ types.Type) types.Type { - if p, ok := typ.Underlying().(*types.Pointer); ok { - return p.Elem() - } - return typ -} diff --git a/go/ssa/interp/interp14_test.go b/go/ssa/interp/interp14_test.go deleted file mode 100644 index 63c3f5301d..0000000000 --- a/go/ssa/interp/interp14_test.go +++ /dev/null @@ -1,367 +0,0 @@ -// 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. - -// +build !go1.5 - -// +build !android,!windows,!plan9 - -package interp_test - -import ( - "bytes" - "fmt" - "go/build" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/interp" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -// Each line contains a space-separated list of $GOROOT/test/ -// filenames comprising the main package of a program. -// They are ordered quickest-first, roughly. -// -// TODO(adonovan): integrate into the $GOROOT/test driver scripts, -// golden file checking, etc. -var gorootTestTests = []string{ - "235.go", - "alias1.go", - "chancap.go", - "func5.go", - "func6.go", - "func7.go", - "func8.go", - "helloworld.go", - "varinit.go", - "escape3.go", - "initcomma.go", - "cmp.go", - "compos.go", - "turing.go", - "indirect.go", - // "complit.go", // tests go1.5 features - "for.go", - "struct0.go", - "intcvt.go", - "printbig.go", - "deferprint.go", - "escape.go", - "range.go", - "const4.go", - "float_lit.go", - "bigalg.go", - "decl.go", - "if.go", - "named.go", - "bigmap.go", - "func.go", - "reorder2.go", - "closure.go", - "gc.go", - "simassign.go", - "iota.go", - "nilptr2.go", - "goprint.go", // doesn't actually assert anything (cmpout) - "utf.go", - "method.go", - "char_lit.go", - "env.go", - "int_lit.go", - "string_lit.go", - "defer.go", - "typeswitch.go", - "stringrange.go", - "reorder.go", - "method3.go", - "literal.go", - "nul1.go", // doesn't actually assert anything (errorcheckoutput) - "zerodivide.go", - "convert.go", - "convT2X.go", - "switch.go", - "initialize.go", - "ddd.go", - "blank.go", // partly disabled - "map.go", - "closedchan.go", - "divide.go", - "rename.go", - "const3.go", - "nil.go", - "recover.go", // reflection parts disabled - "recover1.go", - "recover2.go", - "recover3.go", - "typeswitch1.go", - "floatcmp.go", - "crlf.go", // doesn't actually assert anything (runoutput) - // Slow tests follow. - "bom.go", // ~1.7s - "gc1.go", // ~1.7s - "cmplxdivide.go cmplxdivide1.go", // ~2.4s - - // Working, but not worth enabling: - // "append.go", // works, but slow (15s). - // "gc2.go", // works, but slow, and cheats on the memory check. - // "sigchld.go", // works, but only on POSIX. - // "peano.go", // works only up to n=9, and slow even then. - // "stack.go", // works, but too slow (~30s) by default. - // "solitaire.go", // works, but too slow (~30s). - // "const.go", // works but for but one bug: constant folder doesn't consider representations. - // "init1.go", // too slow (80s) and not that interesting. Cheats on ReadMemStats check too. - // "rotate.go rotate0.go", // emits source for a test - // "rotate.go rotate1.go", // emits source for a test - // "rotate.go rotate2.go", // emits source for a test - // "rotate.go rotate3.go", // emits source for a test - // "64bit.go", // emits source for a test - // "run.go", // test driver, not a test. - - // Broken. TODO(adonovan): fix. - // copy.go // very slow; but with N=4 quickly crashes, slice index out of range. - // nilptr.go // interp: V > uintptr not implemented. Slow test, lots of mem - // args.go // works, but requires specific os.Args from the driver. - // index.go // a template, not a real test. - // mallocfin.go // SetFinalizer not implemented. - - // TODO(adonovan): add tests from $GOROOT/test/* subtrees: - // bench chan bugs fixedbugs interface ken. -} - -// These are files in go.tools/go/ssa/interp/testdata/. -var testdataTests = []string{ - "boundmeth.go", - // "complit.go", // requires go1.5 - "coverage.go", - "defer.go", - "fieldprom.go", - "ifaceconv.go", - "ifaceprom.go", - "initorder.go", - "methprom.go", - "mrvchain.go", - "range.go", - "recover.go", - "reflect.go", - "static.go", - "callstack.go", -} - -// These are files and packages in $GOROOT/src/. -var gorootSrcTests = []string{ - "encoding/ascii85", - "encoding/hex", - // "encoding/pem", // TODO(adonovan): implement (reflect.Value).SetString - // "testing", // TODO(adonovan): implement runtime.Goexit correctly - // "hash/crc32", // TODO(adonovan): implement hash/crc32.haveCLMUL - // "log", // TODO(adonovan): implement runtime.Callers correctly - - // Too slow: - // "container/ring", - // "hash/adler32", - - "unicode/utf8", - "path", - "flag", - "encoding/csv", - "text/scanner", - "unicode", -} - -type successPredicate func(exitcode int, output string) error - -func run(t *testing.T, dir, input string, success successPredicate) bool { - fmt.Printf("Input: %s\n", input) - - start := time.Now() - - var inputs []string - for _, i := range strings.Split(input, " ") { - if strings.HasSuffix(i, ".go") { - i = dir + i - } - inputs = append(inputs, i) - } - - var conf loader.Config - if _, err := conf.FromArgs(inputs, true); err != nil { - t.Errorf("FromArgs(%s) failed: %s", inputs, err) - return false - } - - conf.Import("runtime") - - // Print a helpful hint if we don't make it to the end. - var hint string - defer func() { - if hint != "" { - fmt.Println("FAIL") - fmt.Println(hint) - } else { - fmt.Println("PASS") - } - - interp.CapturedOutput = nil - }() - - hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input) - - iprog, err := conf.Load() - if err != nil { - t.Errorf("conf.Load(%s) failed: %s", inputs, err) - return false - } - - prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) - prog.Build() - - var mainPkg *ssa.Package - var initialPkgs []*ssa.Package - for _, info := range iprog.InitialPackages() { - if info.Pkg.Path() == "runtime" { - continue // not an initial package - } - p := prog.Package(info.Pkg) - initialPkgs = append(initialPkgs, p) - if mainPkg == nil && p.Func("main") != nil { - mainPkg = p - } - } - if mainPkg == nil { - testmainPkg := prog.CreateTestMainPackage(initialPkgs...) - if testmainPkg == nil { - t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg) - return false - } - if testmainPkg.Func("main") == nil { - t.Errorf("synthetic testmain package has no main") - return false - } - mainPkg = testmainPkg - } - - var out bytes.Buffer - interp.CapturedOutput = &out - - hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input) - exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{}) - - // The definition of success varies with each file. - if err := success(exitCode, out.String()); err != nil { - t.Errorf("interp.Interpret(%s) failed: %s", inputs, err) - return false - } - - hint = "" // call off the hounds - - if false { - fmt.Println(input, time.Since(start)) // test profiling - } - - return true -} - -const slash = string(os.PathSeparator) - -func printFailures(failures []string) { - if failures != nil { - fmt.Println("The following tests failed:") - for _, f := range failures { - fmt.Printf("\t%s\n", f) - } - } -} - -func success(exitcode int, output string) error { - if exitcode != 0 { - return fmt.Errorf("exit code was %d", exitcode) - } - if strings.Contains(output, "BUG") { - return fmt.Errorf("exited zero but output contained 'BUG'") - } - return nil -} - -// TestTestdataFiles runs the interpreter on testdata/*.go. -func TestTestdataFiles(t *testing.T) { - var failures []string - start := time.Now() - for _, input := range testdataTests { - if testing.Short() && time.Since(start) > 30*time.Second { - printFailures(failures) - t.Skipf("timeout - aborting test") - } - if !run(t, "testdata"+slash, input, success) { - failures = append(failures, input) - } - } - printFailures(failures) -} - -// TestGorootTest runs the interpreter on $GOROOT/test/*.go. -func TestGorootTest(t *testing.T) { - if testing.Short() { - t.Skip() // too slow (~30s) - } - - var failures []string - - for _, input := range gorootTestTests { - if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) { - failures = append(failures, input) - } - } - for _, input := range gorootSrcTests { - if !run(t, filepath.Join(build.Default.GOROOT, "src")+slash, input, success) { - failures = append(failures, input) - } - } - printFailures(failures) -} - -// TestTestmainPackage runs the interpreter on a synthetic "testmain" package. -func TestTestmainPackage(t *testing.T) { - if testing.Short() { - t.Skip() // too slow on some platforms - } - - success := func(exitcode int, output string) error { - if exitcode == 0 { - return fmt.Errorf("unexpected success") - } - if !strings.Contains(output, "FAIL: TestFoo") { - return fmt.Errorf("missing failure log for TestFoo") - } - if !strings.Contains(output, "FAIL: TestBar") { - return fmt.Errorf("missing failure log for TestBar") - } - // TODO(adonovan): test benchmarks too - return nil - } - run(t, "testdata"+slash, "a_test.go", success) -} - -// CreateTestMainPackage should return nil if there were no tests. -func TestNullTestmainPackage(t *testing.T) { - var conf loader.Config - conf.CreateFromFilenames("", "testdata/b_test.go") - iprog, err := conf.Load() - if err != nil { - t.Fatalf("CreatePackages failed: %s", err) - } - prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) - mainPkg := prog.Package(iprog.Created[0].Pkg) - if mainPkg.Func("main") != nil { - t.Fatalf("unexpected main function") - } - if prog.CreateTestMainPackage(mainPkg) != nil { - t.Fatalf("CreateTestMainPackage returned non-nil") - } -} diff --git a/go/ssa/interp/map14.go b/go/ssa/interp/map14.go deleted file mode 100644 index a268c812fa..0000000000 --- a/go/ssa/interp/map14.go +++ /dev/null @@ -1,115 +0,0 @@ -// 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. - -// +build !go1.5 - -package interp - -// Custom hashtable atop map. -// For use when the key's equivalence relation is not consistent with ==. - -// The Go specification doesn't address the atomicity of map operations. -// The FAQ states that an implementation is permitted to crash on -// concurrent map access. - -import ( - "golang.org/x/tools/go/types" -) - -type hashable interface { - hash(t types.Type) int - eq(t types.Type, x interface{}) bool -} - -type entry struct { - key hashable - value value - next *entry -} - -// A hashtable atop the built-in map. Since each bucket contains -// exactly one hash value, there's no need to perform hash-equality -// tests when walking the linked list. Rehashing is done by the -// underlying map. -type hashmap struct { - keyType types.Type - table map[int]*entry - length int // number of entries in map -} - -// makeMap returns an empty initialized map of key type kt, -// preallocating space for reserve elements. -func makeMap(kt types.Type, reserve int) value { - if usesBuiltinMap(kt) { - return make(map[value]value, reserve) - } - return &hashmap{keyType: kt, table: make(map[int]*entry, reserve)} -} - -// delete removes the association for key k, if any. -func (m *hashmap) delete(k hashable) { - if m != nil { - hash := k.hash(m.keyType) - head := m.table[hash] - if head != nil { - if k.eq(m.keyType, head.key) { - m.table[hash] = head.next - m.length-- - return - } - prev := head - for e := head.next; e != nil; e = e.next { - if k.eq(m.keyType, e.key) { - prev.next = e.next - m.length-- - return - } - prev = e - } - } - } -} - -// lookup returns the value associated with key k, if present, or -// value(nil) otherwise. -func (m *hashmap) lookup(k hashable) value { - if m != nil { - hash := k.hash(m.keyType) - for e := m.table[hash]; e != nil; e = e.next { - if k.eq(m.keyType, e.key) { - return e.value - } - } - } - return nil -} - -// insert updates the map to associate key k with value v. If there -// was already an association for an eq() (though not necessarily ==) -// k, the previous key remains in the map and its associated value is -// updated. -func (m *hashmap) insert(k hashable, v value) { - hash := k.hash(m.keyType) - head := m.table[hash] - for e := head; e != nil; e = e.next { - if k.eq(m.keyType, e.key) { - e.value = v - return - } - } - m.table[hash] = &entry{ - key: k, - value: v, - next: head, - } - m.length++ -} - -// len returns the number of key/value associations in the map. -func (m *hashmap) len() int { - if m != nil { - return m.length - } - return 0 -} diff --git a/go/ssa/interp/ops14.go b/go/ssa/interp/ops14.go deleted file mode 100644 index 2490dff9c1..0000000000 --- a/go/ssa/interp/ops14.go +++ /dev/null @@ -1,1396 +0,0 @@ -// 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. - -// +build !go1.5 - -package interp - -import ( - "bytes" - "fmt" - "go/token" - "strings" - "sync" - "unsafe" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -// If the target program panics, the interpreter panics with this type. -type targetPanic struct { - v value -} - -func (p targetPanic) String() string { - return toString(p.v) -} - -// If the target program calls exit, the interpreter panics with this type. -type exitPanic int - -// constValue returns the value of the constant with the -// dynamic type tag appropriate for c.Type(). -func constValue(c *ssa.Const) value { - if c.IsNil() { - return zero(c.Type()) // typed nil - } - - if t, ok := c.Type().Underlying().(*types.Basic); ok { - // TODO(adonovan): eliminate untyped constants from SSA form. - switch t.Kind() { - case types.Bool, types.UntypedBool: - return exact.BoolVal(c.Value) - case types.Int, types.UntypedInt: - // Assume sizeof(int) is same on host and target. - return int(c.Int64()) - case types.Int8: - return int8(c.Int64()) - case types.Int16: - return int16(c.Int64()) - case types.Int32, types.UntypedRune: - return int32(c.Int64()) - case types.Int64: - return c.Int64() - case types.Uint: - // Assume sizeof(uint) is same on host and target. - return uint(c.Uint64()) - case types.Uint8: - return uint8(c.Uint64()) - case types.Uint16: - return uint16(c.Uint64()) - case types.Uint32: - return uint32(c.Uint64()) - case types.Uint64: - return c.Uint64() - case types.Uintptr: - // Assume sizeof(uintptr) is same on host and target. - return uintptr(c.Uint64()) - case types.Float32: - return float32(c.Float64()) - case types.Float64, types.UntypedFloat: - return c.Float64() - case types.Complex64: - return complex64(c.Complex128()) - case types.Complex128, types.UntypedComplex: - return c.Complex128() - case types.String, types.UntypedString: - if c.Value.Kind() == exact.String { - return exact.StringVal(c.Value) - } - return string(rune(c.Int64())) - } - } - - panic(fmt.Sprintf("constValue: %s", c)) -} - -// asInt converts x, which must be an integer, to an int suitable for -// use as a slice or array index or operand to make(). -func asInt(x value) int { - switch x := x.(type) { - case int: - return x - case int8: - return int(x) - case int16: - return int(x) - case int32: - return int(x) - case int64: - return int(x) - case uint: - return int(x) - case uint8: - return int(x) - case uint16: - return int(x) - case uint32: - return int(x) - case uint64: - return int(x) - case uintptr: - return int(x) - } - panic(fmt.Sprintf("cannot convert %T to int", x)) -} - -// asUint64 converts x, which must be an unsigned integer, to a uint64 -// suitable for use as a bitwise shift count. -func asUint64(x value) uint64 { - switch x := x.(type) { - case uint: - return uint64(x) - case uint8: - return uint64(x) - case uint16: - return uint64(x) - case uint32: - return uint64(x) - case uint64: - return x - case uintptr: - return uint64(x) - } - panic(fmt.Sprintf("cannot convert %T to uint64", x)) -} - -// zero returns a new "zero" value of the specified type. -func zero(t types.Type) value { - switch t := t.(type) { - case *types.Basic: - if t.Kind() == types.UntypedNil { - panic("untyped nil has no zero value") - } - if t.Info()&types.IsUntyped != 0 { - // TODO(adonovan): make it an invariant that - // this is unreachable. Currently some - // constants have 'untyped' types when they - // should be defaulted by the typechecker. - t = ssa.DefaultType(t).(*types.Basic) - } - switch t.Kind() { - case types.Bool: - return false - case types.Int: - return int(0) - case types.Int8: - return int8(0) - case types.Int16: - return int16(0) - case types.Int32: - return int32(0) - case types.Int64: - return int64(0) - case types.Uint: - return uint(0) - case types.Uint8: - return uint8(0) - case types.Uint16: - return uint16(0) - case types.Uint32: - return uint32(0) - case types.Uint64: - return uint64(0) - case types.Uintptr: - return uintptr(0) - case types.Float32: - return float32(0) - case types.Float64: - return float64(0) - case types.Complex64: - return complex64(0) - case types.Complex128: - return complex128(0) - case types.String: - return "" - case types.UnsafePointer: - return unsafe.Pointer(nil) - default: - panic(fmt.Sprint("zero for unexpected type:", t)) - } - case *types.Pointer: - return (*value)(nil) - case *types.Array: - a := make(array, t.Len()) - for i := range a { - a[i] = zero(t.Elem()) - } - return a - 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, t.NumFields()) - for i := range s { - s[i] = zero(t.Field(i).Type()) - } - return s - case *types.Tuple: - if t.Len() == 1 { - return zero(t.At(0).Type()) - } - s := make(tuple, t.Len()) - for i := range s { - s[i] = zero(t.At(i).Type()) - } - return s - case *types.Chan: - return chan value(nil) - case *types.Map: - if usesBuiltinMap(t.Key()) { - return map[value]value(nil) - } - return (*hashmap)(nil) - case *types.Signature: - return (*ssa.Function)(nil) - } - panic(fmt.Sprint("zero: unexpected ", t)) -} - -// slice returns x[lo:hi:max]. Any of lo, hi and max may be nil. -func slice(x, lo, hi, max value) value { - var Len, Cap int - switch x := x.(type) { - case string: - Len = len(x) - case []value: - Len = len(x) - Cap = cap(x) - case *value: // *array - a := (*x).(array) - Len = len(a) - Cap = cap(a) - } - - l := 0 - if lo != nil { - l = asInt(lo) - } - - h := Len - if hi != nil { - h = asInt(hi) - } - - m := Cap - if max != nil { - m = asInt(max) - } - - switch x := x.(type) { - case string: - return x[l:h] - case []value: - return x[l:h:m] - case *value: // *array - a := (*x).(array) - return []value(a)[l:h:m] - } - panic(fmt.Sprintf("slice: unexpected X type: %T", x)) -} - -// lookup returns x[idx] where x is a map or string. -func lookup(instr *ssa.Lookup, x, idx value) value { - switch x := x.(type) { // map or string - case map[value]value, *hashmap: - var v value - var ok bool - switch x := x.(type) { - case map[value]value: - v, ok = x[idx] - case *hashmap: - v = x.lookup(idx.(hashable)) - ok = v != nil - } - if !ok { - v = zero(instr.X.Type().Underlying().(*types.Map).Elem()) - } - if instr.CommaOk { - v = tuple{v, ok} - } - return v - case string: - return x[asInt(idx)] - } - panic(fmt.Sprintf("unexpected x type in Lookup: %T", x)) -} - -// binop implements all arithmetic and logical binary operators for -// numeric datatypes and strings. Both operands must have identical -// dynamic type. -// -func binop(op token.Token, t types.Type, x, y value) value { - switch op { - case token.ADD: - switch x.(type) { - case int: - return x.(int) + y.(int) - case int8: - return x.(int8) + y.(int8) - case int16: - return x.(int16) + y.(int16) - case int32: - return x.(int32) + y.(int32) - case int64: - return x.(int64) + y.(int64) - case uint: - return x.(uint) + y.(uint) - case uint8: - return x.(uint8) + y.(uint8) - case uint16: - return x.(uint16) + y.(uint16) - case uint32: - return x.(uint32) + y.(uint32) - case uint64: - return x.(uint64) + y.(uint64) - case uintptr: - return x.(uintptr) + y.(uintptr) - case float32: - return x.(float32) + y.(float32) - case float64: - return x.(float64) + y.(float64) - case complex64: - return x.(complex64) + y.(complex64) - case complex128: - return x.(complex128) + y.(complex128) - case string: - return x.(string) + y.(string) - } - - case token.SUB: - switch x.(type) { - case int: - return x.(int) - y.(int) - case int8: - return x.(int8) - y.(int8) - case int16: - return x.(int16) - y.(int16) - case int32: - return x.(int32) - y.(int32) - case int64: - return x.(int64) - y.(int64) - case uint: - return x.(uint) - y.(uint) - case uint8: - return x.(uint8) - y.(uint8) - case uint16: - return x.(uint16) - y.(uint16) - case uint32: - return x.(uint32) - y.(uint32) - case uint64: - return x.(uint64) - y.(uint64) - case uintptr: - return x.(uintptr) - y.(uintptr) - case float32: - return x.(float32) - y.(float32) - case float64: - return x.(float64) - y.(float64) - case complex64: - return x.(complex64) - y.(complex64) - case complex128: - return x.(complex128) - y.(complex128) - } - - case token.MUL: - switch x.(type) { - case int: - return x.(int) * y.(int) - case int8: - return x.(int8) * y.(int8) - case int16: - return x.(int16) * y.(int16) - case int32: - return x.(int32) * y.(int32) - case int64: - return x.(int64) * y.(int64) - case uint: - return x.(uint) * y.(uint) - case uint8: - return x.(uint8) * y.(uint8) - case uint16: - return x.(uint16) * y.(uint16) - case uint32: - return x.(uint32) * y.(uint32) - case uint64: - return x.(uint64) * y.(uint64) - case uintptr: - return x.(uintptr) * y.(uintptr) - case float32: - return x.(float32) * y.(float32) - case float64: - return x.(float64) * y.(float64) - case complex64: - return x.(complex64) * y.(complex64) - case complex128: - return x.(complex128) * y.(complex128) - } - - case token.QUO: - switch x.(type) { - case int: - return x.(int) / y.(int) - case int8: - return x.(int8) / y.(int8) - case int16: - return x.(int16) / y.(int16) - case int32: - return x.(int32) / y.(int32) - case int64: - return x.(int64) / y.(int64) - case uint: - return x.(uint) / y.(uint) - case uint8: - return x.(uint8) / y.(uint8) - case uint16: - return x.(uint16) / y.(uint16) - case uint32: - return x.(uint32) / y.(uint32) - case uint64: - return x.(uint64) / y.(uint64) - case uintptr: - return x.(uintptr) / y.(uintptr) - case float32: - return x.(float32) / y.(float32) - case float64: - return x.(float64) / y.(float64) - case complex64: - return x.(complex64) / y.(complex64) - case complex128: - return x.(complex128) / y.(complex128) - } - - case token.REM: - switch x.(type) { - case int: - return x.(int) % y.(int) - case int8: - return x.(int8) % y.(int8) - case int16: - return x.(int16) % y.(int16) - case int32: - return x.(int32) % y.(int32) - case int64: - return x.(int64) % y.(int64) - case uint: - return x.(uint) % y.(uint) - case uint8: - return x.(uint8) % y.(uint8) - case uint16: - return x.(uint16) % y.(uint16) - case uint32: - return x.(uint32) % y.(uint32) - case uint64: - return x.(uint64) % y.(uint64) - case uintptr: - return x.(uintptr) % y.(uintptr) - } - - case token.AND: - switch x.(type) { - case int: - return x.(int) & y.(int) - case int8: - return x.(int8) & y.(int8) - case int16: - return x.(int16) & y.(int16) - case int32: - return x.(int32) & y.(int32) - case int64: - return x.(int64) & y.(int64) - case uint: - return x.(uint) & y.(uint) - case uint8: - return x.(uint8) & y.(uint8) - case uint16: - return x.(uint16) & y.(uint16) - case uint32: - return x.(uint32) & y.(uint32) - case uint64: - return x.(uint64) & y.(uint64) - case uintptr: - return x.(uintptr) & y.(uintptr) - } - - case token.OR: - switch x.(type) { - case int: - return x.(int) | y.(int) - case int8: - return x.(int8) | y.(int8) - case int16: - return x.(int16) | y.(int16) - case int32: - return x.(int32) | y.(int32) - case int64: - return x.(int64) | y.(int64) - case uint: - return x.(uint) | y.(uint) - case uint8: - return x.(uint8) | y.(uint8) - case uint16: - return x.(uint16) | y.(uint16) - case uint32: - return x.(uint32) | y.(uint32) - case uint64: - return x.(uint64) | y.(uint64) - case uintptr: - return x.(uintptr) | y.(uintptr) - } - - case token.XOR: - switch x.(type) { - case int: - return x.(int) ^ y.(int) - case int8: - return x.(int8) ^ y.(int8) - case int16: - return x.(int16) ^ y.(int16) - case int32: - return x.(int32) ^ y.(int32) - case int64: - return x.(int64) ^ y.(int64) - case uint: - return x.(uint) ^ y.(uint) - case uint8: - return x.(uint8) ^ y.(uint8) - case uint16: - return x.(uint16) ^ y.(uint16) - case uint32: - return x.(uint32) ^ y.(uint32) - case uint64: - return x.(uint64) ^ y.(uint64) - case uintptr: - return x.(uintptr) ^ y.(uintptr) - } - - case token.AND_NOT: - switch x.(type) { - case int: - return x.(int) &^ y.(int) - case int8: - return x.(int8) &^ y.(int8) - case int16: - return x.(int16) &^ y.(int16) - case int32: - return x.(int32) &^ y.(int32) - case int64: - return x.(int64) &^ y.(int64) - case uint: - return x.(uint) &^ y.(uint) - case uint8: - return x.(uint8) &^ y.(uint8) - case uint16: - return x.(uint16) &^ y.(uint16) - case uint32: - return x.(uint32) &^ y.(uint32) - case uint64: - return x.(uint64) &^ y.(uint64) - case uintptr: - return x.(uintptr) &^ y.(uintptr) - } - - case token.SHL: - y := asUint64(y) - switch x.(type) { - case int: - return x.(int) << y - case int8: - return x.(int8) << y - case int16: - return x.(int16) << y - case int32: - return x.(int32) << y - case int64: - return x.(int64) << y - case uint: - return x.(uint) << y - case uint8: - return x.(uint8) << y - case uint16: - return x.(uint16) << y - case uint32: - return x.(uint32) << y - case uint64: - return x.(uint64) << y - case uintptr: - return x.(uintptr) << y - } - - case token.SHR: - y := asUint64(y) - switch x.(type) { - case int: - return x.(int) >> y - case int8: - return x.(int8) >> y - case int16: - return x.(int16) >> y - case int32: - return x.(int32) >> y - case int64: - return x.(int64) >> y - case uint: - return x.(uint) >> y - case uint8: - return x.(uint8) >> y - case uint16: - return x.(uint16) >> y - case uint32: - return x.(uint32) >> y - case uint64: - return x.(uint64) >> y - case uintptr: - return x.(uintptr) >> y - } - - case token.LSS: - switch x.(type) { - case int: - return x.(int) < y.(int) - case int8: - return x.(int8) < y.(int8) - case int16: - return x.(int16) < y.(int16) - case int32: - return x.(int32) < y.(int32) - case int64: - return x.(int64) < y.(int64) - case uint: - return x.(uint) < y.(uint) - case uint8: - return x.(uint8) < y.(uint8) - case uint16: - return x.(uint16) < y.(uint16) - case uint32: - return x.(uint32) < y.(uint32) - case uint64: - return x.(uint64) < y.(uint64) - case uintptr: - return x.(uintptr) < y.(uintptr) - case float32: - return x.(float32) < y.(float32) - case float64: - return x.(float64) < y.(float64) - case string: - return x.(string) < y.(string) - } - - case token.LEQ: - switch x.(type) { - case int: - return x.(int) <= y.(int) - case int8: - return x.(int8) <= y.(int8) - case int16: - return x.(int16) <= y.(int16) - case int32: - return x.(int32) <= y.(int32) - case int64: - return x.(int64) <= y.(int64) - case uint: - return x.(uint) <= y.(uint) - case uint8: - return x.(uint8) <= y.(uint8) - case uint16: - return x.(uint16) <= y.(uint16) - case uint32: - return x.(uint32) <= y.(uint32) - case uint64: - return x.(uint64) <= y.(uint64) - case uintptr: - return x.(uintptr) <= y.(uintptr) - case float32: - return x.(float32) <= y.(float32) - case float64: - return x.(float64) <= y.(float64) - case string: - return x.(string) <= y.(string) - } - - case token.EQL: - return eqnil(t, x, y) - - case token.NEQ: - return !eqnil(t, x, y) - - case token.GTR: - switch x.(type) { - case int: - return x.(int) > y.(int) - case int8: - return x.(int8) > y.(int8) - case int16: - return x.(int16) > y.(int16) - case int32: - return x.(int32) > y.(int32) - case int64: - return x.(int64) > y.(int64) - case uint: - return x.(uint) > y.(uint) - case uint8: - return x.(uint8) > y.(uint8) - case uint16: - return x.(uint16) > y.(uint16) - case uint32: - return x.(uint32) > y.(uint32) - case uint64: - return x.(uint64) > y.(uint64) - case uintptr: - return x.(uintptr) > y.(uintptr) - case float32: - return x.(float32) > y.(float32) - case float64: - return x.(float64) > y.(float64) - case string: - return x.(string) > y.(string) - } - - case token.GEQ: - switch x.(type) { - case int: - return x.(int) >= y.(int) - case int8: - return x.(int8) >= y.(int8) - case int16: - return x.(int16) >= y.(int16) - case int32: - return x.(int32) >= y.(int32) - case int64: - return x.(int64) >= y.(int64) - case uint: - return x.(uint) >= y.(uint) - case uint8: - return x.(uint8) >= y.(uint8) - case uint16: - return x.(uint16) >= y.(uint16) - case uint32: - return x.(uint32) >= y.(uint32) - case uint64: - return x.(uint64) >= y.(uint64) - case uintptr: - return x.(uintptr) >= y.(uintptr) - case float32: - return x.(float32) >= y.(float32) - case float64: - return x.(float64) >= y.(float64) - case string: - return x.(string) >= y.(string) - } - } - panic(fmt.Sprintf("invalid binary op: %T %s %T", x, op, y)) -} - -// eqnil returns the comparison x == y using the equivalence relation -// appropriate for type t. -// If t is a reference type, at most one of x or y may be a nil value -// of that type. -// -func eqnil(t types.Type, x, y value) bool { - switch t.Underlying().(type) { - case *types.Map, *types.Signature, *types.Slice: - // Since these types don't support comparison, - // one of the operands must be a literal nil. - switch x := x.(type) { - case *hashmap: - return (x != nil) == (y.(*hashmap) != nil) - case map[value]value: - return (x != nil) == (y.(map[value]value) != nil) - case *ssa.Function: - switch y := y.(type) { - case *ssa.Function: - return (x != nil) == (y != nil) - case *closure: - return true - } - case *closure: - return (x != nil) == (y.(*ssa.Function) != nil) - case []value: - return (x != nil) == (y.([]value) != nil) - } - panic(fmt.Sprintf("eqnil(%s): illegal dynamic type: %T", t, x)) - } - - return equals(t, x, y) -} - -func unop(instr *ssa.UnOp, x value) value { - switch instr.Op { - case token.ARROW: // receive - v, ok := <-x.(chan value) - if !ok { - v = zero(instr.X.Type().Underlying().(*types.Chan).Elem()) - } - if instr.CommaOk { - v = tuple{v, ok} - } - return v - case token.SUB: - switch x := x.(type) { - case int: - return -x - case int8: - return -x - case int16: - return -x - case int32: - return -x - case int64: - return -x - case uint: - return -x - case uint8: - return -x - case uint16: - return -x - case uint32: - return -x - case uint64: - return -x - case uintptr: - return -x - case float32: - return -x - case float64: - return -x - case complex64: - return -x - case complex128: - return -x - } - case token.MUL: - return load(deref(instr.X.Type()), x.(*value)) - case token.NOT: - return !x.(bool) - case token.XOR: - switch x := x.(type) { - case int: - return ^x - case int8: - return ^x - case int16: - return ^x - case int32: - return ^x - case int64: - return ^x - case uint: - return ^x - case uint8: - return ^x - case uint16: - return ^x - case uint32: - return ^x - case uint64: - return ^x - case uintptr: - return ^x - } - } - panic(fmt.Sprintf("invalid unary op %s %T", instr.Op, x)) -} - -// typeAssert checks whether dynamic type of itf is instr.AssertedType. -// It returns the extracted value on success, and panics on failure, -// unless instr.CommaOk, in which case it always returns a "value,ok" tuple. -// -func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value { - var v value - err := "" - if itf.t == nil { - err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType) - - } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok { - v = itf - err = checkInterface(i, idst, itf) - - } else if types.Identical(itf.t, instr.AssertedType) { - v = itf.v // extract value - - } else { - err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType) - } - - if err != "" { - if !instr.CommaOk { - panic(err) - } - return tuple{zero(instr.AssertedType), false} - } - if instr.CommaOk { - return tuple{v, true} - } - return v -} - -// If CapturedOutput is non-nil, all writes by the interpreted program -// to file descriptors 1 and 2 will also be written to CapturedOutput. -// -// (The $GOROOT/test system requires that the test be considered a -// failure if "BUG" appears in the combined stdout/stderr output, even -// if it exits zero. This is a global variable shared by all -// interpreters in the same process.) -// -var CapturedOutput *bytes.Buffer -var capturedOutputMu sync.Mutex - -// write writes bytes b to the target program's file descriptor fd. -// The print/println built-ins and the write() system call funnel -// through here so they can be captured by the test driver. -func write(fd int, b []byte) (int, error) { - // TODO(adonovan): fix: on Windows, std{out,err} are not 1, 2. - if CapturedOutput != nil && (fd == 1 || fd == 2) { - capturedOutputMu.Lock() - CapturedOutput.Write(b) // ignore errors - capturedOutputMu.Unlock() - } - return syswrite(fd, b) -} - -// callBuiltin interprets a call to builtin fn with arguments args, -// returning its result. -func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value { - switch fn.Name() { - case "append": - if len(args) == 1 { - return args[0] - } - if s, ok := args[1].(string); ok { - // append([]byte, ...string) []byte - arg0 := args[0].([]value) - for i := 0; i < len(s); i++ { - arg0 = append(arg0, s[i]) - } - return arg0 - } - // append([]T, ...[]T) []T - return append(args[0].([]value), args[1].([]value)...) - - case "copy": // copy([]T, []T) int or copy([]byte, string) int - src := args[1] - if _, ok := src.(string); ok { - params := fn.Type().(*types.Signature).Params() - src = conv(params.At(0).Type(), params.At(1).Type(), src) - } - return copy(args[0].([]value), src.([]value)) - - case "close": // close(chan T) - close(args[0].(chan value)) - return nil - - case "delete": // delete(map[K]value, K) - switch m := args[0].(type) { - case map[value]value: - delete(m, args[1]) - case *hashmap: - m.delete(args[1].(hashable)) - default: - panic(fmt.Sprintf("illegal map type: %T", m)) - } - return nil - - case "print", "println": // print(any, ...) - ln := fn.Name() == "println" - var buf bytes.Buffer - for i, arg := range args { - if i > 0 && ln { - buf.WriteRune(' ') - } - buf.WriteString(toString(arg)) - } - if ln { - buf.WriteRune('\n') - } - write(1, buf.Bytes()) - return nil - - case "len": - switch x := args[0].(type) { - case string: - return len(x) - case array: - return len(x) - case *value: - return len((*x).(array)) - case []value: - return len(x) - case map[value]value: - return len(x) - case *hashmap: - return x.len() - case chan value: - return len(x) - default: - panic(fmt.Sprintf("len: illegal operand: %T", x)) - } - - case "cap": - switch x := args[0].(type) { - case array: - return cap(x) - case *value: - return cap((*x).(array)) - case []value: - return cap(x) - case chan value: - return cap(x) - default: - panic(fmt.Sprintf("cap: illegal operand: %T", x)) - } - - case "real": - switch c := args[0].(type) { - case complex64: - return real(c) - case complex128: - return real(c) - default: - panic(fmt.Sprintf("real: illegal operand: %T", c)) - } - - case "imag": - switch c := args[0].(type) { - case complex64: - return imag(c) - case complex128: - return imag(c) - default: - panic(fmt.Sprintf("imag: illegal operand: %T", c)) - } - - case "complex": - switch f := args[0].(type) { - case float32: - return complex(f, args[1].(float32)) - case float64: - return complex(f, args[1].(float64)) - default: - panic(fmt.Sprintf("complex: illegal operand: %T", f)) - } - - case "panic": - // ssa.Panic handles most cases; this is only for "go - // panic" or "defer panic". - panic(targetPanic{args[0]}) - - case "recover": - return doRecover(caller) - - case "ssa:wrapnilchk": - recv := args[0] - if recv.(*value) == nil { - recvType := args[1] - methodName := args[2] - panic(fmt.Sprintf("value method (%s).%s called using nil *%s pointer", - recvType, methodName, recvType)) - } - return recv - } - - panic("unknown built-in: " + fn.Name()) -} - -func rangeIter(x value, t types.Type) iter { - switch x := x.(type) { - case map[value]value: - // TODO(adonovan): fix: leaks goroutines and channels - // on each incomplete map iteration. We need to open - // up an iteration interface using the - // reflect.(Value).MapKeys machinery. - it := make(mapIter) - go func() { - for k, v := range x { - it <- [2]value{k, v} - } - close(it) - }() - return it - case *hashmap: - // TODO(adonovan): fix: leaks goroutines and channels - // on each incomplete map iteration. We need to open - // up an iteration interface using the - // reflect.(Value).MapKeys machinery. - it := make(mapIter) - go func() { - for _, e := range x.table { - for e != nil { - it <- [2]value{e.key, e.value} - e = e.next - } - } - close(it) - }() - return it - case string: - return &stringIter{Reader: strings.NewReader(x)} - } - panic(fmt.Sprintf("cannot range over %T", x)) -} - -// widen widens a basic typed value x to the widest type of its -// category, one of: -// bool, int64, uint64, float64, complex128, string. -// This is inefficient but reduces the size of the cross-product of -// cases we have to consider. -// -func widen(x value) value { - switch y := x.(type) { - case bool, int64, uint64, float64, complex128, string, unsafe.Pointer: - return x - case int: - return int64(y) - case int8: - return int64(y) - case int16: - return int64(y) - case int32: - return int64(y) - case uint: - return uint64(y) - case uint8: - return uint64(y) - case uint16: - return uint64(y) - case uint32: - return uint64(y) - case uintptr: - return uint64(y) - case float32: - return float64(y) - case complex64: - return complex128(y) - } - panic(fmt.Sprintf("cannot widen %T", x)) -} - -// conv converts the value x of type t_src to type t_dst and returns -// the result. -// Possible cases are described with the ssa.Convert operator. -// -func conv(t_dst, t_src types.Type, x value) value { - 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("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: Convert should be ChangeInterface") - } else { - panic("oops: Convert should be MakeInterface") - } - } - - // Remaining conversions: - // + untyped string/number/bool constant to a specific - // representation. - // + conversions between non-complex numeric types. - // + conversions between complex numeric types. - // + integer/[]byte/[]rune -> string. - // + string -> []byte/[]rune. - // - // All are treated the same: first we extract the value to the - // widest representation (int64, uint64, float64, complex128, - // or string), then we convert it to the desired type. - - switch ut_src := ut_src.(type) { - case *types.Pointer: - switch ut_dst := ut_dst.(type) { - case *types.Basic: - // *value to unsafe.Pointer? - if ut_dst.Kind() == types.UnsafePointer { - return unsafe.Pointer(x.(*value)) - } - } - - case *types.Slice: - // []byte or []rune -> string - // TODO(adonovan): fix: type B byte; conv([]B -> string). - switch ut_src.Elem().(*types.Basic).Kind() { - case types.Byte: - x := x.([]value) - b := make([]byte, 0, len(x)) - for i := range x { - b = append(b, x[i].(byte)) - } - return string(b) - - case types.Rune: - x := x.([]value) - r := make([]rune, 0, len(x)) - for i := range x { - r = append(r, x[i].(rune)) - } - return string(r) - } - - case *types.Basic: - x = widen(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 { - return string(asInt(x)) - } - } - - // string -> []rune, []byte or string? - if s, ok := x.(string); ok { - switch ut_dst := ut_dst.(type) { - case *types.Slice: - var res []value - // TODO(adonovan): fix: test named alias of rune, byte. - switch ut_dst.Elem().(*types.Basic).Kind() { - case types.Rune: - for _, r := range []rune(s) { - res = append(res, r) - } - return res - case types.Byte: - for _, b := range []byte(s) { - res = append(res, b) - } - return res - } - case *types.Basic: - if ut_dst.Kind() == types.String { - return x.(string) - } - } - break // fail: no other conversions for string - } - - // unsafe.Pointer -> *value - if ut_src.Kind() == types.UnsafePointer { - // TODO(adonovan): this is wrong and cannot - // really be fixed with the current design. - // - // return (*value)(x.(unsafe.Pointer)) - // creates a new pointer of a different - // type but the underlying interface value - // knows its "true" type and so cannot be - // meaningfully used through the new pointer. - // - // To make this work, the interpreter needs to - // simulate the memory layout of a real - // compiled implementation. - // - // To at least preserve type-safety, we'll - // just return the zero value of the - // destination type. - return zero(t_dst) - } - - // Conversions between complex numeric types? - if ut_src.Info()&types.IsComplex != 0 { - switch ut_dst.(*types.Basic).Kind() { - case types.Complex64: - return complex64(x.(complex128)) - case types.Complex128: - return x.(complex128) - } - break // fail: no other conversions for complex - } - - // Conversions between non-complex numeric types? - if ut_src.Info()&types.IsNumeric != 0 { - kind := ut_dst.(*types.Basic).Kind() - switch x := x.(type) { - case int64: // signed integer -> numeric? - switch kind { - case types.Int: - return int(x) - case types.Int8: - return int8(x) - case types.Int16: - return int16(x) - case types.Int32: - return int32(x) - case types.Int64: - return int64(x) - case types.Uint: - return uint(x) - case types.Uint8: - return uint8(x) - case types.Uint16: - return uint16(x) - case types.Uint32: - return uint32(x) - case types.Uint64: - return uint64(x) - case types.Uintptr: - return uintptr(x) - case types.Float32: - return float32(x) - case types.Float64: - return float64(x) - } - - case uint64: // unsigned integer -> numeric? - switch kind { - case types.Int: - return int(x) - case types.Int8: - return int8(x) - case types.Int16: - return int16(x) - case types.Int32: - return int32(x) - case types.Int64: - return int64(x) - case types.Uint: - return uint(x) - case types.Uint8: - return uint8(x) - case types.Uint16: - return uint16(x) - case types.Uint32: - return uint32(x) - case types.Uint64: - return uint64(x) - case types.Uintptr: - return uintptr(x) - case types.Float32: - return float32(x) - case types.Float64: - return float64(x) - } - - case float64: // floating point -> numeric? - switch kind { - case types.Int: - return int(x) - case types.Int8: - return int8(x) - case types.Int16: - return int16(x) - case types.Int32: - return int32(x) - case types.Int64: - return int64(x) - case types.Uint: - return uint(x) - case types.Uint8: - return uint8(x) - case types.Uint16: - return uint16(x) - case types.Uint32: - return uint32(x) - case types.Uint64: - return uint64(x) - case types.Uintptr: - return uintptr(x) - case types.Float32: - return float32(x) - case types.Float64: - return float64(x) - } - } - } - } - - panic(fmt.Sprintf("unsupported conversion: %s -> %s, dynamic type %T", t_src, t_dst, x)) -} - -// checkInterface checks that the method set of x implements the -// interface itype. -// On success it returns "", on failure, an error message. -// -func checkInterface(i *interpreter, itype *types.Interface, x iface) string { - if meth, _ := types.MissingMethod(x.t, itype, true); meth != nil { - return fmt.Sprintf("interface conversion: %v is not %v: missing method %s", - x.t, itype, meth.Name()) - } - return "" // ok -} diff --git a/go/ssa/interp/reflect14.go b/go/ssa/interp/reflect14.go deleted file mode 100644 index 9f42327d49..0000000000 --- a/go/ssa/interp/reflect14.go +++ /dev/null @@ -1,576 +0,0 @@ -// 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. - -// +build !go1.5 - -package interp - -// Emulated "reflect" package. -// -// We completely replace the built-in "reflect" package. -// The only thing clients can depend upon are that reflect.Type is an -// interface and reflect.Value is an (opaque) struct. - -import ( - "fmt" - "go/token" - "reflect" - "unsafe" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" -) - -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. -// -// type 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 -// interface that it creates. -// We put it in the "reflect" package for expedience. -// -// type error string -var errorType = makeNamedType("error", &opaqueType{nil, "error"}) - -func makeNamedType(name string, underlying types.Type) *types.Named { - obj := types.NewTypeName(token.NoPos, reflectTypesPackage, name, nil) - return types.NewNamed(obj, underlying, nil) -} - -func makeReflectValue(t types.Type, v value) value { - return structure{rtype{t}, v} -} - -// Given a reflect.Value, returns its rtype. -func rV2T(v value) rtype { - return v.(structure)[0].(rtype) -} - -// Given a reflect.Value, returns the underlying interpreter value. -func rV2V(v value) value { - return v.(structure)[1] -} - -// makeReflectType boxes up an rtype in a reflect.Type interface. -func makeReflectType(rt rtype) value { - return iface{rtypeType, rt} -} - -func ext۰reflect۰Init(fr *frame, args []value) value { - // Signature: func() - return nil -} - -func ext۰reflect۰rtype۰Bits(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) int - rt := args[0].(rtype).t - basic, ok := rt.Underlying().(*types.Basic) - if !ok { - panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt)) - } - return int(fr.i.sizes.Sizeof(basic)) * 8 -} - -func ext۰reflect۰rtype۰Elem(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) reflect.Type - return makeReflectType(rtype{args[0].(rtype).t.Underlying().(interface { - Elem() types.Type - }).Elem()}) -} - -func ext۰reflect۰rtype۰Field(fr *frame, args []value) value { - // Signature: func (t reflect.rtype, i int) reflect.StructField - st := args[0].(rtype).t.Underlying().(*types.Struct) - i := args[1].(int) - f := st.Field(i) - return structure{ - f.Name(), - f.Pkg().Path(), - makeReflectType(rtype{f.Type()}), - st.Tag(i), - 0, // TODO(adonovan): offset - []value{}, // TODO(adonovan): indices - f.Anonymous(), - } -} - -func ext۰reflect۰rtype۰In(fr *frame, args []value) value { - // Signature: func (t reflect.rtype, i int) int - i := args[1].(int) - return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Params().At(i).Type()}) -} - -func ext۰reflect۰rtype۰Kind(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) uint - return uint(reflectKind(args[0].(rtype).t)) -} - -func ext۰reflect۰rtype۰NumField(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) int - return args[0].(rtype).t.Underlying().(*types.Struct).NumFields() -} - -func ext۰reflect۰rtype۰NumIn(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) int - return args[0].(rtype).t.(*types.Signature).Params().Len() -} - -func ext۰reflect۰rtype۰NumMethod(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) int - return fr.i.prog.MethodSets.MethodSet(args[0].(rtype).t).Len() -} - -func ext۰reflect۰rtype۰NumOut(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) int - return args[0].(rtype).t.(*types.Signature).Results().Len() -} - -func ext۰reflect۰rtype۰Out(fr *frame, 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().At(i).Type()}) -} - -func ext۰reflect۰rtype۰Size(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) uintptr - return uintptr(fr.i.sizes.Sizeof(args[0].(rtype).t)) -} - -func ext۰reflect۰rtype۰String(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) string - return args[0].(rtype).t.String() -} - -func ext۰reflect۰New(fr *frame, args []value) value { - // Signature: func (t reflect.Type) reflect.Value - t := args[0].(iface).v.(rtype).t - alloc := zero(t) - return makeReflectValue(types.NewPointer(t), &alloc) -} - -func ext۰reflect۰SliceOf(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) Type - return makeReflectType(rtype{types.NewSlice(args[0].(iface).v.(rtype).t)}) -} - -func ext۰reflect۰TypeOf(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) Type - return makeReflectType(rtype{args[0].(iface).t}) -} - -func ext۰reflect۰ValueOf(fr *frame, args []value) value { - // Signature: func (interface{}) reflect.Value - itf := args[0].(iface) - return makeReflectValue(itf.t, itf.v) -} - -func ext۰reflect۰Zero(fr *frame, args []value) value { - // Signature: func (t reflect.Type) reflect.Value - t := args[0].(iface).v.(rtype).t - return makeReflectValue(t, zero(t)) -} - -func reflectKind(t types.Type) reflect.Kind { - switch t := t.(type) { - case *types.Named: - return reflectKind(t.Underlying()) - case *types.Basic: - switch t.Kind() { - case types.Bool: - return reflect.Bool - case types.Int: - return reflect.Int - case types.Int8: - return reflect.Int8 - case types.Int16: - return reflect.Int16 - case types.Int32: - return reflect.Int32 - case types.Int64: - return reflect.Int64 - case types.Uint: - return reflect.Uint - case types.Uint8: - return reflect.Uint8 - case types.Uint16: - return reflect.Uint16 - case types.Uint32: - return reflect.Uint32 - case types.Uint64: - return reflect.Uint64 - case types.Uintptr: - return reflect.Uintptr - case types.Float32: - return reflect.Float32 - case types.Float64: - return reflect.Float64 - case types.Complex64: - return reflect.Complex64 - case types.Complex128: - return reflect.Complex128 - case types.String: - return reflect.String - case types.UnsafePointer: - return reflect.UnsafePointer - } - case *types.Array: - return reflect.Array - case *types.Chan: - return reflect.Chan - case *types.Signature: - return reflect.Func - case *types.Interface: - return reflect.Interface - case *types.Map: - return reflect.Map - case *types.Pointer: - return reflect.Ptr - case *types.Slice: - return reflect.Slice - case *types.Struct: - return reflect.Struct - } - panic(fmt.Sprint("unexpected type: ", t)) -} - -func ext۰reflect۰Value۰Kind(fr *frame, args []value) value { - // Signature: func (reflect.Value) uint - return uint(reflectKind(rV2T(args[0]).t)) -} - -func ext۰reflect۰Value۰String(fr *frame, args []value) value { - // Signature: func (reflect.Value) string - return toString(rV2V(args[0])) -} - -func ext۰reflect۰Value۰Type(fr *frame, args []value) value { - // Signature: func (reflect.Value) reflect.Type - return makeReflectType(rV2T(args[0])) -} - -func ext۰reflect۰Value۰Uint(fr *frame, args []value) value { - // Signature: func (reflect.Value) uint64 - switch v := rV2V(args[0]).(type) { - case uint: - return uint64(v) - case uint8: - return uint64(v) - case uint16: - return uint64(v) - case uint32: - return uint64(v) - case uint64: - return uint64(v) - case uintptr: - return uint64(v) - } - panic("reflect.Value.Uint") -} - -func ext۰reflect۰Value۰Len(fr *frame, args []value) value { - // Signature: func (reflect.Value) int - switch v := rV2V(args[0]).(type) { - case string: - return len(v) - case array: - return len(v) - case chan value: - return cap(v) - case []value: - return len(v) - case *hashmap: - return v.len() - case map[value]value: - return len(v) - default: - panic(fmt.Sprintf("reflect.(Value).Len(%v)", v)) - } -} - -func ext۰reflect۰Value۰MapIndex(fr *frame, args []value) value { - // Signature: func (reflect.Value) Value - tValue := rV2T(args[0]).t.Underlying().(*types.Map).Key() - k := rV2V(args[1]) - switch m := rV2V(args[0]).(type) { - case map[value]value: - if v, ok := m[k]; ok { - return makeReflectValue(tValue, v) - } - - case *hashmap: - if v := m.lookup(k.(hashable)); v != nil { - return makeReflectValue(tValue, v) - } - - default: - panic(fmt.Sprintf("(reflect.Value).MapIndex(%T, %T)", m, k)) - } - return makeReflectValue(nil, nil) -} - -func ext۰reflect۰Value۰MapKeys(fr *frame, args []value) value { - // Signature: func (reflect.Value) []Value - var keys []value - tKey := rV2T(args[0]).t.Underlying().(*types.Map).Key() - switch v := rV2V(args[0]).(type) { - case map[value]value: - for k := range v { - keys = append(keys, makeReflectValue(tKey, k)) - } - - case *hashmap: - for _, e := range v.table { - for ; e != nil; e = e.next { - keys = append(keys, makeReflectValue(tKey, e.key)) - } - } - - default: - panic(fmt.Sprintf("(reflect.Value).MapKeys(%T)", v)) - } - return keys -} - -func ext۰reflect۰Value۰NumField(fr *frame, args []value) value { - // Signature: func (reflect.Value) int - return len(rV2V(args[0]).(structure)) -} - -func ext۰reflect۰Value۰NumMethod(fr *frame, args []value) value { - // Signature: func (reflect.Value) int - return fr.i.prog.MethodSets.MethodSet(rV2T(args[0]).t).Len() -} - -func ext۰reflect۰Value۰Pointer(fr *frame, args []value) value { - // Signature: func (v reflect.Value) uintptr - switch v := rV2V(args[0]).(type) { - case *value: - return uintptr(unsafe.Pointer(v)) - case chan value: - return reflect.ValueOf(v).Pointer() - case []value: - return reflect.ValueOf(v).Pointer() - case *hashmap: - return reflect.ValueOf(v.table).Pointer() - case map[value]value: - return reflect.ValueOf(v).Pointer() - case *ssa.Function: - return uintptr(unsafe.Pointer(v)) - case *closure: - return uintptr(unsafe.Pointer(v)) - default: - panic(fmt.Sprintf("reflect.(Value).Pointer(%T)", v)) - } -} - -func ext۰reflect۰Value۰Index(fr *frame, args []value) value { - // Signature: func (v reflect.Value, i int) Value - i := args[1].(int) - t := rV2T(args[0]).t.Underlying() - switch v := rV2V(args[0]).(type) { - case array: - return makeReflectValue(t.(*types.Array).Elem(), v[i]) - case []value: - return makeReflectValue(t.(*types.Slice).Elem(), v[i]) - default: - panic(fmt.Sprintf("reflect.(Value).Index(%T)", v)) - } -} - -func ext۰reflect۰Value۰Bool(fr *frame, args []value) value { - // Signature: func (reflect.Value) bool - return rV2V(args[0]).(bool) -} - -func ext۰reflect۰Value۰CanAddr(fr *frame, args []value) value { - // Signature: func (v reflect.Value) bool - // Always false for our representation. - return false -} - -func ext۰reflect۰Value۰CanInterface(fr *frame, args []value) value { - // Signature: func (v reflect.Value) bool - // Always true for our representation. - return true -} - -func ext۰reflect۰Value۰Elem(fr *frame, args []value) value { - // Signature: func (v reflect.Value) reflect.Value - switch x := rV2V(args[0]).(type) { - case iface: - return makeReflectValue(x.t, x.v) - case *value: - return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x) - default: - panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x)) - } -} - -func ext۰reflect۰Value۰Field(fr *frame, args []value) value { - // Signature: func (v reflect.Value, i int) reflect.Value - v := args[0] - i := args[1].(int) - return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type(), rV2V(v).(structure)[i]) -} - -func ext۰reflect۰Value۰Float(fr *frame, args []value) value { - // Signature: func (reflect.Value) float64 - switch v := rV2V(args[0]).(type) { - case float32: - return float64(v) - case float64: - return float64(v) - } - panic("reflect.Value.Float") -} - -func ext۰reflect۰Value۰Interface(fr *frame, args []value) value { - // Signature: func (v reflect.Value) interface{} - return ext۰reflect۰valueInterface(fr, args) -} - -func ext۰reflect۰Value۰Int(fr *frame, args []value) value { - // Signature: func (reflect.Value) int64 - switch x := rV2V(args[0]).(type) { - case int: - return int64(x) - case int8: - return int64(x) - case int16: - return int64(x) - case int32: - return int64(x) - case int64: - return x - default: - panic(fmt.Sprintf("reflect.(Value).Int(%T)", x)) - } -} - -func ext۰reflect۰Value۰IsNil(fr *frame, args []value) value { - // Signature: func (reflect.Value) bool - switch x := rV2V(args[0]).(type) { - case *value: - return x == nil - case chan value: - return x == nil - case map[value]value: - return x == nil - case *hashmap: - return x == nil - case iface: - return x.t == nil - case []value: - return x == nil - case *ssa.Function: - return x == nil - case *ssa.Builtin: - return x == nil - case *closure: - return x == nil - default: - panic(fmt.Sprintf("reflect.(Value).IsNil(%T)", x)) - } -} - -func ext۰reflect۰Value۰IsValid(fr *frame, args []value) value { - // Signature: func (reflect.Value) bool - return rV2V(args[0]) != nil -} - -func ext۰reflect۰Value۰Set(fr *frame, args []value) value { - // TODO(adonovan): implement. - return nil -} - -func ext۰reflect۰valueInterface(fr *frame, args []value) value { - // Signature: func (v reflect.Value, safe bool) interface{} - v := args[0].(structure) - return iface{rV2T(v).t, rV2V(v)} -} - -func ext۰reflect۰error۰Error(fr *frame, args []value) value { - return args[0] -} - -// newMethod creates a new method of the specified name, package and receiver type. -func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function { - // TODO(adonovan): fix: hack: currently the only part of Signature - // 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. - sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) - fn := pkg.Prog.NewFunction(name, sig, "fake reflect method") - fn.Pkg = pkg - return fn -} - -func initReflect(i *interpreter) { - i.reflectPackage = &ssa.Package{ - Prog: i.prog, - Pkg: reflectTypesPackage, - Members: make(map[string]ssa.Member), - } - - // Clobber the type-checker's notion of reflect.Value's - // underlying type so that it more closely matches the fake one - // (at least in the number of fields---we lie about the type of - // the rtype field). - // - // We must ensure that calls to (ssa.Value).Type() return the - // fake type so that correct "shape" is used when allocating - // variables, making zero values, loading, and storing. - // - // TODO(adonovan): obviously this is a hack. We need a cleaner - // way to fake the reflect package (almost---DeepEqual is fine). - // One approach would be not to even load its source code, but - // provide fake source files. This would guarantee that no bad - // information leaks into other packages. - if r := i.prog.ImportedPackage("reflect"); r != nil { - rV := r.Pkg.Scope().Lookup("Value").Type().(*types.Named) - - // delete bodies of the old methods - mset := i.prog.MethodSets.MethodSet(rV) - for j := 0; j < mset.Len(); j++ { - i.prog.MethodValue(mset.At(j)).Blocks = nil - } - - tEface := types.NewInterface(nil, nil).Complete() - rV.SetUnderlying(types.NewStruct([]*types.Var{ - types.NewField(token.NoPos, r.Pkg, "t", tEface, false), // a lie - types.NewField(token.NoPos, r.Pkg, "v", tEface, false), - }, nil)) - } - - i.rtypeMethods = methodSet{ - "Bits": newMethod(i.reflectPackage, rtypeType, "Bits"), - "Elem": newMethod(i.reflectPackage, rtypeType, "Elem"), - "Field": newMethod(i.reflectPackage, rtypeType, "Field"), - "In": newMethod(i.reflectPackage, rtypeType, "In"), - "Kind": newMethod(i.reflectPackage, rtypeType, "Kind"), - "NumField": newMethod(i.reflectPackage, rtypeType, "NumField"), - "NumIn": newMethod(i.reflectPackage, rtypeType, "NumIn"), - "NumMethod": newMethod(i.reflectPackage, rtypeType, "NumMethod"), - "NumOut": newMethod(i.reflectPackage, rtypeType, "NumOut"), - "Out": newMethod(i.reflectPackage, rtypeType, "Out"), - "Size": newMethod(i.reflectPackage, rtypeType, "Size"), - "String": newMethod(i.reflectPackage, rtypeType, "String"), - } - i.errorMethods = methodSet{ - "Error": newMethod(i.reflectPackage, errorType, "Error"), - } -} diff --git a/go/ssa/interp/value14.go b/go/ssa/interp/value14.go deleted file mode 100644 index f8fedd3fc0..0000000000 --- a/go/ssa/interp/value14.go +++ /dev/null @@ -1,499 +0,0 @@ -// 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. - -// +build !go1.5 - -package interp - -// Values -// -// All interpreter values are "boxed" in the empty interface, value. -// The range of possible dynamic types within value are: -// -// - bool -// - numbers (all built-in int/float/complex types are distinguished) -// - string -// - map[value]value --- maps for which usesBuiltinMap(keyType) -// *hashmap --- maps for which !usesBuiltinMap(keyType) -// - chan value -// - []value --- slices -// - iface --- interfaces. -// - structure --- structs. Fields are ordered and accessed by numeric indices. -// - array --- arrays. -// - *value --- pointers. Careful: *value is a distinct type from *array etc. -// - *ssa.Function \ -// *ssa.Builtin } --- functions. A nil 'func' is always of type *ssa.Function. -// *closure / -// - tuple --- as returned by Return, Next, "value,ok" modes, etc. -// - iter --- iterators from 'range' over map or string. -// - bad --- a poison pill for locals that have gone out of scope. -// - rtype -- the interpreter's concrete implementation of reflect.Type -// -// Note that nil is not on this list. -// -// Pay close attention to whether or not the dynamic type is a pointer. -// The compiler cannot help you since value is an empty interface. - -import ( - "bytes" - "fmt" - "io" - "reflect" - "strings" - "sync" - "unsafe" - - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -type value interface{} - -type tuple []value - -type array []value - -type iface struct { - t types.Type // never an "untyped" type - v value -} - -type structure []value - -// For map, array, *array, slice, string or channel. -type iter interface { - // next returns a Tuple (key, value, ok). - // key and value are unaliased, e.g. copies of the sequence element. - next() tuple -} - -type closure struct { - Fn *ssa.Function - Env []value -} - -type bad struct{} - -type rtype struct { - t types.Type -} - -// Hash functions and equivalence relation: - -// hashString computes the FNV hash of s. -func hashString(s string) int { - var h uint32 - for i := 0; i < len(s); i++ { - h ^= uint32(s[i]) - h *= 16777619 - } - return int(h) -} - -var ( - mu sync.Mutex - hasher = typeutil.MakeHasher() -) - -// hashType returns a hash for t such that -// types.Identical(x, y) => hashType(x) == hashType(y). -func hashType(t types.Type) int { - mu.Lock() - h := int(hasher.Hash(t)) - mu.Unlock() - return h -} - -// usesBuiltinMap returns true if the built-in hash function and -// equivalence relation for type t are consistent with those of the -// interpreter's representation of type t. Such types are: all basic -// types (bool, numbers, string), pointers and channels. -// -// usesBuiltinMap returns false for types that require a custom map -// implementation: interfaces, arrays and structs. -// -// Panic ensues if t is an invalid map key type: function, map or slice. -func usesBuiltinMap(t types.Type) bool { - switch t := t.(type) { - case *types.Basic, *types.Chan, *types.Pointer: - return true - case *types.Named: - return usesBuiltinMap(t.Underlying()) - case *types.Interface, *types.Array, *types.Struct: - return false - } - panic(fmt.Sprintf("invalid map key type: %T", t)) -} - -func (x array) eq(t types.Type, _y interface{}) bool { - y := _y.(array) - tElt := t.Underlying().(*types.Array).Elem() - for i, xi := range x { - if !equals(tElt, xi, y[i]) { - return false - } - } - return true -} - -func (x array) hash(t types.Type) int { - h := 0 - tElt := t.Underlying().(*types.Array).Elem() - for _, xi := range x { - h += hash(tElt, xi) - } - return h -} - -func (x structure) eq(t types.Type, _y interface{}) bool { - y := _y.(structure) - tStruct := t.Underlying().(*types.Struct) - for i, n := 0, tStruct.NumFields(); i < n; i++ { - if f := tStruct.Field(i); !f.Anonymous() { - if !equals(f.Type(), x[i], y[i]) { - return false - } - } - } - return true -} - -func (x structure) hash(t types.Type) int { - tStruct := t.Underlying().(*types.Struct) - h := 0 - for i, n := 0, tStruct.NumFields(); i < n; i++ { - if f := tStruct.Field(i); !f.Anonymous() { - h += hash(f.Type(), x[i]) - } - } - return h -} - -// nil-tolerant variant of types.Identical. -func sameType(x, y types.Type) bool { - if x == nil { - return y == nil - } - return y != nil && types.Identical(x, y) -} - -func (x iface) eq(t types.Type, _y interface{}) bool { - y := _y.(iface) - return sameType(x.t, y.t) && (x.t == nil || equals(x.t, x.v, y.v)) -} - -func (x iface) hash(_ types.Type) int { - return hashType(x.t)*8581 + hash(x.t, x.v) -} - -func (x rtype) hash(_ types.Type) int { - return hashType(x.t) -} - -func (x rtype) eq(_ types.Type, y interface{}) bool { - return types.Identical(x.t, y.(rtype).t) -} - -// equals returns true iff x and y are equal according to Go's -// linguistic equivalence relation for type t. -// In a well-typed program, the dynamic types of x and y are -// guaranteed equal. -func equals(t types.Type, x, y value) bool { - switch x := x.(type) { - case bool: - return x == y.(bool) - case int: - return x == y.(int) - case int8: - return x == y.(int8) - case int16: - return x == y.(int16) - case int32: - return x == y.(int32) - case int64: - return x == y.(int64) - case uint: - return x == y.(uint) - case uint8: - return x == y.(uint8) - case uint16: - return x == y.(uint16) - case uint32: - return x == y.(uint32) - case uint64: - return x == y.(uint64) - case uintptr: - return x == y.(uintptr) - case float32: - return x == y.(float32) - case float64: - return x == y.(float64) - case complex64: - return x == y.(complex64) - case complex128: - return x == y.(complex128) - case string: - return x == y.(string) - case *value: - return x == y.(*value) - case chan value: - return x == y.(chan value) - case structure: - return x.eq(t, y) - case array: - return x.eq(t, y) - case iface: - return x.eq(t, y) - case rtype: - return x.eq(t, y) - } - - // Since map, func and slice don't support comparison, this - // case is only reachable if one of x or y is literally nil - // (handled in eqnil) or via interface{} values. - panic(fmt.Sprintf("comparing uncomparable type %s", t)) -} - -// Returns an integer hash of x such that equals(x, y) => hash(x) == hash(y). -func hash(t types.Type, x value) int { - switch x := x.(type) { - case bool: - if x { - return 1 - } - return 0 - case int: - return x - case int8: - return int(x) - case int16: - return int(x) - case int32: - return int(x) - case int64: - return int(x) - case uint: - return int(x) - case uint8: - return int(x) - case uint16: - return int(x) - case uint32: - return int(x) - case uint64: - return int(x) - case uintptr: - return int(x) - case float32: - return int(x) - case float64: - return int(x) - case complex64: - return int(real(x)) - case complex128: - return int(real(x)) - case string: - return hashString(x) - case *value: - return int(uintptr(unsafe.Pointer(x))) - case chan value: - return int(uintptr(reflect.ValueOf(x).Pointer())) - case structure: - return x.hash(t) - case array: - return x.hash(t) - case iface: - return x.hash(t) - case rtype: - return x.hash(t) - } - panic(fmt.Sprintf("%T is unhashable", x)) -} - -// reflect.Value struct values don't have a fixed shape, since the -// payload can be a scalar or an aggregate depending on the instance. -// So store (and load) can't simply use recursion over the shape of the -// rhs value, or the lhs, to copy the value; we need the static type -// information. (We can't make reflect.Value a new basic data type -// because its "structness" is exposed to Go programs.) - -// load returns the value of type T in *addr. -func load(T types.Type, addr *value) value { - switch T := T.Underlying().(type) { - case *types.Struct: - v := (*addr).(structure) - a := make(structure, len(v)) - for i := range a { - a[i] = load(T.Field(i).Type(), &v[i]) - } - return a - case *types.Array: - v := (*addr).(array) - a := make(array, len(v)) - for i := range a { - a[i] = load(T.Elem(), &v[i]) - } - return a - default: - return *addr - } -} - -// store stores value v of type T into *addr. -func store(T types.Type, addr *value, v value) { - switch T := T.Underlying().(type) { - case *types.Struct: - lhs := (*addr).(structure) - rhs := v.(structure) - for i := range lhs { - store(T.Field(i).Type(), &lhs[i], rhs[i]) - } - case *types.Array: - lhs := (*addr).(array) - rhs := v.(array) - for i := range lhs { - store(T.Elem(), &lhs[i], rhs[i]) - } - default: - *addr = v - } -} - -// Prints in the style of built-in println. -// (More or less; in gc println is actually a compiler intrinsic and -// can distinguish println(1) from println(interface{}(1)).) -func writeValue(buf *bytes.Buffer, v value) { - switch v := v.(type) { - case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128, string: - fmt.Fprintf(buf, "%v", v) - - case map[value]value: - buf.WriteString("map[") - sep := "" - for k, e := range v { - buf.WriteString(sep) - sep = " " - writeValue(buf, k) - buf.WriteString(":") - writeValue(buf, e) - } - buf.WriteString("]") - - case *hashmap: - buf.WriteString("map[") - sep := " " - for _, e := range v.table { - for e != nil { - buf.WriteString(sep) - sep = " " - writeValue(buf, e.key) - buf.WriteString(":") - writeValue(buf, e.value) - e = e.next - } - } - buf.WriteString("]") - - case chan value: - fmt.Fprintf(buf, "%v", v) // (an address) - - case *value: - if v == nil { - buf.WriteString("") - } else { - fmt.Fprintf(buf, "%p", v) - } - - case iface: - fmt.Fprintf(buf, "(%s, ", v.t) - writeValue(buf, v.v) - buf.WriteString(")") - - case structure: - buf.WriteString("{") - for i, e := range v { - if i > 0 { - buf.WriteString(" ") - } - writeValue(buf, e) - } - buf.WriteString("}") - - case array: - buf.WriteString("[") - for i, e := range v { - if i > 0 { - buf.WriteString(" ") - } - writeValue(buf, e) - } - buf.WriteString("]") - - case []value: - buf.WriteString("[") - for i, e := range v { - if i > 0 { - buf.WriteString(" ") - } - writeValue(buf, e) - } - buf.WriteString("]") - - case *ssa.Function, *ssa.Builtin, *closure: - fmt.Fprintf(buf, "%p", v) // (an address) - - case rtype: - buf.WriteString(v.t.String()) - - case tuple: - // Unreachable in well-formed Go programs - buf.WriteString("(") - for i, e := range v { - if i > 0 { - buf.WriteString(", ") - } - writeValue(buf, e) - } - buf.WriteString(")") - - default: - fmt.Fprintf(buf, "<%T>", v) - } -} - -// Implements printing of Go values in the style of built-in println. -func toString(v value) string { - var b bytes.Buffer - writeValue(&b, v) - return b.String() -} - -// ------------------------------------------------------------------------ -// Iterators - -type stringIter struct { - *strings.Reader - i int -} - -func (it *stringIter) next() tuple { - okv := make(tuple, 3) - ch, n, err := it.ReadRune() - ok := err != io.EOF - okv[0] = ok - if ok { - okv[1] = it.i - okv[2] = ch - } - it.i += n - return okv -} - -type mapIter chan [2]value - -func (it mapIter) next() tuple { - kv, ok := <-it - return tuple{ok, kv[0], kv[1]} -} diff --git a/go/ssa/lift14.go b/go/ssa/lift14.go deleted file mode 100644 index d57a85ccfc..0000000000 --- a/go/ssa/lift14.go +++ /dev/null @@ -1,601 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines the lifting pass which tries to "lift" Alloc -// cells (new/local variables) into SSA registers, replacing loads -// with the dominating stored value, eliminating loads and stores, and -// inserting φ-nodes as needed. - -// Cited papers and resources: -// -// Ron Cytron et al. 1991. Efficiently computing SSA form... -// http://doi.acm.org/10.1145/115372.115320 -// -// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm. -// Software Practice and Experience 2001, 4:1-10. -// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf -// -// Daniel Berlin, llvmdev mailing list, 2012. -// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html -// (Be sure to expand the whole thread.) - -// TODO(adonovan): opt: there are many optimizations worth evaluating, and -// the conventional wisdom for SSA construction is that a simple -// algorithm well engineered often beats those of better asymptotic -// complexity on all but the most egregious inputs. -// -// Danny Berlin suggests that the Cooper et al. algorithm for -// computing the dominance frontier is superior to Cytron et al. -// Furthermore he recommends that rather than computing the DF for the -// whole function then renaming all alloc cells, it may be cheaper to -// compute the DF for each alloc cell separately and throw it away. -// -// Consider exploiting liveness information to avoid creating dead -// φ-nodes which we then immediately remove. -// -// Integrate lifting with scalar replacement of aggregates (SRA) since -// the two are synergistic. -// -// Also see many other "TODO: opt" suggestions in the code. - -import ( - "fmt" - "go/token" - "math/big" - "os" - - "golang.org/x/tools/go/types" -) - -// If true, perform sanity checking and show diagnostic information at -// each step of lifting. Very verbose. -const debugLifting = false - -// domFrontier maps each block to the set of blocks in its dominance -// frontier. The outer slice is conceptually a map keyed by -// Block.Index. The inner slice is conceptually a set, possibly -// containing duplicates. -// -// TODO(adonovan): opt: measure impact of dups; consider a packed bit -// representation, e.g. big.Int, and bitwise parallel operations for -// the union step in the Children loop. -// -// domFrontier's methods mutate the slice's elements but not its -// length, so their receivers needn't be pointers. -// -type domFrontier [][]*BasicBlock - -func (df domFrontier) add(u, v *BasicBlock) { - p := &df[u.Index] - *p = append(*p, v) -} - -// build builds the dominance frontier df for the dominator (sub)tree -// rooted at u, using the Cytron et al. algorithm. -// -// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA -// by pruning the entire IDF computation, rather than merely pruning -// the DF -> IDF step. -func (df domFrontier) build(u *BasicBlock) { - // Encounter each node u in postorder of dom tree. - for _, child := range u.dom.children { - df.build(child) - } - for _, vb := range u.Succs { - if v := vb.dom; v.idom != u { - df.add(u, vb) - } - } - for _, w := range u.dom.children { - for _, vb := range df[w.Index] { - // TODO(adonovan): opt: use word-parallel bitwise union. - if v := vb.dom; v.idom != u { - df.add(u, vb) - } - } - } -} - -func buildDomFrontier(fn *Function) domFrontier { - df := make(domFrontier, len(fn.Blocks)) - df.build(fn.Blocks[0]) - if fn.Recover != nil { - df.build(fn.Recover) - } - return df -} - -func removeInstr(refs []Instruction, instr Instruction) []Instruction { - i := 0 - for _, ref := range refs { - if ref == instr { - continue - } - refs[i] = ref - i++ - } - for j := i; j != len(refs); j++ { - refs[j] = nil // aid GC - } - return refs[:i] -} - -// lift attempts to replace local and new Allocs accessed only with -// load/store by SSA registers, inserting φ-nodes where necessary. -// The result is a program in classical pruned SSA form. -// -// Preconditions: -// - fn has no dead blocks (blockopt has run). -// - Def/use info (Operands and Referrers) is up-to-date. -// - The dominator tree is up-to-date. -// -func lift(fn *Function) { - // TODO(adonovan): opt: lots of little optimizations may be - // worthwhile here, especially if they cause us to avoid - // buildDomFrontier. For example: - // - // - Alloc never loaded? Eliminate. - // - Alloc never stored? Replace all loads with a zero constant. - // - Alloc stored once? Replace loads with dominating store; - // don't forget that an Alloc is itself an effective store - // of zero. - // - Alloc used only within a single block? - // Use degenerate algorithm avoiding φ-nodes. - // - Consider synergy with scalar replacement of aggregates (SRA). - // e.g. *(&x.f) where x is an Alloc. - // Perhaps we'd get better results if we generated this as x.f - // i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)). - // Unclear. - // - // But we will start with the simplest correct code. - df := buildDomFrontier(fn) - - if debugLifting { - title := false - for i, blocks := range df { - if blocks != nil { - if !title { - fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn) - title = true - } - fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks) - } - } - } - - newPhis := make(newPhiMap) - - // During this pass we will replace some BasicBlock.Instrs - // (allocs, loads and stores) with nil, keeping a count in - // BasicBlock.gaps. At the end we will reset Instrs to the - // concatenation of all non-dead newPhis and non-nil Instrs - // for the block, reusing the original array if space permits. - - // While we're here, we also eliminate 'rundefers' - // instructions in functions that contain no 'defer' - // instructions. - usesDefer := false - - // Determine which allocs we can lift and number them densely. - // The renaming phase uses this numbering for compact maps. - numAllocs := 0 - for _, b := range fn.Blocks { - b.gaps = 0 - b.rundefers = 0 - for _, instr := range b.Instrs { - switch instr := instr.(type) { - case *Alloc: - index := -1 - if liftAlloc(df, instr, newPhis) { - index = numAllocs - numAllocs++ - } - instr.index = index - case *Defer: - usesDefer = true - case *RunDefers: - b.rundefers++ - } - } - } - - // renaming maps an alloc (keyed by index) to its replacement - // value. Initially the renaming contains nil, signifying the - // zero constant of the appropriate type; we construct the - // Const lazily at most once on each path through the domtree. - // TODO(adonovan): opt: cache per-function not per subtree. - renaming := make([]Value, numAllocs) - - // Renaming. - rename(fn.Blocks[0], renaming, newPhis) - - // Eliminate dead new phis, then prepend the live ones to each block. - for _, b := range fn.Blocks { - - // Compress the newPhis slice to eliminate unused phis. - // TODO(adonovan): opt: compute liveness to avoid - // placing phis in blocks for which the alloc cell is - // not live. - nps := newPhis[b] - j := 0 - for _, np := range nps { - if !phiIsLive(np.phi) { - // discard it, first removing it from referrers - for _, newval := range np.phi.Edges { - if refs := newval.Referrers(); refs != nil { - *refs = removeInstr(*refs, np.phi) - } - } - continue - } - nps[j] = np - j++ - } - nps = nps[:j] - - rundefersToKill := b.rundefers - if usesDefer { - rundefersToKill = 0 - } - - if j+b.gaps+rundefersToKill == 0 { - continue // fast path: no new phis or gaps - } - - // Compact nps + non-nil Instrs into a new slice. - // TODO(adonovan): opt: compact in situ if there is - // sufficient space or slack in the slice. - dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill) - for i, np := range nps { - dst[i] = np.phi - } - for _, instr := range b.Instrs { - if instr == nil { - continue - } - if !usesDefer { - if _, ok := instr.(*RunDefers); ok { - continue - } - } - dst[j] = instr - j++ - } - for i, np := range nps { - dst[i] = np.phi - } - b.Instrs = dst - } - - // Remove any fn.Locals that were lifted. - j := 0 - for _, l := range fn.Locals { - if l.index < 0 { - fn.Locals[j] = l - j++ - } - } - // Nil out fn.Locals[j:] to aid GC. - for i := j; i < len(fn.Locals); i++ { - fn.Locals[i] = nil - } - fn.Locals = fn.Locals[:j] -} - -func phiIsLive(phi *Phi) bool { - for _, instr := range *phi.Referrers() { - if instr == phi { - continue // self-refs don't count - } - if _, ok := instr.(*DebugRef); ok { - continue // debug refs don't count - } - return true - } - return false -} - -type blockSet struct{ big.Int } // (inherit methods from Int) - -// add adds b to the set and returns true if the set changed. -func (s *blockSet) add(b *BasicBlock) bool { - i := b.Index - if s.Bit(i) != 0 { - return false - } - s.SetBit(&s.Int, i, 1) - return true -} - -// take removes an arbitrary element from a set s and -// returns its index, or returns -1 if empty. -func (s *blockSet) take() int { - l := s.BitLen() - for i := 0; i < l; i++ { - if s.Bit(i) == 1 { - s.SetBit(&s.Int, i, 0) - return i - } - } - return -1 -} - -// newPhi is a pair of a newly introduced φ-node and the lifted Alloc -// it replaces. -type newPhi struct { - phi *Phi - alloc *Alloc -} - -// newPhiMap records for each basic block, the set of newPhis that -// must be prepended to the block. -type newPhiMap map[*BasicBlock][]newPhi - -// liftAlloc determines whether alloc can be lifted into registers, -// and if so, it populates newPhis with all the φ-nodes it may require -// and returns true. -// -func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { - // Don't lift aggregates into registers, because we don't have - // a way to express their zero-constants. - switch deref(alloc.Type()).Underlying().(type) { - case *types.Array, *types.Struct: - return false - } - - // Don't lift named return values in functions that defer - // calls that may recover from panic. - if fn := alloc.Parent(); fn.Recover != nil { - for _, nr := range fn.namedResults { - if nr == alloc { - return false - } - } - } - - // Compute defblocks, the set of blocks containing a - // definition of the alloc cell. - var defblocks blockSet - for _, instr := range *alloc.Referrers() { - // Bail out if we discover the alloc is not liftable; - // the only operations permitted to use the alloc are - // loads/stores into the cell, and DebugRef. - switch instr := instr.(type) { - case *Store: - if instr.Val == alloc { - return false // address used as value - } - if instr.Addr != alloc { - panic("Alloc.Referrers is inconsistent") - } - defblocks.add(instr.Block()) - case *UnOp: - if instr.Op != token.MUL { - return false // not a load - } - if instr.X != alloc { - panic("Alloc.Referrers is inconsistent") - } - case *DebugRef: - // ok - default: - return false // some other instruction - } - } - // The Alloc itself counts as a (zero) definition of the cell. - defblocks.add(alloc.Block()) - - if debugLifting { - fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name()) - } - - fn := alloc.Parent() - - // Φ-insertion. - // - // What follows is the body of the main loop of the insert-φ - // function described by Cytron et al, but instead of using - // counter tricks, we just reset the 'hasAlready' and 'work' - // sets each iteration. These are bitmaps so it's pretty cheap. - // - // TODO(adonovan): opt: recycle slice storage for W, - // hasAlready, defBlocks across liftAlloc calls. - var hasAlready blockSet - - // Initialize W and work to defblocks. - var work blockSet = defblocks // blocks seen - var W blockSet // blocks to do - W.Set(&defblocks.Int) - - // Traverse iterated dominance frontier, inserting φ-nodes. - for i := W.take(); i != -1; i = W.take() { - u := fn.Blocks[i] - for _, v := range df[u.Index] { - if hasAlready.add(v) { - // Create φ-node. - // It will be prepended to v.Instrs later, if needed. - phi := &Phi{ - Edges: make([]Value, len(v.Preds)), - Comment: alloc.Comment, - } - phi.pos = alloc.Pos() - phi.setType(deref(alloc.Type())) - phi.block = v - if debugLifting { - fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v) - } - newPhis[v] = append(newPhis[v], newPhi{phi, alloc}) - - if work.add(v) { - W.add(v) - } - } - } - } - - return true -} - -// replaceAll replaces all intraprocedural uses of x with y, -// updating x.Referrers and y.Referrers. -// Precondition: x.Referrers() != nil, i.e. x must be local to some function. -// -func replaceAll(x, y Value) { - var rands []*Value - pxrefs := x.Referrers() - pyrefs := y.Referrers() - for _, instr := range *pxrefs { - rands = instr.Operands(rands[:0]) // recycle storage - for _, rand := range rands { - if *rand != nil { - if *rand == x { - *rand = y - } - } - } - if pyrefs != nil { - *pyrefs = append(*pyrefs, instr) // dups ok - } - } - *pxrefs = nil // x is now unreferenced -} - -// renamed returns the value to which alloc is being renamed, -// constructing it lazily if it's the implicit zero initialization. -// -func renamed(renaming []Value, alloc *Alloc) Value { - v := renaming[alloc.index] - if v == nil { - v = zeroConst(deref(alloc.Type())) - renaming[alloc.index] = v - } - return v -} - -// rename implements the (Cytron et al) SSA renaming algorithm, a -// preorder traversal of the dominator tree replacing all loads of -// Alloc cells with the value stored to that cell by the dominating -// store instruction. For lifting, we need only consider loads, -// stores and φ-nodes. -// -// renaming is a map from *Alloc (keyed by index number) to its -// dominating stored value; newPhis[x] is the set of new φ-nodes to be -// prepended to block x. -// -func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) { - // Each φ-node becomes the new name for its associated Alloc. - for _, np := range newPhis[u] { - phi := np.phi - alloc := np.alloc - renaming[alloc.index] = phi - } - - // Rename loads and stores of allocs. - for i, instr := range u.Instrs { - switch instr := instr.(type) { - case *Alloc: - if instr.index >= 0 { // store of zero to Alloc cell - // Replace dominated loads by the zero value. - renaming[instr.index] = nil - if debugLifting { - fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr) - } - // Delete the Alloc. - u.Instrs[i] = nil - u.gaps++ - } - - case *Store: - if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell - // Replace dominated loads by the stored value. - renaming[alloc.index] = instr.Val - if debugLifting { - fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n", - instr, instr.Val.Name()) - } - // Remove the store from the referrer list of the stored value. - if refs := instr.Val.Referrers(); refs != nil { - *refs = removeInstr(*refs, instr) - } - // Delete the Store. - u.Instrs[i] = nil - u.gaps++ - } - - case *UnOp: - if instr.Op == token.MUL { - if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell - newval := renamed(renaming, alloc) - if debugLifting { - fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n", - instr.Name(), instr, newval.Name()) - } - // Replace all references to - // the loaded value by the - // dominating stored value. - replaceAll(instr, newval) - // Delete the Load. - u.Instrs[i] = nil - u.gaps++ - } - } - - case *DebugRef: - if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell - if instr.IsAddr { - instr.X = renamed(renaming, alloc) - instr.IsAddr = false - - // Add DebugRef to instr.X's referrers. - if refs := instr.X.Referrers(); refs != nil { - *refs = append(*refs, instr) - } - } else { - // A source expression denotes the address - // of an Alloc that was optimized away. - instr.X = nil - - // Delete the DebugRef. - u.Instrs[i] = nil - u.gaps++ - } - } - } - } - - // For each φ-node in a CFG successor, rename the edge. - for _, v := range u.Succs { - phis := newPhis[v] - if len(phis) == 0 { - continue - } - i := v.predIndex(u) - for _, np := range phis { - phi := np.phi - alloc := np.alloc - newval := renamed(renaming, alloc) - if debugLifting { - fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n", - phi.Name(), u, v, i, alloc.Name(), newval.Name()) - } - phi.Edges[i] = newval - if prefs := newval.Referrers(); prefs != nil { - *prefs = append(*prefs, phi) - } - } - } - - // Continue depth-first recursion over domtree, pushing a - // fresh copy of the renaming map for each subtree. - for _, v := range u.dom.children { - // TODO(adonovan): opt: avoid copy on final iteration; use destructive update. - r := make([]Value, len(renaming)) - copy(r, renaming) - rename(v, r, newPhis) - } -} diff --git a/go/ssa/lvalue14.go b/go/ssa/lvalue14.go deleted file mode 100644 index 597761b31a..0000000000 --- a/go/ssa/lvalue14.go +++ /dev/null @@ -1,123 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// lvalues are the union of addressable expressions and map-index -// expressions. - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -// An lvalue represents an assignable location that may appear on the -// left-hand side of an assignment. This is a generalization of a -// pointer to permit updates to elements of maps. -// -type lvalue interface { - store(fn *Function, v Value) // stores v into the location - load(fn *Function) Value // loads the contents of the location - address(fn *Function) Value // address of the location - typ() types.Type // returns the type of the location -} - -// An address is an lvalue represented by a true pointer. -type address struct { - addr Value - pos token.Pos // source position - expr ast.Expr // source syntax of the value (not address) [debug mode] -} - -func (a *address) load(fn *Function) Value { - load := emitLoad(fn, a.addr) - load.pos = a.pos - return load -} - -func (a *address) store(fn *Function, v Value) { - store := emitStore(fn, a.addr, v, a.pos) - if a.expr != nil { - // store.Val is v, converted for assignability. - emitDebugRef(fn, a.expr, store.Val, false) - } -} - -func (a *address) address(fn *Function) Value { - if a.expr != nil { - emitDebugRef(fn, a.expr, a.addr, true) - } - return a.addr -} - -func (a *address) typ() types.Type { - return deref(a.addr.Type()) -} - -// An element is an lvalue represented by m[k], the location of an -// element of a map or string. These locations are not addressable -// since pointers cannot be formed from them, but they do support -// load(), and in the case of maps, store(). -// -type element struct { - m, k Value // map or string - t types.Type // map element type or string byte type - pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v) -} - -func (e *element) load(fn *Function) Value { - l := &Lookup{ - X: e.m, - Index: e.k, - } - l.setPos(e.pos) - l.setType(e.t) - return fn.emit(l) -} - -func (e *element) store(fn *Function, v Value) { - up := &MapUpdate{ - Map: e.m, - Key: e.k, - Value: emitConv(fn, v, e.t), - } - up.pos = e.pos - fn.emit(up) -} - -func (e *element) address(fn *Function) Value { - panic("map/string elements are not addressable") -} - -func (e *element) typ() types.Type { - return e.t -} - -// A blank is a dummy variable whose name is "_". -// It is not reified: loads are illegal and stores are ignored. -// -type blank struct{} - -func (bl blank) load(fn *Function) Value { - panic("blank.load is illegal") -} - -func (bl blank) store(fn *Function, v Value) { - // no-op -} - -func (bl blank) address(fn *Function) Value { - panic("blank var is not addressable") -} - -func (bl blank) typ() types.Type { - // This should be the type of the blank Ident; the typechecker - // doesn't provide this yet, but fortunately, we don't need it - // yet either. - panic("blank.typ is unimplemented") -} diff --git a/go/ssa/methods14.go b/go/ssa/methods14.go deleted file mode 100644 index 7c2a40d62c..0000000000 --- a/go/ssa/methods14.go +++ /dev/null @@ -1,242 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines utilities for population of method sets. - -import ( - "fmt" - - "golang.org/x/tools/go/types" -) - -// MethodValue returns the Function implementing method sel, building -// wrapper methods on demand. It returns nil if sel denotes an -// abstract (interface) method. -// -// Precondition: sel.Kind() == MethodVal. -// -// Thread-safe. -// -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) -// -func (prog *Program) MethodValue(sel *types.Selection) *Function { - if sel.Kind() != types.MethodVal { - panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel)) - } - T := sel.Recv() - if isInterface(T) { - return nil // abstract method - } - if prog.mode&LogSource != 0 { - defer logStack("Method %s %v", T, sel)() - } - - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - - return prog.addMethod(prog.createMethodSet(T), sel) -} - -// LookupMethod returns the implementation of the method of type T -// identified by (pkg, name). It returns nil if the method exists but -// is abstract, and panics if T has no such method. -// -func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function { - sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name) - if sel == nil { - panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name))) - } - return prog.MethodValue(sel) -} - -// methodSet contains the (concrete) methods of a non-interface type. -type methodSet struct { - mapping map[string]*Function // populated lazily - complete bool // mapping contains all methods -} - -// Precondition: !isInterface(T). -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func (prog *Program) createMethodSet(T types.Type) *methodSet { - mset, ok := prog.methodSets.At(T).(*methodSet) - if !ok { - mset = &methodSet{mapping: make(map[string]*Function)} - prog.methodSets.Set(T, mset) - } - return mset -} - -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function { - if sel.Kind() == types.MethodExpr { - panic(sel) - } - id := sel.Obj().Id() - fn := mset.mapping[id] - if fn == nil { - obj := sel.Obj().(*types.Func) - - needsPromotion := len(sel.Index()) > 1 - needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv()) - if needsPromotion || needsIndirection { - fn = makeWrapper(prog, sel) - } else { - fn = prog.declaredFunc(obj) - } - if fn.Signature.Recv() == nil { - panic(fn) // missing receiver - } - mset.mapping[id] = fn - } - return fn -} - -// RuntimeTypes returns a new unordered slice containing all -// concrete types in the program for which a complete (non-empty) -// method set is required at run-time. -// -// Thread-safe. -// -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) -// -func (prog *Program) RuntimeTypes() []types.Type { - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - - var res []types.Type - prog.methodSets.Iterate(func(T types.Type, v interface{}) { - if v.(*methodSet).complete { - res = append(res, T) - } - }) - return res -} - -// declaredFunc returns the concrete function/method denoted by obj. -// Panic ensues if there is none. -// -func (prog *Program) declaredFunc(obj *types.Func) *Function { - if v := prog.packageLevelValue(obj); v != nil { - return v.(*Function) - } - panic("no concrete method: " + obj.String()) -} - -// needMethodsOf ensures that runtime type information (including the -// complete method set) is available for the specified type T and all -// its subcomponents. -// -// needMethodsOf must be called for at least every type that is an -// operand of some MakeInterface instruction, and for the type of -// every exported package member. -// -// Precondition: T is not a method signature (*Signature with Recv()!=nil). -// -// Thread-safe. (Called via emitConv from multiple builder goroutines.) -// -// TODO(adonovan): make this faster. It accounts for 20% of SSA build time. -// -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) -// -func (prog *Program) needMethodsOf(T types.Type) { - prog.methodsMu.Lock() - prog.needMethods(T, false) - prog.methodsMu.Unlock() -} - -// Precondition: T is not a method signature (*Signature with Recv()!=nil). -// Recursive case: skip => don't create methods for T. -// -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -// -func (prog *Program) needMethods(T types.Type, skip bool) { - // Each package maintains its own set of types it has visited. - if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok { - // needMethods(T) was previously called - if !prevSkip || skip { - return // already seen, with same or false 'skip' value - } - } - prog.runtimeTypes.Set(T, skip) - - tmset := prog.MethodSets.MethodSet(T) - - if !skip && !isInterface(T) && tmset.Len() > 0 { - // Create methods of T. - mset := prog.createMethodSet(T) - if !mset.complete { - mset.complete = true - n := tmset.Len() - for i := 0; i < n; i++ { - prog.addMethod(mset, tmset.At(i)) - } - } - } - - // Recursion over signatures of each method. - for i := 0; i < tmset.Len(); i++ { - sig := tmset.At(i).Type().(*types.Signature) - prog.needMethods(sig.Params(), false) - prog.needMethods(sig.Results(), false) - } - - switch t := T.(type) { - case *types.Basic: - // nop - - case *types.Interface: - // nop---handled by recursion over method set. - - case *types.Pointer: - prog.needMethods(t.Elem(), false) - - case *types.Slice: - prog.needMethods(t.Elem(), false) - - case *types.Chan: - prog.needMethods(t.Elem(), false) - - case *types.Map: - prog.needMethods(t.Key(), false) - prog.needMethods(t.Elem(), false) - - case *types.Signature: - if t.Recv() != nil { - panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv())) - } - prog.needMethods(t.Params(), false) - prog.needMethods(t.Results(), false) - - case *types.Named: - // A pointer-to-named type can be derived from a named - // type via reflection. It may have methods too. - prog.needMethods(types.NewPointer(T), false) - - // Consider 'type T struct{S}' where S has methods. - // Reflection provides no way to get from T to struct{S}, - // only to S, so the method set of struct{S} is unwanted, - // so set 'skip' flag during recursion. - prog.needMethods(t.Underlying(), true) - - case *types.Array: - prog.needMethods(t.Elem(), false) - - case *types.Struct: - for i, n := 0, t.NumFields(); i < n; i++ { - prog.needMethods(t.Field(i).Type(), false) - } - - case *types.Tuple: - for i, n := 0, t.Len(); i < n; i++ { - prog.needMethods(t.At(i).Type(), false) - } - - default: - panic(T) - } -} diff --git a/go/ssa/print14.go b/go/ssa/print14.go deleted file mode 100644 index 155d5ecc87..0000000000 --- a/go/ssa/print14.go +++ /dev/null @@ -1,429 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file implements the String() methods for all Value and -// Instruction types. - -import ( - "bytes" - "fmt" - "io" - "reflect" - "sort" - - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -// relName returns the name of v relative to i. -// In most cases, this is identical to v.Name(), but references to -// Functions (including methods) and Globals use RelString and -// all types are displayed with relType, so that only cross-package -// references are package-qualified. -// -func relName(v Value, i Instruction) string { - var from *types.Package - if i != nil { - from = i.Parent().pkg() - } - switch v := v.(type) { - case Member: // *Function or *Global - return v.RelString(from) - case *Const: - return v.RelString(from) - } - return v.Name() -} - -func relType(t types.Type, from *types.Package) string { - return types.TypeString(t, types.RelativeTo(from)) -} - -func relString(m Member, from *types.Package) string { - // NB: not all globals have an Object (e.g. init$guard), - // so use Package().Object not Object.Package(). - if pkg := m.Package().Pkg; pkg != nil && pkg != from { - return fmt.Sprintf("%s.%s", pkg.Path(), m.Name()) - } - return m.Name() -} - -// Value.String() -// -// This method is provided only for debugging. -// It never appears in disassembly, which uses Value.Name(). - -func (v *Parameter) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from)) -} - -func (v *FreeVar) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from)) -} - -func (v *Builtin) String() string { - return fmt.Sprintf("builtin %s", v.Name()) -} - -// Instruction.String() - -func (v *Alloc) String() string { - op := "local" - if v.Heap { - op = "new" - } - from := v.Parent().pkg() - return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment) -} - -func (v *Phi) String() string { - var b bytes.Buffer - b.WriteString("phi [") - for i, edge := range v.Edges { - if i > 0 { - b.WriteString(", ") - } - // Be robust against malformed CFG. - block := -1 - if v.block != nil && i < len(v.block.Preds) { - block = v.block.Preds[i].Index - } - fmt.Fprintf(&b, "%d: ", block) - edgeVal := "" // be robust - if edge != nil { - edgeVal = relName(edge, v) - } - b.WriteString(edgeVal) - } - b.WriteString("]") - if v.Comment != "" { - b.WriteString(" #") - b.WriteString(v.Comment) - } - return b.String() -} - -func printCall(v *CallCommon, prefix string, instr Instruction) string { - var b bytes.Buffer - b.WriteString(prefix) - if !v.IsInvoke() { - b.WriteString(relName(v.Value, instr)) - } else { - fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name()) - } - b.WriteString("(") - for i, arg := range v.Args { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(relName(arg, instr)) - } - if v.Signature().Variadic() { - b.WriteString("...") - } - b.WriteString(")") - return b.String() -} - -func (c *CallCommon) String() string { - return printCall(c, "", nil) -} - -func (v *Call) String() string { - return printCall(&v.Call, "", v) -} - -func (v *BinOp) String() string { - return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v)) -} - -func (v *UnOp) String() string { - return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk)) -} - -func printConv(prefix string, v, x Value) string { - from := v.Parent().pkg() - return fmt.Sprintf("%s %s <- %s (%s)", - prefix, - relType(v.Type(), from), - relType(x.Type(), from), - relName(x, v.(Instruction))) -} - -func (v *ChangeType) String() string { return printConv("changetype", v, v.X) } -func (v *Convert) String() string { return printConv("convert", v, v.X) } -func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) } -func (v *MakeInterface) String() string { return printConv("make", v, v.X) } - -func (v *MakeClosure) String() string { - var b bytes.Buffer - fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v)) - if v.Bindings != nil { - b.WriteString(" [") - for i, c := range v.Bindings { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(relName(c, v)) - } - b.WriteString("]") - } - return b.String() -} - -func (v *MakeSlice) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("make %s %s %s", - relType(v.Type(), from), - relName(v.Len, v), - relName(v.Cap, v)) -} - -func (v *Slice) String() string { - var b bytes.Buffer - b.WriteString("slice ") - b.WriteString(relName(v.X, v)) - b.WriteString("[") - if v.Low != nil { - b.WriteString(relName(v.Low, v)) - } - b.WriteString(":") - if v.High != nil { - b.WriteString(relName(v.High, v)) - } - if v.Max != nil { - b.WriteString(":") - b.WriteString(relName(v.Max, v)) - } - b.WriteString("]") - return b.String() -} - -func (v *MakeMap) String() string { - res := "" - if v.Reserve != nil { - res = relName(v.Reserve, v) - } - from := v.Parent().pkg() - return fmt.Sprintf("make %s %s", relType(v.Type(), from), res) -} - -func (v *MakeChan) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v)) -} - -func (v *FieldAddr) String() string { - st := deref(v.X.Type()).Underlying().(*types.Struct) - // Be robust against a bad index. - 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 { - st := v.X.Type().Underlying().(*types.Struct) - // Be robust against a bad index. - 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 *IndexAddr) String() string { - return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v)) -} - -func (v *Index) String() string { - return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v)) -} - -func (v *Lookup) String() string { - return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk)) -} - -func (v *Range) String() string { - return "range " + relName(v.X, v) -} - -func (v *Next) String() string { - return "next " + relName(v.Iter, v) -} - -func (v *TypeAssert) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from)) -} - -func (v *Extract) String() string { - return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index) -} - -func (s *Jump) String() string { - // Be robust against malformed CFG. - block := -1 - if s.block != nil && len(s.block.Succs) == 1 { - block = s.block.Succs[0].Index - } - return fmt.Sprintf("jump %d", block) -} - -func (s *If) String() string { - // Be robust against malformed CFG. - tblock, fblock := -1, -1 - if s.block != nil && len(s.block.Succs) == 2 { - tblock = s.block.Succs[0].Index - fblock = s.block.Succs[1].Index - } - return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock) -} - -func (s *Go) String() string { - return printCall(&s.Call, "go ", s) -} - -func (s *Panic) String() string { - return "panic " + relName(s.X, s) -} - -func (s *Return) String() string { - var b bytes.Buffer - b.WriteString("return") - for i, r := range s.Results { - if i == 0 { - b.WriteString(" ") - } else { - b.WriteString(", ") - } - b.WriteString(relName(r, s)) - } - return b.String() -} - -func (*RunDefers) String() string { - return "rundefers" -} - -func (s *Send) String() string { - return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s)) -} - -func (s *Defer) String() string { - return printCall(&s.Call, "defer ", s) -} - -func (s *Select) String() string { - var b bytes.Buffer - for i, st := range s.States { - if i > 0 { - b.WriteString(", ") - } - if st.Dir == types.RecvOnly { - b.WriteString("<-") - b.WriteString(relName(st.Chan, s)) - } else { - b.WriteString(relName(st.Chan, s)) - b.WriteString("<-") - b.WriteString(relName(st.Send, s)) - } - } - non := "" - if !s.Blocking { - non = "non" - } - return fmt.Sprintf("select %sblocking [%s]", non, b.String()) -} - -func (s *Store) String() string { - return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s)) -} - -func (s *MapUpdate) String() string { - return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) -} - -func (s *DebugRef) String() string { - p := s.Parent().Prog.Fset.Position(s.Pos()) - var descr interface{} - if s.object != nil { - descr = s.object // e.g. "var x int" - } else { - descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr" - } - var addr string - if s.IsAddr { - addr = "address of " - } - return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name()) -} - -func (p *Package) String() string { - return "package " + p.Pkg.Path() -} - -var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer - -func (p *Package) WriteTo(w io.Writer) (int64, error) { - var buf bytes.Buffer - WritePackage(&buf, p) - n, err := w.Write(buf.Bytes()) - return int64(n), err -} - -// WritePackage writes to buf a human-readable summary of p. -func WritePackage(buf *bytes.Buffer, p *Package) { - fmt.Fprintf(buf, "%s:\n", p) - - var names []string - maxname := 0 - for name := range p.Members { - if l := len(name); l > maxname { - maxname = l - } - names = append(names, name) - } - - from := p.Pkg - sort.Strings(names) - for _, name := range names { - switch mem := p.Members[name].(type) { - case *NamedConst: - fmt.Fprintf(buf, " const %-*s %s = %s\n", - maxname, name, mem.Name(), mem.Value.RelString(from)) - - case *Function: - fmt.Fprintf(buf, " func %-*s %s\n", - maxname, name, relType(mem.Type(), from)) - - case *Type: - fmt.Fprintf(buf, " type %-*s %s\n", - maxname, name, relType(mem.Type().Underlying(), from)) - for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { - fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) - } - - case *Global: - fmt.Fprintf(buf, " var %-*s %s\n", - maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) - } - } - - fmt.Fprintf(buf, "\n") -} - -func commaOk(x bool) string { - if x { - return ",ok" - } - return "" -} diff --git a/go/ssa/sanity14.go b/go/ssa/sanity14.go deleted file mode 100644 index fe4d4ed252..0000000000 --- a/go/ssa/sanity14.go +++ /dev/null @@ -1,522 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// An optional pass for sanity-checking invariants of the SSA representation. -// Currently it checks CFG invariants but little at the instruction level. - -import ( - "fmt" - "io" - "os" - "strings" - - "golang.org/x/tools/go/types" -) - -type sanity struct { - reporter io.Writer - fn *Function - block *BasicBlock - instrs map[Instruction]struct{} - insane bool -} - -// sanityCheck performs integrity checking of the SSA representation -// of the function fn and returns true if it was valid. Diagnostics -// are written to reporter if non-nil, os.Stderr otherwise. Some -// diagnostics are only warnings and do not imply a negative result. -// -// Sanity-checking is intended to facilitate the debugging of code -// transformation passes. -// -func sanityCheck(fn *Function, reporter io.Writer) bool { - if reporter == nil { - reporter = os.Stderr - } - return (&sanity{reporter: reporter}).checkFunction(fn) -} - -// mustSanityCheck is like sanityCheck but panics instead of returning -// a negative result. -// -func mustSanityCheck(fn *Function, reporter io.Writer) { - if !sanityCheck(fn, reporter) { - fn.WriteTo(os.Stderr) - panic("SanityCheck failed") - } -} - -func (s *sanity) diagnostic(prefix, format string, args ...interface{}) { - fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn) - if s.block != nil { - fmt.Fprintf(s.reporter, ", block %s", s.block) - } - io.WriteString(s.reporter, ": ") - fmt.Fprintf(s.reporter, format, args...) - io.WriteString(s.reporter, "\n") -} - -func (s *sanity) errorf(format string, args ...interface{}) { - s.insane = true - s.diagnostic("Error", format, args...) -} - -func (s *sanity) warnf(format string, args ...interface{}) { - s.diagnostic("Warning", format, args...) -} - -// findDuplicate returns an arbitrary basic block that appeared more -// than once in blocks, or nil if all were unique. -func findDuplicate(blocks []*BasicBlock) *BasicBlock { - if len(blocks) < 2 { - return nil - } - if blocks[0] == blocks[1] { - return blocks[0] - } - // Slow path: - m := make(map[*BasicBlock]bool) - for _, b := range blocks { - if m[b] { - return b - } - m[b] = true - } - return nil -} - -func (s *sanity) checkInstr(idx int, instr Instruction) { - switch instr := instr.(type) { - case *If, *Jump, *Return, *Panic: - s.errorf("control flow instruction not at end of block") - case *Phi: - if idx == 0 { - // It suffices to apply this check to just the first phi node. - if dup := findDuplicate(s.block.Preds); dup != nil { - s.errorf("phi node in block with duplicate predecessor %s", dup) - } - } else { - prev := s.block.Instrs[idx-1] - if _, ok := prev.(*Phi); !ok { - s.errorf("Phi instruction follows a non-Phi: %T", prev) - } - } - if ne, np := len(instr.Edges), len(s.block.Preds); ne != np { - s.errorf("phi node has %d edges but %d predecessors", ne, np) - - } else { - for i, e := range instr.Edges { - if e == nil { - s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i]) - } - } - } - - case *Alloc: - if !instr.Heap { - found := false - for _, l := range s.fn.Locals { - if l == instr { - found = true - break - } - } - if !found { - s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr) - } - } - - case *BinOp: - case *Call: - case *ChangeInterface: - case *ChangeType: - case *Convert: - if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok { - if _, ok := instr.Type().Underlying().(*types.Basic); !ok { - s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type()) - } - } - - case *Defer: - case *Extract: - case *Field: - case *FieldAddr: - case *Go: - case *Index: - case *IndexAddr: - case *Lookup: - case *MakeChan: - case *MakeClosure: - numFree := len(instr.Fn.(*Function).FreeVars) - numBind := len(instr.Bindings) - if numFree != numBind { - s.errorf("MakeClosure has %d Bindings for function %s with %d free vars", - numBind, instr.Fn, numFree) - - } - if recv := instr.Type().(*types.Signature).Recv(); recv != nil { - s.errorf("MakeClosure's type includes receiver %s", recv.Type()) - } - - case *MakeInterface: - case *MakeMap: - case *MakeSlice: - case *MapUpdate: - case *Next: - case *Range: - case *RunDefers: - case *Select: - case *Send: - case *Slice: - case *Store: - case *TypeAssert: - case *UnOp: - case *DebugRef: - // TODO(adonovan): implement checks. - default: - panic(fmt.Sprintf("Unknown instruction type: %T", instr)) - } - - if call, ok := instr.(CallInstruction); ok { - if call.Common().Signature() == nil { - s.errorf("nil signature: %s", call) - } - } - - // Check that value-defining instructions have valid types - // and a valid referrer list. - if v, ok := instr.(Value); ok { - t := v.Type() - if t == nil { - s.errorf("no type: %s = %s", v.Name(), v) - } else if t == tRangeIter { - // not a proper type; ignore. - } else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { - s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t) - } - s.checkReferrerList(v) - } - - // Untyped constants are legal as instruction Operands(), - // for example: - // _ = "foo"[0] - // or: - // if wordsize==64 {...} - - // All other non-Instruction Values can be found via their - // enclosing Function or Package. -} - -func (s *sanity) checkFinalInstr(idx int, instr Instruction) { - switch instr := instr.(type) { - case *If: - if nsuccs := len(s.block.Succs); nsuccs != 2 { - s.errorf("If-terminated block has %d successors; expected 2", nsuccs) - return - } - if s.block.Succs[0] == s.block.Succs[1] { - s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0]) - return - } - - case *Jump: - if nsuccs := len(s.block.Succs); nsuccs != 1 { - s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs) - return - } - - case *Return: - if nsuccs := len(s.block.Succs); nsuccs != 0 { - s.errorf("Return-terminated block has %d successors; expected none", nsuccs) - return - } - if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na { - s.errorf("%d-ary return in %d-ary function", na, nf) - } - - case *Panic: - if nsuccs := len(s.block.Succs); nsuccs != 0 { - s.errorf("Panic-terminated block has %d successors; expected none", nsuccs) - return - } - - default: - s.errorf("non-control flow instruction at end of block") - } -} - -func (s *sanity) checkBlock(b *BasicBlock, index int) { - s.block = b - - if b.Index != index { - s.errorf("block has incorrect Index %d", b.Index) - } - if b.parent != s.fn { - s.errorf("block has incorrect parent %s", b.parent) - } - - // Check all blocks are reachable. - // (The entry block is always implicitly reachable, - // as is the Recover block, if any.) - if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 { - s.warnf("unreachable block") - if b.Instrs == nil { - // Since this block is about to be pruned, - // tolerating transient problems in it - // simplifies other optimizations. - return - } - } - - // Check predecessor and successor relations are dual, - // and that all blocks in CFG belong to same function. - for _, a := range b.Preds { - found := false - for _, bb := range a.Succs { - if bb == b { - found = true - break - } - } - if !found { - s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs) - } - if a.parent != s.fn { - s.errorf("predecessor %s belongs to different function %s", a, a.parent) - } - } - for _, c := range b.Succs { - found := false - for _, bb := range c.Preds { - if bb == b { - found = true - break - } - } - if !found { - s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds) - } - if c.parent != s.fn { - s.errorf("successor %s belongs to different function %s", c, c.parent) - } - } - - // Check each instruction is sane. - n := len(b.Instrs) - if n == 0 { - s.errorf("basic block contains no instructions") - } - var rands [10]*Value // reuse storage - for j, instr := range b.Instrs { - if instr == nil { - s.errorf("nil instruction at index %d", j) - continue - } - if b2 := instr.Block(); b2 == nil { - s.errorf("nil Block() for instruction at index %d", j) - continue - } else if b2 != b { - s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j) - continue - } - if j < n-1 { - s.checkInstr(j, instr) - } else { - s.checkFinalInstr(j, instr) - } - - // Check Instruction.Operands. - operands: - for i, op := range instr.Operands(rands[:0]) { - if op == nil { - s.errorf("nil operand pointer %d of %s", i, instr) - continue - } - val := *op - if val == nil { - continue // a nil operand is ok - } - - // Check that "untyped" types only appear on constant operands. - if _, ok := (*op).(*Const); !ok { - if basic, ok := (*op).Type().(*types.Basic); ok { - if basic.Info()&types.IsUntyped != 0 { - s.errorf("operand #%d of %s is untyped: %s", i, instr, basic) - } - } - } - - // Check that Operands that are also Instructions belong to same function. - // TODO(adonovan): also check their block dominates block b. - if val, ok := val.(Instruction); ok { - if val.Parent() != s.fn { - s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent()) - } - } - - // Check that each function-local operand of - // instr refers back to instr. (NB: quadratic) - switch val := val.(type) { - case *Const, *Global, *Builtin: - continue // not local - case *Function: - if val.parent == nil { - continue // only anon functions are local - } - } - - // TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined. - - if refs := val.Referrers(); refs != nil { - for _, ref := range *refs { - if ref == instr { - continue operands - } - } - s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val) - } else { - s.errorf("operand %d of %s (%s) has no referrers", i, instr, val) - } - } - } -} - -func (s *sanity) checkReferrerList(v Value) { - refs := v.Referrers() - if refs == nil { - s.errorf("%s has missing referrer list", v.Name()) - return - } - for i, ref := range *refs { - if _, ok := s.instrs[ref]; !ok { - s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref) - } - } -} - -func (s *sanity) checkFunction(fn *Function) bool { - // TODO(adonovan): check Function invariants: - // - check params match signature - // - check transient fields are nil - // - warn if any fn.Locals do not appear among block instructions. - s.fn = fn - if fn.Prog == nil { - s.errorf("nil Prog") - } - - fn.String() // must not crash - fn.RelString(fn.pkg()) // must not crash - - // All functions have a package, except delegates (which are - // shared across packages, or duplicated as weak symbols in a - // separate-compilation model), and error.Error. - if fn.Pkg == nil { - if strings.HasPrefix(fn.Synthetic, "wrapper ") || - strings.HasPrefix(fn.Synthetic, "bound ") || - strings.HasPrefix(fn.Synthetic, "thunk ") || - strings.HasSuffix(fn.name, "Error") { - // ok - } else { - s.errorf("nil Pkg") - } - } - if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn { - s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn) - } - for i, l := range fn.Locals { - if l.Parent() != fn { - s.errorf("Local %s at index %d has wrong parent", l.Name(), i) - } - if l.Heap { - s.errorf("Local %s at index %d has Heap flag set", l.Name(), i) - } - } - // Build the set of valid referrers. - s.instrs = make(map[Instruction]struct{}) - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - s.instrs[instr] = struct{}{} - } - } - for i, p := range fn.Params { - if p.Parent() != fn { - s.errorf("Param %s at index %d has wrong parent", p.Name(), i) - } - s.checkReferrerList(p) - } - for i, fv := range fn.FreeVars { - if fv.Parent() != fn { - s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i) - } - s.checkReferrerList(fv) - } - - if fn.Blocks != nil && len(fn.Blocks) == 0 { - // Function _had_ blocks (so it's not external) but - // they were "optimized" away, even the entry block. - s.errorf("Blocks slice is non-nil but empty") - } - for i, b := range fn.Blocks { - if b == nil { - s.warnf("nil *BasicBlock at f.Blocks[%d]", i) - continue - } - s.checkBlock(b, i) - } - if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover { - s.errorf("Recover block is not in Blocks slice") - } - - s.block = nil - for i, anon := range fn.AnonFuncs { - if anon.Parent() != fn { - s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent()) - } - } - s.fn = nil - return !s.insane -} - -// sanityCheckPackage checks invariants of packages upon creation. -// It does not require that the package is built. -// Unlike sanityCheck (for functions), it just panics at the first error. -func sanityCheckPackage(pkg *Package) { - if pkg.Pkg == nil { - panic(fmt.Sprintf("Package %s has no Object", pkg)) - } - pkg.String() // must not crash - - for name, mem := range pkg.Members { - if name != mem.Name() { - panic(fmt.Sprintf("%s: %T.Name() = %s, want %s", - pkg.Pkg.Path(), mem, mem.Name(), name)) - } - obj := mem.Object() - if obj == nil { - // This check is sound because fields - // {Global,Function}.object have type - // types.Object. (If they were declared as - // *types.{Var,Func}, we'd have a non-empty - // interface containing a nil pointer.) - - continue // not all members have typechecker objects - } - if obj.Name() != name { - if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") { - // Ok. The name of a declared init function varies between - // its types.Func ("init") and its ssa.Function ("init#%d"). - } else { - panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s", - pkg.Pkg.Path(), mem, obj.Name(), name)) - } - } - if obj.Pos() != mem.Pos() { - panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos())) - } - } -} diff --git a/go/ssa/source14.go b/go/ssa/source14.go deleted file mode 100644 index af93136cef..0000000000 --- a/go/ssa/source14.go +++ /dev/null @@ -1,296 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines utilities for working with source positions -// or source-level named entities ("objects"). - -// TODO(adonovan): test that {Value,Instruction}.Pos() positions match -// the originating syntax, as specified. - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/types" -) - -// EnclosingFunction returns the function that contains the syntax -// node denoted by path. -// -// Syntax associated with package-level variable specifications is -// enclosed by the package's init() function. -// -// Returns nil if not found; reasons might include: -// - the node is not enclosed by any function. -// - the node is within an anonymous function (FuncLit) and -// its SSA function has not been created yet -// (pkg.Build() has not yet been called). -// -func EnclosingFunction(pkg *Package, path []ast.Node) *Function { - // Start with package-level function... - fn := findEnclosingPackageLevelFunction(pkg, path) - if fn == nil { - return nil // not in any function - } - - // ...then walk down the nested anonymous functions. - n := len(path) -outer: - for i := range path { - if lit, ok := path[n-1-i].(*ast.FuncLit); ok { - for _, anon := range fn.AnonFuncs { - if anon.Pos() == lit.Type.Func { - fn = anon - continue outer - } - } - // SSA function not found: - // - package not yet built, or maybe - // - builder skipped FuncLit in dead block - // (in principle; but currently the Builder - // generates even dead FuncLits). - return nil - } - } - return fn -} - -// HasEnclosingFunction returns true if the AST node denoted by path -// is contained within the declaration of some function or -// package-level variable. -// -// Unlike EnclosingFunction, the behaviour of this function does not -// depend on whether SSA code for pkg has been built, so it can be -// used to quickly reject check inputs that will cause -// EnclosingFunction to fail, prior to SSA building. -// -func HasEnclosingFunction(pkg *Package, path []ast.Node) bool { - return findEnclosingPackageLevelFunction(pkg, path) != nil -} - -// findEnclosingPackageLevelFunction returns the Function -// corresponding to the package-level function enclosing path. -// -func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function { - if n := len(path); n >= 2 { // [... {Gen,Func}Decl File] - switch decl := path[n-2].(type) { - case *ast.GenDecl: - if decl.Tok == token.VAR && n >= 3 { - // Package-level 'var' initializer. - return pkg.init - } - - case *ast.FuncDecl: - if decl.Recv == nil && decl.Name.Name == "init" { - // Explicit init() function. - for _, b := range pkg.init.Blocks { - for _, instr := range b.Instrs { - if instr, ok := instr.(*Call); ok { - if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos { - return callee - } - } - } - } - // Hack: return non-nil when SSA is not yet - // built so that HasEnclosingFunction works. - return pkg.init - } - // Declared function/method. - return findNamedFunc(pkg, decl.Name.NamePos) - } - } - return nil // not in any function -} - -// findNamedFunc returns the named function whose FuncDecl.Ident is at -// position pos. -// -func findNamedFunc(pkg *Package, pos token.Pos) *Function { - // Look at all package members and method sets of named types. - // Not very efficient. - for _, mem := range pkg.Members { - switch mem := mem.(type) { - case *Function: - if mem.Pos() == pos { - return mem - } - case *Type: - mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) - for i, n := 0, mset.Len(); i < n; i++ { - // Don't call Program.Method: avoid creating wrappers. - obj := mset.At(i).Obj().(*types.Func) - if obj.Pos() == pos { - return pkg.values[obj].(*Function) - } - } - } - } - return nil -} - -// ValueForExpr returns the SSA Value that corresponds to non-constant -// expression e. -// -// It returns nil if no value was found, e.g. -// - the expression is not lexically contained within f; -// - f was not built with debug information; or -// - e is a constant expression. (For efficiency, no debug -// information is stored for constants. Use -// go/types.Info.Types[e].Value instead.) -// - e is a reference to nil or a built-in function. -// - the value was optimised away. -// -// If e is an addressable expression used in an lvalue context, -// value is the address denoted by e, and isAddr is true. -// -// The types of e (or &e, if isAddr) and the result are equal -// (modulo "untyped" bools resulting from comparisons). -// -// (Tip: to find the ssa.Value given a source position, use -// importer.PathEnclosingInterval to locate the ast.Node, then -// EnclosingFunction to locate the Function, then ValueForExpr to find -// the ssa.Value.) -// -func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) { - if f.debugInfo() { // (opt) - e = unparen(e) - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - if ref, ok := instr.(*DebugRef); ok { - if ref.Expr == e { - return ref.X, ref.IsAddr - } - } - } - } - } - return -} - -// --- Lookup functions for source-level named entities (types.Objects) --- - -// Package returns the SSA Package corresponding to the specified -// type-checker package object. -// It returns nil if no such SSA package has been created. -// -func (prog *Program) Package(obj *types.Package) *Package { - return prog.packages[obj] -} - -// packageLevelValue returns the package-level value corresponding to -// the specified named object, which may be a package-level const -// (*Const), var (*Global) or func (*Function) of some package in -// prog. It returns nil if the object is not found. -// -func (prog *Program) packageLevelValue(obj types.Object) Value { - if pkg, ok := prog.packages[obj.Pkg()]; ok { - return pkg.values[obj] - } - return nil -} - -// FuncValue returns the concrete Function denoted by the source-level -// named function obj, or nil if obj denotes an interface method. -// -// TODO(adonovan): check the invariant that obj.Type() matches the -// result's Signature, both in the params/results and in the receiver. -// -func (prog *Program) FuncValue(obj *types.Func) *Function { - fn, _ := prog.packageLevelValue(obj).(*Function) - return fn -} - -// ConstValue returns the SSA Value denoted by the source-level named -// constant obj. -// -func (prog *Program) ConstValue(obj *types.Const) *Const { - // TODO(adonovan): opt: share (don't reallocate) - // Consts for const objects and constant ast.Exprs. - - // Universal constant? {true,false,nil} - if obj.Parent() == types.Universe { - return NewConst(obj.Val(), obj.Type()) - } - // Package-level named constant? - if v := prog.packageLevelValue(obj); v != nil { - return v.(*Const) - } - return NewConst(obj.Val(), obj.Type()) -} - -// VarValue returns the SSA Value that corresponds to a specific -// identifier denoting the source-level named variable obj. -// -// VarValue returns nil if a local variable was not found, perhaps -// because its package was not built, the debug information was not -// requested during SSA construction, or the value was optimized away. -// -// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval), -// and that ident must resolve to obj. -// -// pkg is the package enclosing the reference. (A reference to a var -// always occurs within a function, so we need to know where to find it.) -// -// If the identifier is a field selector and its base expression is -// non-addressable, then VarValue returns the value of that field. -// For example: -// func f() struct {x int} -// f().x // VarValue(x) returns a *Field instruction of type int -// -// All other identifiers denote addressable locations (variables). -// For them, VarValue may return either the variable's address or its -// value, even when the expression is evaluated only for its value; the -// situation is reported by isAddr, the second component of the result. -// -// If !isAddr, the returned value is the one associated with the -// specific identifier. For example, -// var x int // VarValue(x) returns Const 0 here -// x = 1 // VarValue(x) returns Const 1 here -// -// It is not specified whether the value or the address is returned in -// any particular case, as it may depend upon optimizations performed -// during SSA code generation, such as registerization, constant -// folding, avoidance of materialization of subexpressions, etc. -// -func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) { - // All references to a var are local to some function, possibly init. - fn := EnclosingFunction(pkg, ref) - if fn == nil { - return // e.g. def of struct field; SSA not built? - } - - id := ref[0].(*ast.Ident) - - // Defining ident of a parameter? - if id.Pos() == obj.Pos() { - for _, param := range fn.Params { - if param.Object() == obj { - return param, false - } - } - } - - // Other ident? - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - if dr, ok := instr.(*DebugRef); ok { - if dr.Pos() == id.Pos() { - return dr.X, dr.IsAddr - } - } - } - } - - // Defining ident of package-level var? - if v := prog.packageLevelValue(obj); v != nil { - return v.(*Global), true - } - - return // e.g. debug info not requested, or var optimized away -} diff --git a/go/ssa/source14_test.go b/go/ssa/source14_test.go deleted file mode 100644 index cd7b268d05..0000000000 --- a/go/ssa/source14_test.go +++ /dev/null @@ -1,395 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa_test - -// This file defines tests of source-level debugging utilities. - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "os" - "regexp" - "runtime" - "strings" - "testing" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" -) - -func TestObjValueLookup(t *testing.T) { - if runtime.GOOS == "android" { - t.Skipf("no testdata directory on %s", runtime.GOOS) - } - - conf := loader.Config{ParserMode: parser.ParseComments} - f, err := conf.ParseFile("testdata/objlookup.go", nil) - if err != nil { - t.Error(err) - return - } - conf.CreateFromFiles("main", f) - - // Maps each var Ident (represented "name:linenum") to the - // kind of ssa.Value we expect (represented "Constant", "&Alloc"). - expectations := make(map[string]string) - - // Find all annotations of form x::BinOp, &y::Alloc, etc. - re := regexp.MustCompile(`(\b|&)?(\w*)::(\w*)\b`) - for _, c := range f.Comments { - text := c.Text() - pos := conf.Fset.Position(c.Pos()) - for _, m := range re.FindAllStringSubmatch(text, -1) { - key := fmt.Sprintf("%s:%d", m[2], pos.Line) - value := m[1] + m[3] - expectations[key] = value - } - } - - iprog, err := conf.Load() - if err != nil { - t.Error(err) - return - } - - prog := ssautil.CreateProgram(iprog, 0 /*|ssa.PrintFunctions*/) - mainInfo := iprog.Created[0] - mainPkg := prog.Package(mainInfo.Pkg) - mainPkg.SetDebugMode(true) - mainPkg.Build() - - var varIds []*ast.Ident - var varObjs []*types.Var - for id, obj := range mainInfo.Defs { - // Check invariants for func and const objects. - switch obj := obj.(type) { - case *types.Func: - checkFuncValue(t, prog, obj) - - case *types.Const: - checkConstValue(t, prog, obj) - - case *types.Var: - if id.Name == "_" { - continue - } - varIds = append(varIds, id) - varObjs = append(varObjs, obj) - } - } - for id, obj := range mainInfo.Uses { - if obj, ok := obj.(*types.Var); ok { - varIds = append(varIds, id) - varObjs = append(varObjs, obj) - } - } - - // Check invariants for var objects. - // The result varies based on the specific Ident. - for i, id := range varIds { - obj := varObjs[i] - ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos()) - pos := prog.Fset.Position(id.Pos()) - exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)] - if exp == "" { - t.Errorf("%s: no expectation for var ident %s ", pos, id.Name) - continue - } - wantAddr := false - if exp[0] == '&' { - wantAddr = true - exp = exp[1:] - } - checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr) - } -} - -func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { - fn := prog.FuncValue(obj) - // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging - if fn == nil { - if obj.Name() != "interfaceMethod" { - t.Errorf("FuncValue(%s) == nil", obj) - } - return - } - if fnobj := fn.Object(); fnobj != obj { - t.Errorf("FuncValue(%s).Object() == %s; value was %s", - obj, fnobj, fn.Name()) - return - } - if !types.Identical(fn.Type(), obj.Type()) { - t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) - return - } -} - -func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { - c := prog.ConstValue(obj) - // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging - if c == nil { - t.Errorf("ConstValue(%s) == nil", obj) - return - } - if !types.Identical(c.Type(), obj.Type()) { - t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) - return - } - if obj.Name() != "nil" { - if !exact.Compare(c.Value, token.EQL, obj.Val()) { - t.Errorf("ConstValue(%s).Value (%s) != %s", - obj, c.Value, obj.Val()) - return - } - } -} - -func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) { - // The prefix of all assertions messages. - prefix := fmt.Sprintf("VarValue(%s @ L%d)", - obj, prog.Fset.Position(ref[0].Pos()).Line) - - v, gotAddr := prog.VarValue(obj, pkg, ref) - - // Kind is the concrete type of the ssa Value. - gotKind := "nil" - if v != nil { - gotKind = fmt.Sprintf("%T", v)[len("*ssa."):] - } - - // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging - - // Check the kinds match. - // "nil" indicates expected failure (e.g. optimized away). - if expKind != gotKind { - t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind) - } - - // Check the types match. - // If wantAddr, the expected type is the object's address. - if v != nil { - expType := obj.Type() - if wantAddr { - expType = types.NewPointer(expType) - if !gotAddr { - t.Errorf("%s: got value, want address", prefix) - } - } else if gotAddr { - t.Errorf("%s: got address, want value", prefix) - } - if !types.Identical(v.Type(), expType) { - t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) - } - } -} - -// Ensure that, in debug mode, we can determine the ssa.Value -// corresponding to every ast.Expr. -func TestValueForExpr(t *testing.T) { - if runtime.GOOS == "android" { - t.Skipf("no testdata dir on %s", runtime.GOOS) - } - - conf := loader.Config{ParserMode: parser.ParseComments} - f, err := conf.ParseFile("testdata/valueforexpr.go", nil) - if err != nil { - t.Error(err) - return - } - conf.CreateFromFiles("main", f) - - iprog, err := conf.Load() - if err != nil { - t.Error(err) - return - } - - mainInfo := iprog.Created[0] - - prog := ssautil.CreateProgram(iprog, 0) - mainPkg := prog.Package(mainInfo.Pkg) - mainPkg.SetDebugMode(true) - mainPkg.Build() - - if false { - // debugging - for _, mem := range mainPkg.Members { - if fn, ok := mem.(*ssa.Function); ok { - fn.WriteTo(os.Stderr) - } - } - } - - // Find the actual AST node for each canonical position. - parenExprByPos := make(map[token.Pos]*ast.ParenExpr) - ast.Inspect(f, func(n ast.Node) bool { - if n != nil { - if e, ok := n.(*ast.ParenExpr); ok { - parenExprByPos[e.Pos()] = e - } - } - return true - }) - - // Find all annotations of form /*@kind*/. - for _, c := range f.Comments { - text := strings.TrimSpace(c.Text()) - if text == "" || text[0] != '@' { - continue - } - text = text[1:] - pos := c.End() + 1 - position := prog.Fset.Position(pos) - var e ast.Expr - if target := parenExprByPos[pos]; target == nil { - t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text) - continue - } else { - e = target.X - } - - path, _ := astutil.PathEnclosingInterval(f, pos, pos) - if path == nil { - t.Errorf("%s: can't find AST path from root to comment: %s", position, text) - continue - } - - fn := ssa.EnclosingFunction(mainPkg, path) - if fn == nil { - t.Errorf("%s: can't find enclosing function", position) - continue - } - - v, gotAddr := fn.ValueForExpr(e) // (may be nil) - got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.") - if want := text; got != want { - t.Errorf("%s: got value %q, want %q", position, got, want) - } - if v != nil { - T := v.Type() - if gotAddr { - T = T.Underlying().(*types.Pointer).Elem() // deref - } - if !types.Identical(T, mainInfo.TypeOf(e)) { - t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T) - } - } - } -} - -// findInterval parses input and returns the [start, end) positions of -// the first occurrence of substr in input. f==nil indicates failure; -// an error has already been reported in that case. -// -func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) { - f, err := parser.ParseFile(fset, "", input, 0) - if err != nil { - t.Errorf("parse error: %s", err) - return - } - - i := strings.Index(input, substr) - if i < 0 { - t.Errorf("%q is not a substring of input", substr) - f = nil - return - } - - filePos := fset.File(f.Package) - return f, filePos.Pos(i), filePos.Pos(i + len(substr)) -} - -func TestEnclosingFunction(t *testing.T) { - tests := []struct { - input string // the input file - substr string // first occurrence of this string denotes interval - fn string // name of expected containing function - }{ - // We use distinctive numbers as syntactic landmarks. - - // Ordinary function: - {`package main - func f() { println(1003) }`, - "100", "main.f"}, - // Methods: - {`package main - type T int - func (t T) f() { println(200) }`, - "200", "(main.T).f"}, - // Function literal: - {`package main - func f() { println(func() { print(300) }) }`, - "300", "main.f$1"}, - // Doubly nested - {`package main - func f() { println(func() { print(func() { print(350) })})}`, - "350", "main.f$1$1"}, - // Implicit init for package-level var initializer. - {"package main; var a = 400", "400", "main.init"}, - // No code for constants: - {"package main; const a = 500", "500", "(none)"}, - // Explicit init() - {"package main; func init() { println(600) }", "600", "main.init#1"}, - // Multiple explicit init functions: - {`package main - func init() { println("foo") } - func init() { println(800) }`, - "800", "main.init#2"}, - // init() containing FuncLit. - {`package main - func init() { println(func(){print(900)}) }`, - "900", "main.init#1$1"}, - } - for _, test := range tests { - conf := loader.Config{Fset: token.NewFileSet()} - f, start, end := findInterval(t, conf.Fset, test.input, test.substr) - if f == nil { - continue - } - path, exact := astutil.PathEnclosingInterval(f, start, end) - if !exact { - t.Errorf("EnclosingFunction(%q) not exact", test.substr) - continue - } - - conf.CreateFromFiles("main", f) - - iprog, err := conf.Load() - if err != nil { - t.Error(err) - continue - } - prog := ssautil.CreateProgram(iprog, 0) - pkg := prog.Package(iprog.Created[0].Pkg) - pkg.Build() - - name := "(none)" - fn := ssa.EnclosingFunction(pkg, path) - if fn != nil { - name = fn.String() - } - - if name != test.fn { - t.Errorf("EnclosingFunction(%q in %q) got %s, want %s", - test.substr, test.input, name, test.fn) - continue - } - - // While we're here: test HasEnclosingFunction. - if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) { - t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v", - test.substr, test.input, has, fn != nil) - continue - } - } -} diff --git a/go/ssa/ssa14.go b/go/ssa/ssa14.go deleted file mode 100644 index dcc62daabb..0000000000 --- a/go/ssa/ssa14.go +++ /dev/null @@ -1,1702 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This package defines a high-level intermediate representation for -// Go programs using static single-assignment (SSA) form. - -import ( - "fmt" - "go/ast" - "go/token" - "sync" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -// A Program is a partial or complete Go program converted to SSA form. -type Program struct { - Fset *token.FileSet // position information for the files of this Program - imported map[string]*Package // all importable Packages, keyed by import path - packages map[*types.Package]*Package // all loaded Packages, keyed by object - mode BuilderMode // set of mode bits for SSA construction - MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets - - methodsMu sync.Mutex // guards the following maps: - methodSets typeutil.Map // maps type to its concrete methodSet - runtimeTypes typeutil.Map // types for which rtypes are needed - canon typeutil.Map // type canonicalization map - bounds map[*types.Func]*Function // bounds for curried x.Method closures - thunks map[selectionKey]*Function // thunks for T.Method expressions -} - -// A Package is a single analyzed Go package containing Members for -// all package-level functions, variables, constants and types it -// declares. These may be accessed directly via Members, or via the -// type-specific accessor methods Func, Type, Var and Const. -// -// Members also contains entries for "init" (the synthetic package -// initializer) and "init#%d", the nth declared init function, -// and unspecified other things too. -// -type Package struct { - Prog *Program // the owning program - Pkg *types.Package // the corresponding go/types.Package - Members map[string]Member // all package members keyed by name (incl. init and init#%d) - values map[types.Object]Value // package members (incl. types and methods), keyed by object - init *Function // Func("init"); the package's init function - debug bool // include full debug info in this package - - // The following fields are set transiently, then cleared - // after building. - buildOnce sync.Once // ensures package building occurs once - ninit int32 // number of init functions - info *types.Info // package type information - files []*ast.File // package ASTs -} - -// A Member is a member of a Go package, implemented by *NamedConst, -// *Global, *Function, or *Type; they are created by package-level -// const, var, func and type declarations respectively. -// -type Member interface { - Name() string // declared name of the package member - String() string // package-qualified name of the package member - RelString(*types.Package) string // like String, but relative refs are unqualified - Object() types.Object // typechecker's object for this member, if any - Pos() token.Pos // position of member's declaration, if known - Type() types.Type // type of the package member - Token() token.Token // token.{VAR,FUNC,CONST,TYPE} - Package() *Package // the containing package -} - -// A Type is a Member of a Package representing a package-level named type. -// -// Type() returns a *types.Named. -// -type Type struct { - object *types.TypeName - pkg *Package -} - -// A NamedConst is a Member of a Package representing a package-level -// named constant. -// -// Pos() returns the position of the declaring ast.ValueSpec.Names[*] -// identifier. -// -// NB: a NamedConst is not a Value; it contains a constant Value, which -// it augments with the name and position of its 'const' declaration. -// -type NamedConst struct { - object *types.Const - Value *Const - pos token.Pos - pkg *Package -} - -// A Value is an SSA value that can be referenced by an instruction. -type Value interface { - // Name returns the name of this value, and determines how - // this Value appears when used as an operand of an - // Instruction. - // - // This is the same as the source name for Parameters, - // Builtins, Functions, FreeVars, Globals. - // For constants, it is a representation of the constant's value - // and type. For all other Values this is the name of the - // virtual register defined by the instruction. - // - // The name of an SSA Value is not semantically significant, - // and may not even be unique within a function. - Name() string - - // If this value is an Instruction, String returns its - // disassembled form; otherwise it returns unspecified - // human-readable information about the Value, such as its - // kind, name and type. - String() string - - // Type returns the type of this value. Many instructions - // (e.g. IndexAddr) change their behaviour depending on the - // types of their operands. - Type() types.Type - - // Parent returns the function to which this Value belongs. - // It returns nil for named Functions, Builtin, Const and Global. - Parent() *Function - - // Referrers returns the list of instructions that have this - // value as one of their operands; it may contain duplicates - // if an instruction has a repeated operand. - // - // Referrers actually returns a pointer through which the - // caller may perform mutations to the object's state. - // - // Referrers is currently only defined if Parent()!=nil, - // i.e. for the function-local values FreeVar, Parameter, - // Functions (iff anonymous) and all value-defining instructions. - // It returns nil for named Functions, Builtin, Const and Global. - // - // Instruction.Operands contains the inverse of this relation. - Referrers() *[]Instruction - - // Pos returns the location of the AST token most closely - // associated with the operation that gave rise to this value, - // or token.NoPos if it was not explicit in the source. - // - // For each ast.Node type, a particular token is designated as - // the closest location for the expression, e.g. the Lparen - // for an *ast.CallExpr. This permits a compact but - // approximate mapping from Values to source positions for use - // in diagnostic messages, for example. - // - // (Do not use this position to determine which Value - // corresponds to an ast.Expr; use Function.ValueForExpr - // instead. NB: it requires that the function was built with - // debug information.) - Pos() token.Pos -} - -// An Instruction is an SSA instruction that computes a new Value or -// has some effect. -// -// An Instruction that defines a value (e.g. BinOp) also implements -// the Value interface; an Instruction that only has an effect (e.g. Store) -// does not. -// -type Instruction interface { - // String returns the disassembled form of this value. - // - // Examples of Instructions that are Values: - // "x + y" (BinOp) - // "len([])" (Call) - // Note that the name of the Value is not printed. - // - // Examples of Instructions that are not Values: - // "return x" (Return) - // "*y = x" (Store) - // - // (The separation Value.Name() from Value.String() is useful - // for some analyses which distinguish the operation from the - // value it defines, e.g., 'y = local int' is both an allocation - // of memory 'local int' and a definition of a pointer y.) - String() string - - // Parent returns the function to which this instruction - // belongs. - Parent() *Function - - // Block returns the basic block to which this instruction - // belongs. - Block() *BasicBlock - - // setBlock sets the basic block to which this instruction belongs. - setBlock(*BasicBlock) - - // Operands returns the operands of this instruction: the - // set of Values it references. - // - // Specifically, it appends their addresses to rands, a - // user-provided slice, and returns the resulting slice, - // permitting avoidance of memory allocation. - // - // The operands are appended in undefined order, but the order - // is consistent for a given Instruction; the addresses are - // always non-nil but may point to a nil Value. Clients may - // store through the pointers, e.g. to effect a value - // renaming. - // - // Value.Referrers is a subset of the inverse of this - // relation. (Referrers are not tracked for all types of - // Values.) - Operands(rands []*Value) []*Value - - // Pos returns the location of the AST token most closely - // associated with the operation that gave rise to this - // instruction, or token.NoPos if it was not explicit in the - // source. - // - // For each ast.Node type, a particular token is designated as - // the closest location for the expression, e.g. the Go token - // for an *ast.GoStmt. This permits a compact but approximate - // mapping from Instructions to source positions for use in - // diagnostic messages, for example. - // - // (Do not use this position to determine which Instruction - // corresponds to an ast.Expr; see the notes for Value.Pos. - // This position may be used to determine which non-Value - // Instruction corresponds to some ast.Stmts, but not all: If - // and Jump instructions have no Pos(), for example.) - Pos() token.Pos -} - -// A Node is a node in the SSA value graph. Every concrete type that -// implements Node is also either a Value, an Instruction, or both. -// -// Node contains the methods common to Value and Instruction, plus the -// Operands and Referrers methods generalized to return nil for -// non-Instructions and non-Values, respectively. -// -// Node is provided to simplify SSA graph algorithms. Clients should -// use the more specific and informative Value or Instruction -// interfaces where appropriate. -// -type Node interface { - // Common methods: - String() string - Pos() token.Pos - Parent() *Function - - // Partial methods: - Operands(rands []*Value) []*Value // nil for non-Instructions - Referrers() *[]Instruction // nil for non-Values -} - -// Function represents the parameters, results, and code of a function -// or method. -// -// If Blocks is nil, this indicates an external function for which no -// Go source code is available. In this case, FreeVars and Locals -// are nil too. Clients performing whole-program analysis must -// handle external functions specially. -// -// Blocks contains the function's control-flow graph (CFG). -// Blocks[0] is the function entry point; block order is not otherwise -// semantically significant, though it may affect the readability of -// the disassembly. -// To iterate over the blocks in dominance order, use DomPreorder(). -// -// Recover is an optional second entry point to which control resumes -// after a recovered panic. The Recover block may contain only a return -// statement, preceded by a load of the function's named return -// parameters, if any. -// -// A nested function (Parent()!=nil) that refers to one or more -// lexically enclosing local variables ("free variables") has FreeVars. -// Such functions cannot be called directly but require a -// value created by MakeClosure which, via its Bindings, supplies -// values for these parameters. -// -// If the function is a method (Signature.Recv() != nil) then the first -// element of Params is the receiver parameter. -// -// A Go package may declare many functions called "init". -// For each one, Object().Name() returns "init" but Name() returns -// "init#1", etc, in declaration order. -// -// Pos() returns the declaring ast.FuncLit.Type.Func or the position -// of the ast.FuncDecl.Name, if the function was explicit in the -// source. Synthetic wrappers, for which Synthetic != "", may share -// the same position as the function they wrap. -// Syntax.Pos() always returns the position of the declaring "func" token. -// -// Type() returns the function's Signature. -// -type Function struct { - name string - object types.Object // a declared *types.Func or one of its wrappers - method *types.Selection // info about provenance of synthetic methods - Signature *types.Signature - pos token.Pos - - Synthetic string // provenance of synthetic function; "" for true source functions - syntax ast.Node // *ast.Func{Decl,Lit}; replaced with simple ast.Node after build, unless debug mode - parent *Function // enclosing function if anon; nil if global - Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) - Prog *Program // enclosing program - Params []*Parameter // function parameters; for methods, includes receiver - FreeVars []*FreeVar // free variables whose values must be supplied by closure - Locals []*Alloc // local variables of this function - Blocks []*BasicBlock // basic blocks of the function; nil => external - Recover *BasicBlock // optional; control transfers here after recovered panic - AnonFuncs []*Function // anonymous functions directly beneath this one - referrers []Instruction // referring instructions (iff Parent() != nil) - - // The following fields are set transiently during building, - // then cleared. - currentBlock *BasicBlock // where to emit code - objects map[types.Object]Value // addresses of local variables - namedResults []*Alloc // tuple of named results - targets *targets // linked stack of branch targets - lblocks map[*ast.Object]*lblock // labelled blocks -} - -// BasicBlock represents an SSA basic block. -// -// The final element of Instrs is always an explicit transfer of -// control (If, Jump, Return, or Panic). -// -// A block may contain no Instructions only if it is unreachable, -// i.e., Preds is nil. Empty blocks are typically pruned. -// -// BasicBlocks and their Preds/Succs relation form a (possibly cyclic) -// graph independent of the SSA Value graph: the control-flow graph or -// CFG. It is illegal for multiple edges to exist between the same -// pair of blocks. -// -// Each BasicBlock is also a node in the dominator tree of the CFG. -// The tree may be navigated using Idom()/Dominees() and queried using -// Dominates(). -// -// The order of Preds and Succs is significant (to Phi and If -// instructions, respectively). -// -type BasicBlock struct { - Index int // index of this block within Parent().Blocks - Comment string // optional label; no semantic significance - parent *Function // parent function - Instrs []Instruction // instructions in order - Preds, Succs []*BasicBlock // predecessors and successors - succs2 [2]*BasicBlock // initial space for Succs - dom domInfo // dominator tree info - gaps int // number of nil Instrs (transient) - rundefers int // number of rundefers (transient) -} - -// Pure values ---------------------------------------- - -// A FreeVar represents a free variable of the function to which it -// belongs. -// -// FreeVars are used to implement anonymous functions, whose free -// variables are lexically captured in a closure formed by -// MakeClosure. The value of such a free var is an Alloc or another -// FreeVar and is considered a potentially escaping heap address, with -// pointer type. -// -// FreeVars are also used to implement bound method closures. Such a -// free var represents the receiver value and may be of any type that -// has concrete methods. -// -// Pos() returns the position of the value that was captured, which -// belongs to an enclosing function. -// -type FreeVar struct { - name string - typ types.Type - pos token.Pos - parent *Function - referrers []Instruction - - // Transiently needed during building. - outer Value // the Value captured from the enclosing context. -} - -// A Parameter represents an input parameter of a function. -// -type Parameter struct { - name string - object types.Object // a *types.Var; nil for non-source locals - typ types.Type - pos token.Pos - parent *Function - referrers []Instruction -} - -// A Const represents the value of a constant expression. -// -// The underlying type of a constant may be any boolean, numeric, or -// string type. In addition, a Const may represent the nil value of -// any reference type---interface, map, channel, pointer, slice, or -// function---but not "untyped nil". -// -// All source-level constant expressions are represented by a Const -// of the same type and value. -// -// Value holds the exact value of the constant, independent of its -// Type(), using the same representation as package go/exact uses for -// constants, or nil for a typed nil value. -// -// Pos() returns token.NoPos. -// -// Example printed form: -// 42:int -// "hello":untyped string -// 3+4i:MyComplex -// -type Const struct { - typ types.Type - Value exact.Value -} - -// 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 - object types.Object // a *types.Var; may be nil for synthetics e.g. init$guard - typ types.Type - pos token.Pos - - Pkg *Package -} - -// A Builtin represents a specific use of a built-in function, e.g. len. -// -// Builtins are immutable values. Builtins do not have addresses. -// Builtins can only appear in CallCommon.Func. -// -// Name() indicates the function: one of the built-in functions from the -// Go spec (excluding "make" and "new") or one of these ssa-defined -// intrinsics: -// -// // wrapnilchk returns ptr if non-nil, panics otherwise. -// // (For use in indirection wrappers.) -// func ssa:wrapnilchk(ptr *T, recvType, methodName string) *T -// -// Object() returns a *types.Builtin for built-ins defined by the spec, -// nil for others. -// -// Type() returns a *types.Signature representing the effective -// signature of the built-in for this call. -// -type Builtin struct { - name string - sig *types.Signature -} - -// Value-defining instructions ---------------------------------------- - -// The Alloc instruction reserves space for a variable of the given type, -// zero-initializes it, and yields its address. -// -// Alloc values are always addresses, and have pointer types, so the -// type of the allocated variable is actually -// Type().Underlying().(*types.Pointer).Elem(). -// -// If Heap is false, Alloc allocates space in the function's -// activation record (frame); we refer to an Alloc(Heap=false) as a -// "local" alloc. Each local Alloc returns the same address each time -// it is executed within the same activation; the space is -// re-initialized to zero. -// -// If Heap is true, Alloc allocates space in the heap; we -// refer to an Alloc(Heap=true) as a "new" alloc. Each new Alloc -// returns a different address each time it is executed. -// -// When Alloc is applied to a channel, map or slice type, it returns -// the address of an uninitialized (nil) reference of that kind; store -// 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.Rparen for a call to new() or for a call that -// allocates a varargs slice. -// -// Example printed form: -// t0 = local int -// t1 = new int -// -type Alloc struct { - register - Comment string - Heap bool - index int // dense numbering; for lifting -} - -// 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 the position of the && or || for short-circuit -// control-flow joins, or that of the *Alloc for φ-nodes inserted -// during SSA renaming. -// -// Example printed form: -// t2 = phi [0: t0, 1: t1] -// -type Phi struct { - register - Comment string // a hint as to its purpose - Edges []Value // Edges[i] is value for Block().Preds[i] -} - -// The Call instruction represents a function or method call. -// -// The Call instruction yields the function result if there is exactly -// one. Otherwise it returns a tuple, the components of which are -// accessed via Extract. -// -// 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() -// t7 = invoke t5.Println(...t6) -// -type Call struct { - register - Call CallCommon -} - -// The BinOp instruction yields the result of binary operation X Op Y. -// -// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source. -// -// Example printed form: -// t1 = t0 + 1:int -// -type BinOp struct { - register - // One of: - // ADD SUB MUL QUO REM + - * / % - // AND OR XOR SHL SHR AND_NOT & | ^ << >> &~ - // EQL LSS GTR NEQ LEQ GEQ == != < <= < >= - Op token.Token - X, Y Value -} - -// The UnOp instruction yields the result of Op X. -// ARROW is channel receive. -// MUL is pointer indirection (load). -// XOR is bitwise complement. -// SUB is negation. -// NOT is logical negation. -// -// If CommaOk and Op=ARROW, the result is a 2-tuple of the value above -// 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. -// For receive operations (ARROW) implicit in ranging over a channel, -// Pos() returns the ast.RangeStmt.For. -// For implicit memory loads (STAR), Pos() returns the position of the -// most closely associated source-level construct; the details are not -// specified. -// -// Example printed form: -// t0 = *x -// t2 = <-t1,ok -// -type UnOp struct { - register - Op token.Token // One of: NOT SUB ARROW MUL XOR ! - <- * ^ - X Value - CommaOk bool -} - -// The ChangeType instruction applies to X a value-preserving type -// change to Type(). -// -// 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. -// - from a bidirectional channel to a read- or write-channel, -// optionally adding/removing a name. -// -// This operation cannot fail dynamically. -// -// Pos() returns the ast.CallExpr.Lparen, if the instruction arose -// from an explicit conversion in the source. -// -// 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(). One or both of those types is basic (but possibly named). -// -// 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. -// - between pointers and unsafe.Pointer. -// - between unsafe.Pointer and uintptr. -// - 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. -// -// Pos() returns the ast.CallExpr.Lparen, if the instruction arose -// from an explicit conversion in the source. -// -// Example printed form: -// t1 = convert []byte <- string (t0) -// -type Convert struct { - register - X Value -} - -// ChangeInterface constructs a value of one interface type from a -// value of another interface type known to be assignable to it. -// This operation cannot fail. -// -// Pos() returns the ast.CallExpr.Lparen if the instruction arose from -// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the -// instruction arose from an explicit e.(T) operation; or token.NoPos -// otherwise. -// -// Example printed form: -// t1 = change interface interface{} <- I (t0) -// -type ChangeInterface struct { - register - X Value -} - -// MakeInterface constructs an instance of an interface type from a -// value of a concrete type. -// -// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set -// of X, and Program.Method(m) to find the implementation of a method. -// -// To construct the zero value of an interface type T, use: -// NewConst(exact.MakeNil(), T, pos) -// -// Pos() returns the ast.CallExpr.Lparen, if the instruction arose -// from an explicit conversion in the source. -// -// Example printed form: -// t1 = make interface{} <- int (42:int) -// t2 = make Stringer <- t0 -// -type MakeInterface struct { - register - X Value -} - -// The MakeClosure instruction yields a closure value whose code is -// Fn and whose free variables' values are supplied by Bindings. -// -// Type() returns a (possibly named) *types.Signature. -// -// Pos() returns the ast.FuncLit.Type.Func for a function literal -// closure or the ast.SelectorExpr.Sel for a bound method closure. -// -// Example printed form: -// t0 = make closure anon@1.2 [x y z] -// t1 = make closure bound$(main.I).add [i] -// -type MakeClosure struct { - register - Fn Value // always a *Function - Bindings []Value // values for each free variable in Fn.FreeVars -} - -// The MakeMap instruction creates a new hash-table-based map object -// and yields a value of kind map. -// -// 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 -// t1 = make StringIntMap t0 -// -type MakeMap struct { - register - Reserve Value // initial space reservation; nil => default -} - -// The MakeChan instruction creates a new channel object and yields a -// value of kind chan. -// -// 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 -// t0 = make IntChan 0 -// -type MakeChan struct { - register - Size Value // int; size of buffer; zero => synchronous. -} - -// 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. -// -// (Alloc(types.Array) followed by Slice will not suffice because -// Alloc can only create arrays of constant length.) -// -// 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 []string 1:int t0 -// t1 = make StringSlice 1:int t0 -// -type MakeSlice struct { - register - Len Value - Cap Value -} - -// The Slice instruction yields a slice of an existing string, slice -// or *array X between optional integer bounds Low and High. -// -// Dynamically, this instruction panics if X evaluates to a nil *array -// pointer. -// -// 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:] -// -type Slice struct { - register - X Value // slice, string, or *array - Low, High, Max Value // each may be nil -} - -// 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. -// -// Dynamically, this instruction panics if X evaluates to a nil -// pointer. -// -// 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] -// -type FieldAddr struct { - register - X Value // *struct - Field int // index into X.Type().Deref().(*types.Struct).Fields -} - -// 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] -// -type Field struct { - register - X Value // struct - Field int // index into X.Type().(*types.Struct).Fields -} - -// 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. -// -// Dynamically, this instruction panics if X evaluates to a nil *array -// pointer. -// -// 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] -// -type IndexAddr struct { - register - X Value // slice or *array, - Index Value // numeric index -} - -// 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] -// -type Index struct { - register - X Value // array - Index Value // integer index -} - -// 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 -// -type Lookup struct { - register - X Value // string or map - Index Value // numeric or key-typed index - CommaOk bool // return a value,ok pair -} - -// SelectState is a helper for Select. -// It represents one goal state and its corresponding communication. -// -type SelectState struct { - Dir types.ChanDir // direction of case (SendOnly or RecvOnly) - Chan Value // channel to use (for send or receive) - Send Value // value to send (for send) - Pos token.Pos // position of token.ARROW - DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode] -} - -// The Select instruction tests whether (or blocks until) one -// of the specified sent or received states is entered. -// -// Let n be the number of States for which Dir==RECV and T_i (0<=i string iterator; false => map iterator. -} - -// 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. -// -// If CommaOk: on success it returns a pair (v, true) where v is the -// result of the conversion; on failure it returns (z, false) where z -// is AssertedType's zero value. The components of the pair must be -// accessed using the Extract instruction. -// -// If AssertedType is a concrete type, TypeAssert checks whether the -// dynamic type in interface X is equal to it, and if so, the result -// of the conversion is a copy of the value in the interface. -// -// If AssertedType is an interface, TypeAssert checks whether the -// dynamic type of the interface is assignable to it, and if so, the -// result of the conversion is a copy of the interface value X. -// If AssertedType is a superinterface of X.Type(), the operation will -// fail iff the operand is nil. (Contrast with ChangeInterface, which -// performs no nil-check.) -// -// Type() reflects the actual type of the result, possibly a -// 2-types.Tuple; AssertedType is the asserted type. -// -// Pos() returns the ast.CallExpr.Lparen if the instruction arose from -// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the -// instruction arose from an explicit e.(T) operation; or the -// ast.CaseClause.Case if the instruction arose from a case of a -// type-switch statement. -// -// Example printed form: -// t1 = typeassert t0.(int) -// t3 = typeassert,ok t2.(T) -// -type TypeAssert struct { - register - X Value - AssertedType types.Type - CommaOk bool -} - -// 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 -// IndexExpr(Map). -// -// Example printed form: -// t1 = extract t0 #1 -// -type Extract struct { - register - Tuple Value - Index int -} - -// Instructions executed for effect. They do not yield a value. -------------------- - -// The Jump instruction transfers control to the sole successor of its -// owning block. -// -// A Jump must be the last instruction of its containing BasicBlock. -// -// Pos() returns NoPos. -// -// Example printed form: -// jump done -// -type Jump struct { - anInstruction -} - -// The If instruction transfers control to one of the two successors -// of its owning block, depending on the boolean Cond: the first if -// true, the second if false. -// -// An If instruction must be the last instruction of its containing -// BasicBlock. -// -// Pos() returns NoPos. -// -// Example printed form: -// if t0 goto done else body -// -type If struct { - anInstruction - Cond Value -} - -// The Return 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. -// -// If len(Results) > 1, Return returns a tuple value with the specified -// components which the caller must access using Extract instructions. -// -// There is no instruction to return a ready-made tuple like those -// returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or -// a tail-call to a function with multiple result parameters. -// -// Return 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: -// return -// return nil:I, 2:int -// -type Return struct { - anInstruction - Results []Value - pos token.Pos -} - -// 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 -// -type RunDefers struct { - anInstruction -} - -// 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. -// -// 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{} - pos token.Pos -} - -// The Go instruction creates a new goroutine and calls the specified -// function within it. -// -// See CallCommon for generic function call documentation. -// -// Pos() returns the ast.GoStmt.Go. -// -// Example printed form: -// go println(t0, t1) -// go t3() -// go invoke t5.Println(...t6) -// -type Go struct { - anInstruction - Call CallCommon - pos token.Pos -} - -// 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. -// -// Pos() returns the ast.DeferStmt.Defer. -// -// Example printed form: -// defer println(t0, t1) -// defer t3() -// defer invoke t5.Println(...t6) -// -type Defer struct { - anInstruction - Call CallCommon - pos token.Pos -} - -// 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 -// -type Send struct { - anInstruction - Chan, X Value - pos token.Pos -} - -// The Store instruction stores Val at address Addr. -// Stores can be of arbitrary types. -// -// Pos() returns the position of the source-level construct most closely -// associated with the memory store operation. -// Since implicit memory stores are numerous and varied and depend upon -// implementation choices, the details are not specified. -// -// Example printed form: -// *x = y -// -type Store struct { - anInstruction - Addr Value - Val Value - pos token.Pos -} - -// The MapUpdate instruction updates the association of Map[Key] to -// Value. -// -// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack, -// if explicit in the source. -// -// Example printed form: -// t0[t1] = t2 -// -type MapUpdate struct { - anInstruction - Map Value - Key Value - Value Value - pos token.Pos -} - -// A DebugRef instruction maps a source-level expression Expr to the -// SSA value X that represents the value (!IsAddr) or address (IsAddr) -// of that expression. -// -// DebugRef is a pseudo-instruction: it has no dynamic effect. -// -// Pos() returns Expr.Pos(), the start position of the source-level -// expression. This is not the same as the "designated" token as -// documented at Value.Pos(). e.g. CallExpr.Pos() does not return the -// position of the ("designated") Lparen token. -// -// If Expr is an *ast.Ident denoting a var or func, Object() returns -// the object; though this information can be obtained from the type -// checker, including it here greatly facilitates debugging. -// For non-Ident expressions, Object() returns nil. -// -// DebugRefs are generated only for functions built with debugging -// enabled; see Package.SetDebugMode() and the GlobalDebug builder -// mode flag. -// -// DebugRefs are not emitted for ast.Idents referring to constants or -// predeclared identifiers, since they are trivial and numerous. -// Nor are they emitted for ast.ParenExprs. -// -// (By representing these as instructions, rather than out-of-band, -// consistency is maintained during transformation passes by the -// ordinary SSA renaming machinery.) -// -// Example printed form: -// ; *ast.CallExpr @ 102:9 is t5 -// ; var x float64 @ 109:72 is x -// ; address of *ast.CompositeLit @ 216:10 is t0 -// -type DebugRef struct { - anInstruction - Expr ast.Expr // the referring expression (never *ast.ParenExpr) - object types.Object // the identity of the source var/func - IsAddr bool // Expr is addressable and X is the address it denotes - X Value // the value or address of Expr -} - -// Embeddable mix-ins and helpers for common parts of other structs. ----------- - -// register is a mix-in embedded by all SSA values that are also -// instructions, i.e. virtual registers, and provides a uniform -// implementation of most of the Value interface: Value.Name() is a -// numbered register (e.g. "t0"); the other methods are field accessors. -// -// Temporary names are automatically assigned to each register on -// completion of building a function in SSA form. -// -// Clients must not assume that the 'id' value (and the Name() derived -// from it) is unique within a function. As always in this API, -// semantics are determined only by identity; names exist only to -// facilitate debugging. -// -type register struct { - anInstruction - num int // "name" of virtual register, e.g. "t0". Not guaranteed unique. - typ types.Type // type of virtual register - pos token.Pos // position of source expression, or NoPos - referrers []Instruction -} - -// anInstruction is a mix-in embedded by all Instructions. -// It provides the implementations of the Block and setBlock methods. -type anInstruction struct { - block *BasicBlock // the basic block of this instruction -} - -// CallCommon is contained by Go, Defer and Call to hold the -// common parts of a function or method call. -// -// Each CallCommon exists in one of two modes, function call and -// interface method invocation, or "call" and "invoke" for short. -// -// 1. "call" mode: when Method is nil (!IsInvoke), a CallCommon -// represents an ordinary function call of the value in Value, -// which may be a *Builtin, a *Function or any other value of kind -// 'func'. -// -// Value may be one of: -// (a) a *Function, indicating a statically dispatched call -// to a package-level function, an anonymous function, or -// a method of a named type. -// (b) a *MakeClosure, indicating an immediately applied -// function literal with free variables. -// (c) a *Builtin, indicating a statically dispatched call -// to a built-in function. -// (d) any other value, indicating a dynamically dispatched -// function call. -// StaticCallee returns the identity of the callee in cases -// (a) and (b), nil otherwise. -// -// Args contains the arguments to the call. If Value is a method, -// Args[0] contains the receiver parameter. -// -// Example printed form: -// t2 = println(t0, t1) -// go t3() -// defer t5(...t6) -// -// 2. "invoke" mode: when Method is non-nil (IsInvoke), a CallCommon -// represents a dynamically dispatched call to an interface method. -// In this mode, Value is the interface value and Method is the -// interface's abstract method. Note: an abstract method may be -// shared by multiple interfaces due to embedding; Value.Type() -// provides the specific interface used for this call. -// -// Value is implicitly supplied to the concrete method implementation -// as the receiver parameter; in other words, Args[0] holds not the -// receiver but the first true argument. -// -// Example printed form: -// t1 = invoke t0.String() -// go invoke t3.Run(t2) -// defer invoke t4.Handle(...t5) -// -// For all calls to variadic functions (Signature().Variadic()), -// the last element of Args is a slice. -// -type CallCommon struct { - Value Value // receiver (invoke mode) or func value (call mode) - Method *types.Func // abstract method (invoke mode) - Args []Value // actual parameters (in static method call, includes receiver) - pos token.Pos // position of CallExpr.Lparen, iff explicit in source -} - -// IsInvoke returns true if this call has "invoke" (not "call") mode. -func (c *CallCommon) IsInvoke() bool { - return c.Method != nil -} - -func (c *CallCommon) Pos() token.Pos { return c.pos } - -// Signature returns the signature of the called function. -// -// For an "invoke"-mode call, the signature of the interface method is -// returned. -// -// In either "call" or "invoke" mode, if the callee is a method, its -// receiver is represented by sig.Recv, not sig.Params().At(0). -// -func (c *CallCommon) Signature() *types.Signature { - if c.Method != nil { - return c.Method.Type().(*types.Signature) - } - return c.Value.Type().Underlying().(*types.Signature) -} - -// StaticCallee returns the callee if this is a trivially static -// "call"-mode call to a function. -func (c *CallCommon) StaticCallee() *Function { - switch fn := c.Value.(type) { - case *Function: - return fn - case *MakeClosure: - return fn.Fn.(*Function) - } - return nil -} - -// Description returns a description of the mode of this call suitable -// for a user interface, e.g., "static method call". -func (c *CallCommon) Description() string { - switch fn := c.Value.(type) { - case *Builtin: - return "built-in function call" - case *MakeClosure: - return "static function closure call" - case *Function: - if fn.Signature.Recv() != nil { - return "static method call" - } - return "static function call" - } - if c.IsInvoke() { - return "dynamic method call" // ("invoke" mode) - } - return "dynamic function call" -} - -// The CallInstruction interface, implemented by *Go, *Defer and *Call, -// exposes the common parts of function-calling instructions, -// yet provides a way back to the Value defined by *Call alone. -// -type CallInstruction interface { - Instruction - Common() *CallCommon // returns the common parts of the call - Value() *Call // returns the result value of the call (*Call) or nil (*Go, *Defer) -} - -func (s *Call) Common() *CallCommon { return &s.Call } -func (s *Defer) Common() *CallCommon { return &s.Call } -func (s *Go) Common() *CallCommon { return &s.Call } - -func (s *Call) Value() *Call { return s } -func (s *Defer) Value() *Call { return nil } -func (s *Go) Value() *Call { return nil } - -func (v *Builtin) Type() types.Type { return v.sig } -func (v *Builtin) Name() string { return v.name } -func (*Builtin) Referrers() *[]Instruction { return nil } -func (v *Builtin) Pos() token.Pos { return token.NoPos } -func (v *Builtin) Object() types.Object { return types.Universe.Lookup(v.name) } -func (v *Builtin) Parent() *Function { return nil } - -func (v *FreeVar) Type() types.Type { return v.typ } -func (v *FreeVar) Name() string { return v.name } -func (v *FreeVar) Referrers() *[]Instruction { return &v.referrers } -func (v *FreeVar) Pos() token.Pos { return v.pos } -func (v *FreeVar) Parent() *Function { return v.parent } - -func (v *Global) Type() types.Type { return v.typ } -func (v *Global) Name() string { return v.name } -func (v *Global) Parent() *Function { return nil } -func (v *Global) Pos() token.Pos { return v.pos } -func (v *Global) Referrers() *[]Instruction { return nil } -func (v *Global) Token() token.Token { return token.VAR } -func (v *Global) Object() types.Object { return v.object } -func (v *Global) String() string { return v.RelString(nil) } -func (v *Global) Package() *Package { return v.Pkg } -func (v *Global) RelString(from *types.Package) string { return relString(v, from) } - -func (v *Function) Name() string { return v.name } -func (v *Function) Type() types.Type { return v.Signature } -func (v *Function) Pos() token.Pos { return v.pos } -func (v *Function) Token() token.Token { return token.FUNC } -func (v *Function) Object() types.Object { return v.object } -func (v *Function) String() string { return v.RelString(nil) } -func (v *Function) Package() *Package { return v.Pkg } -func (v *Function) Parent() *Function { return v.parent } -func (v *Function) Referrers() *[]Instruction { - if v.parent != nil { - return &v.referrers - } - return nil -} - -func (v *Parameter) Type() types.Type { return v.typ } -func (v *Parameter) Name() string { return v.name } -func (v *Parameter) Object() types.Object { return v.object } -func (v *Parameter) Referrers() *[]Instruction { return &v.referrers } -func (v *Parameter) Pos() token.Pos { return v.pos } -func (v *Parameter) Parent() *Function { return v.parent } - -func (v *Alloc) Type() types.Type { return v.typ } -func (v *Alloc) Referrers() *[]Instruction { return &v.referrers } -func (v *Alloc) Pos() token.Pos { return v.pos } - -func (v *register) Type() types.Type { return v.typ } -func (v *register) setType(typ types.Type) { v.typ = typ } -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) Pos() token.Pos { return v.pos } -func (v *register) setPos(pos token.Pos) { v.pos = pos } - -func (v *anInstruction) Parent() *Function { return v.block.parent } -func (v *anInstruction) Block() *BasicBlock { return v.block } -func (v *anInstruction) setBlock(block *BasicBlock) { v.block = block } -func (v *anInstruction) Referrers() *[]Instruction { return nil } - -func (t *Type) Name() string { return t.object.Name() } -func (t *Type) Pos() token.Pos { return t.object.Pos() } -func (t *Type) Type() types.Type { return t.object.Type() } -func (t *Type) Token() token.Token { return token.TYPE } -func (t *Type) Object() types.Object { return t.object } -func (t *Type) String() string { return t.RelString(nil) } -func (t *Type) Package() *Package { return t.pkg } -func (t *Type) RelString(from *types.Package) string { return relString(t, from) } - -func (c *NamedConst) Name() string { return c.object.Name() } -func (c *NamedConst) Pos() token.Pos { return c.object.Pos() } -func (c *NamedConst) String() string { return c.RelString(nil) } -func (c *NamedConst) Type() types.Type { return c.object.Type() } -func (c *NamedConst) Token() token.Token { return token.CONST } -func (c *NamedConst) Object() types.Object { return c.object } -func (c *NamedConst) Package() *Package { return c.pkg } -func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) } - -// Func returns the package-level function of the specified name, -// or nil if not found. -// -func (p *Package) Func(name string) (f *Function) { - f, _ = p.Members[name].(*Function) - return -} - -// Var returns the package-level variable of the specified name, -// or nil if not found. -// -func (p *Package) Var(name string) (g *Global) { - g, _ = p.Members[name].(*Global) - return -} - -// Const returns the package-level constant of the specified name, -// or nil if not found. -// -func (p *Package) Const(name string) (c *NamedConst) { - c, _ = p.Members[name].(*NamedConst) - return -} - -// Type returns the package-level type of the specified name, -// or nil if not found. -// -func (p *Package) Type(name string) (t *Type) { - t, _ = p.Members[name].(*Type) - return -} - -func (v *Call) Pos() token.Pos { return v.Call.pos } -func (s *Defer) Pos() token.Pos { return s.pos } -func (s *Go) Pos() token.Pos { return s.pos } -func (s *MapUpdate) Pos() token.Pos { return s.pos } -func (s *Panic) Pos() token.Pos { return s.pos } -func (s *Return) 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 } -func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() } - -// Operands. - -func (v *Alloc) Operands(rands []*Value) []*Value { - return rands -} - -func (v *BinOp) Operands(rands []*Value) []*Value { - return append(rands, &v.X, &v.Y) -} - -func (c *CallCommon) Operands(rands []*Value) []*Value { - rands = append(rands, &c.Value) - for i := range c.Args { - rands = append(rands, &c.Args[i]) - } - return rands -} - -func (s *Go) Operands(rands []*Value) []*Value { - return s.Call.Operands(rands) -} - -func (s *Call) Operands(rands []*Value) []*Value { - return s.Call.Operands(rands) -} - -func (s *Defer) Operands(rands []*Value) []*Value { - return s.Call.Operands(rands) -} - -func (v *ChangeInterface) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (v *ChangeType) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (v *Convert) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (s *DebugRef) Operands(rands []*Value) []*Value { - return append(rands, &s.X) -} - -func (v *Extract) Operands(rands []*Value) []*Value { - return append(rands, &v.Tuple) -} - -func (v *Field) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (v *FieldAddr) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (s *If) Operands(rands []*Value) []*Value { - return append(rands, &s.Cond) -} - -func (v *Index) Operands(rands []*Value) []*Value { - return append(rands, &v.X, &v.Index) -} - -func (v *IndexAddr) Operands(rands []*Value) []*Value { - return append(rands, &v.X, &v.Index) -} - -func (*Jump) Operands(rands []*Value) []*Value { - return rands -} - -func (v *Lookup) Operands(rands []*Value) []*Value { - return append(rands, &v.X, &v.Index) -} - -func (v *MakeChan) Operands(rands []*Value) []*Value { - return append(rands, &v.Size) -} - -func (v *MakeClosure) Operands(rands []*Value) []*Value { - rands = append(rands, &v.Fn) - for i := range v.Bindings { - rands = append(rands, &v.Bindings[i]) - } - return rands -} - -func (v *MakeInterface) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (v *MakeMap) Operands(rands []*Value) []*Value { - return append(rands, &v.Reserve) -} - -func (v *MakeSlice) Operands(rands []*Value) []*Value { - return append(rands, &v.Len, &v.Cap) -} - -func (v *MapUpdate) Operands(rands []*Value) []*Value { - return append(rands, &v.Map, &v.Key, &v.Value) -} - -func (v *Next) Operands(rands []*Value) []*Value { - return append(rands, &v.Iter) -} - -func (s *Panic) Operands(rands []*Value) []*Value { - return append(rands, &s.X) -} - -func (v *Phi) Operands(rands []*Value) []*Value { - for i := range v.Edges { - rands = append(rands, &v.Edges[i]) - } - return rands -} - -func (v *Range) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (s *Return) Operands(rands []*Value) []*Value { - for i := range s.Results { - rands = append(rands, &s.Results[i]) - } - return rands -} - -func (*RunDefers) Operands(rands []*Value) []*Value { - return rands -} - -func (v *Select) Operands(rands []*Value) []*Value { - for i := range v.States { - rands = append(rands, &v.States[i].Chan, &v.States[i].Send) - } - return rands -} - -func (s *Send) Operands(rands []*Value) []*Value { - return append(rands, &s.Chan, &s.X) -} - -func (v *Slice) Operands(rands []*Value) []*Value { - return append(rands, &v.X, &v.Low, &v.High, &v.Max) -} - -func (s *Store) Operands(rands []*Value) []*Value { - return append(rands, &s.Addr, &s.Val) -} - -func (v *TypeAssert) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -func (v *UnOp) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - -// Non-Instruction Values: -func (v *Builtin) Operands(rands []*Value) []*Value { return rands } -func (v *FreeVar) Operands(rands []*Value) []*Value { return rands } -func (v *Const) Operands(rands []*Value) []*Value { return rands } -func (v *Function) Operands(rands []*Value) []*Value { return rands } -func (v *Global) Operands(rands []*Value) []*Value { return rands } -func (v *Parameter) Operands(rands []*Value) []*Value { return rands } diff --git a/go/ssa/testmain14.go b/go/ssa/testmain14.go deleted file mode 100644 index ddd27a1c59..0000000000 --- a/go/ssa/testmain14.go +++ /dev/null @@ -1,304 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// CreateTestMainPackage synthesizes a main package that runs all the -// tests of the supplied packages. -// It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing. - -import ( - "go/ast" - "go/token" - "os" - "sort" - "strings" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// FindTests returns the list of packages that define at least one Test, -// Example or Benchmark function (as defined by "go test"), and the -// lists of all such functions. -// -func FindTests(pkgs []*Package) (testpkgs []*Package, tests, benchmarks, examples []*Function) { - if len(pkgs) == 0 { - return - } - prog := pkgs[0].Prog - - // The first two of these may be nil: if the program doesn't import "testing", - // it can't contain any tests, but it may yet contain Examples. - var testSig *types.Signature // func(*testing.T) - var benchmarkSig *types.Signature // func(*testing.B) - var exampleSig = types.NewSignature(nil, nil, nil, false) // func() - - // Obtain the types from the parameters of testing.Main(). - if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { - params := testingPkg.Func("Main").Signature.Params() - testSig = funcField(params.At(1).Type()) - benchmarkSig = funcField(params.At(2).Type()) - } - - seen := make(map[*Package]bool) - for _, pkg := range pkgs { - if pkg.Prog != prog { - panic("wrong Program") - } - - // TODO(adonovan): use a stable order, e.g. lexical. - for _, mem := range pkg.Members { - if f, ok := mem.(*Function); ok && - ast.IsExported(f.Name()) && - strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") { - - switch { - case testSig != nil && isTestSig(f, "Test", testSig): - tests = append(tests, f) - case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig): - benchmarks = append(benchmarks, f) - case isTestSig(f, "Example", exampleSig): - examples = append(examples, f) - default: - continue - } - - if !seen[pkg] { - seen[pkg] = true - testpkgs = append(testpkgs, pkg) - } - } - } - } - return -} - -// Like isTest, but checks the signature too. -func isTestSig(f *Function, prefix string, sig *types.Signature) bool { - return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig) -} - -// If non-nil, testMainStartBodyHook is called immediately after -// startBody for main.init and main.main, making it easy for users to -// add custom imports and initialization steps for proprietary build -// systems that don't exactly follow 'go test' conventions. -var testMainStartBodyHook func(*Function) - -// CreateTestMainPackage creates and returns a synthetic "main" -// package that runs all the tests of the supplied packages, similar -// to the one that would be created by the 'go test' tool. -// -// It returns nil if the program contains no tests. -// -func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { - pkgs, tests, benchmarks, examples := FindTests(pkgs) - if len(pkgs) == 0 { - return nil - } - - testmain := &Package{ - Prog: prog, - Members: make(map[string]Member), - values: make(map[types.Object]Value), - Pkg: types.NewPackage("test$main", "main"), - } - - // Build package's init function. - init := &Function{ - name: "init", - Signature: new(types.Signature), - Synthetic: "package initializer", - Pkg: testmain, - Prog: prog, - } - init.startBody() - - if testMainStartBodyHook != nil { - testMainStartBodyHook(init) - } - - // Initialize packages to test. - var pkgpaths []string - for _, pkg := range pkgs { - var v Call - v.Call.Value = pkg.init - v.setType(types.NewTuple()) - init.emit(&v) - - pkgpaths = append(pkgpaths, pkg.Pkg.Path()) - } - sort.Strings(pkgpaths) - init.emit(new(Return)) - init.finishBody() - testmain.init = init - testmain.Pkg.MarkComplete() - testmain.Members[init.name] = init - - // For debugging convenience, define an unexported const - // that enumerates the packages. - packagesConst := types.NewConst(token.NoPos, testmain.Pkg, "packages", tString, - exact.MakeString(strings.Join(pkgpaths, " "))) - memberFromObject(testmain, packagesConst, nil) - - // Create main *types.Func and *ssa.Function - mainFunc := types.NewFunc(token.NoPos, testmain.Pkg, "main", new(types.Signature)) - memberFromObject(testmain, mainFunc, nil) - main := testmain.Func("main") - main.Synthetic = "test main function" - - main.startBody() - - if testMainStartBodyHook != nil { - testMainStartBodyHook(main) - } - - if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { - testingMain := testingPkg.Func("Main") - testingMainParams := testingMain.Signature.Params() - - // The generated code is as if compiled from this: - // - // func main() { - // match := func(_, _ string) (bool, error) { return true, nil } - // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} - // benchmarks := []testing.InternalBenchmark{...} - // examples := []testing.InternalExample{...} - // testing.Main(match, tests, benchmarks, examples) - // } - - matcher := &Function{ - name: "matcher", - Signature: testingMainParams.At(0).Type().(*types.Signature), - Synthetic: "test matcher predicate", - parent: main, - Pkg: testmain, - Prog: prog, - } - main.AnonFuncs = append(main.AnonFuncs, matcher) - matcher.startBody() - matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) - matcher.finishBody() - - // Emit call: testing.Main(matcher, tests, benchmarks, examples). - var c Call - c.Call.Value = testingMain - c.Call.Args = []Value{ - matcher, - testMainSlice(main, tests, testingMainParams.At(1).Type()), - testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), - testMainSlice(main, examples, testingMainParams.At(3).Type()), - } - emitTailCall(main, &c) - } else { - // The program does not import "testing", but FindTests - // returned non-nil, which must mean there were Examples - // but no Tests or Benchmarks. - // We'll simply call them from testmain.main; this will - // ensure they don't panic, but will not check any - // "Output:" comments. - for _, eg := range examples { - var c Call - c.Call.Value = eg - c.setType(types.NewTuple()) - main.emit(&c) - } - main.emit(&Return{}) - main.currentBlock = nil - } - - main.finishBody() - - testmain.Members["main"] = main - - if prog.mode&PrintPackages != 0 { - printMu.Lock() - testmain.WriteTo(os.Stdout) - printMu.Unlock() - } - - if prog.mode&SanityCheckFunctions != 0 { - sanityCheckPackage(testmain) - } - - prog.packages[testmain.Pkg] = testmain - - return testmain -} - -// testMainSlice emits to fn code to construct a slice of type slice -// (one of []testing.Internal{Test,Benchmark,Example}) for all -// functions in testfuncs. It returns the slice value. -// -func testMainSlice(fn *Function, testfuncs []*Function, slice types.Type) Value { - if testfuncs == nil { - return nilConst(slice) - } - - tElem := slice.(*types.Slice).Elem() - tPtrString := types.NewPointer(tString) - tPtrElem := types.NewPointer(tElem) - tPtrFunc := types.NewPointer(funcField(slice)) - - // TODO(adonovan): fix: populate the - // testing.InternalExample.Output field correctly so that tests - // work correctly under the interpreter. This requires that we - // do this step using ASTs, not *ssa.Functions---quite a - // redesign. See also the fake runExample in go/ssa/interp. - - // Emit: array = new [n]testing.InternalTest - tArray := types.NewArray(tElem, int64(len(testfuncs))) - array := emitNew(fn, tArray, token.NoPos) - array.Comment = "test main" - for i, testfunc := range testfuncs { - // Emit: pitem = &array[i] - ia := &IndexAddr{X: array, Index: intConst(int64(i))} - ia.setType(tPtrElem) - pitem := fn.emit(ia) - - // Emit: pname = &pitem.Name - fa := &FieldAddr{X: pitem, Field: 0} // .Name - fa.setType(tPtrString) - pname := fn.emit(fa) - - // Emit: *pname = "testfunc" - emitStore(fn, pname, stringConst(testfunc.Name()), token.NoPos) - - // Emit: pfunc = &pitem.F - fa = &FieldAddr{X: pitem, Field: 1} // .F - fa.setType(tPtrFunc) - pfunc := fn.emit(fa) - - // Emit: *pfunc = testfunc - emitStore(fn, pfunc, testfunc, token.NoPos) - } - - // Emit: slice array[:] - sl := &Slice{X: array} - sl.setType(slice) - return fn.emit(sl) -} - -// Given the type of one of the three slice parameters of testing.Main, -// returns the function type. -func funcField(slice types.Type) *types.Signature { - return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature) -} - -// Plundered from $GOROOT/src/cmd/go/test.go - -// isTest tells whether name looks like a test (or benchmark, according to prefix). -// It is a Test (say) if there is a character after Test that is not a lower-case letter. -// We don't want TesticularCancer. -func isTest(name, prefix string) bool { - if !strings.HasPrefix(name, prefix) { - return false - } - if len(name) == len(prefix) { // "Test" is ok - return true - } - return ast.IsExported(name[len(prefix):]) -} diff --git a/go/ssa/util14.go b/go/ssa/util14.go deleted file mode 100644 index 444d69c415..0000000000 --- a/go/ssa/util14.go +++ /dev/null @@ -1,121 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines a number of miscellaneous utility functions. - -import ( - "fmt" - "go/ast" - "go/token" - "io" - "os" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" -) - -//// AST utilities - -func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) } - -// isBlankIdent returns true iff e is an Ident with name "_". -// They have no associated types.Object, and thus no type. -// -func isBlankIdent(e ast.Expr) bool { - id, ok := e.(*ast.Ident) - return ok && id.Name == "_" -} - -//// Type utilities. Some of these belong in go/types. - -// isPointer returns true for types whose underlying type is a pointer. -func isPointer(typ types.Type) bool { - _, ok := typ.Underlying().(*types.Pointer) - return ok -} - -func isInterface(T types.Type) bool { return types.IsInterface(T) } - -// deref returns a pointer's element type; otherwise it returns typ. -func deref(typ types.Type) types.Type { - if p, ok := typ.Underlying().(*types.Pointer); ok { - return p.Elem() - } - return typ -} - -// recvType returns the receiver type of method obj. -func recvType(obj *types.Func) types.Type { - return obj.Type().(*types.Signature).Recv().Type() -} - -// DefaultType returns the default "typed" type for an "untyped" type; -// it returns the incoming type for all other types. The default type -// for untyped nil is untyped nil. -// -// Exported to ssa/interp. -// -// TODO(gri): this is a copy of go/types.defaultType; export that function. -// -func DefaultType(typ types.Type) types.Type { - if t, ok := typ.(*types.Basic); ok { - k := t.Kind() - switch k { - case types.UntypedBool: - k = types.Bool - case types.UntypedInt: - k = types.Int - case types.UntypedRune: - k = types.Rune - case types.UntypedFloat: - k = types.Float64 - case types.UntypedComplex: - k = types.Complex128 - case types.UntypedString: - k = types.String - } - typ = types.Typ[k] - } - return typ -} - -// logStack prints the formatted "start" message to stderr and -// returns a closure that prints the corresponding "end" message. -// Call using 'defer logStack(...)()' to show builder stack on panic. -// Don't forget trailing parens! -// -func logStack(format string, args ...interface{}) func() { - msg := fmt.Sprintf(format, args...) - io.WriteString(os.Stderr, msg) - io.WriteString(os.Stderr, "\n") - return func() { - io.WriteString(os.Stderr, msg) - io.WriteString(os.Stderr, " end\n") - } -} - -// newVar creates a 'var' for use in a types.Tuple. -func newVar(name string, typ types.Type) *types.Var { - return types.NewParam(token.NoPos, nil, name, typ) -} - -// anonVar creates an anonymous 'var' for use in a types.Tuple. -func anonVar(typ types.Type) *types.Var { - return newVar("", typ) -} - -var lenResults = types.NewTuple(anonVar(tInt)) - -// makeLen returns the len builtin specialized to type func(T)int. -func makeLen(T types.Type) *Builtin { - lenParams := types.NewTuple(anonVar(T)) - return &Builtin{ - name: "len", - sig: types.NewSignature(nil, lenParams, lenResults, false), - } -} diff --git a/go/ssa/wrappers14.go b/go/ssa/wrappers14.go deleted file mode 100644 index 89f71b7b24..0000000000 --- a/go/ssa/wrappers14.go +++ /dev/null @@ -1,296 +0,0 @@ -// 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. - -// +build !go1.5 - -package ssa - -// This file defines synthesis of Functions that delegate to declared -// methods; they come in three kinds: -// -// (1) wrappers: methods that wrap declared methods, performing -// implicit pointer indirections and embedded field selections. -// -// (2) thunks: funcs that wrap declared methods. Like wrappers, -// thunks perform indirections and field selections. The thunk's -// first parameter is used as the receiver for the method call. -// -// (3) bounds: funcs that wrap declared methods. The bound's sole -// free variable, supplied by a closure, is used as the receiver -// for the method call. No indirections or field selections are -// performed since they can be done before the call. - -import ( - "fmt" - - "golang.org/x/tools/go/types" -) - -// -- wrappers ----------------------------------------------------------- - -// makeWrapper returns a synthetic method that delegates to the -// declared method denoted by meth.Obj(), first performing any -// necessary pointer indirections or field selections implied by meth. -// -// The resulting method's receiver type is meth.Recv(). -// -// This function is versatile but quite subtle! Consider the -// following axes of variation when making changes: -// - optional receiver indirection -// - optional implicit field selections -// - meth.Obj() may denote a concrete or an interface method -// - the result may be a thunk or a wrapper. -// -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -// -func makeWrapper(prog *Program, sel *types.Selection) *Function { - obj := sel.Obj().(*types.Func) // the declared function - sig := sel.Type().(*types.Signature) // type of this wrapper - - var recv *types.Var // wrapper's receiver or thunk's params[0] - name := obj.Name() - var description string - var start int // first regular param - if sel.Kind() == types.MethodExpr { - name += "$thunk" - description = "thunk" - recv = sig.Params().At(0) - start = 1 - } else { - description = "wrapper" - recv = sig.Recv() - } - - description = fmt.Sprintf("%s for %s", description, sel.Obj()) - if prog.mode&LogSource != 0 { - defer logStack("make %s to (%s)", description, recv.Type())() - } - fn := &Function{ - name: name, - method: sel, - object: obj, - Signature: sig, - Synthetic: description, - Prog: prog, - pos: obj.Pos(), - } - fn.startBody() - fn.addSpilledParam(recv) - createParams(fn, start) - - indices := sel.Index() - - var v Value = fn.Locals[0] // spilled receiver - if isPointer(sel.Recv()) { - v = emitLoad(fn, v) - - // For simple indirection wrappers, perform an informative nil-check: - // "value method (T).f called using nil *T pointer" - if len(indices) == 1 && !isPointer(recvType(obj)) { - var c Call - c.Call.Value = &Builtin{ - name: "ssa:wrapnilchk", - sig: types.NewSignature(nil, - types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)), - types.NewTuple(anonVar(sel.Recv())), false), - } - c.Call.Args = []Value{ - v, - stringConst(deref(sel.Recv()).String()), - stringConst(sel.Obj().Name()), - } - c.setType(v.Type()) - v = fn.emit(&c) - } - } - - // Invariant: v is a pointer, either - // value of *A receiver param, or - // address of A spilled receiver. - - // We use pointer arithmetic (FieldAddr possibly followed by - // Load) in preference to value extraction (Field possibly - // preceded by Load). - - v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) - - // Invariant: v is a pointer, either - // value of implicit *C field, or - // address of implicit C field. - - var c Call - if r := recvType(obj); !isInterface(r) { // concrete method - if !isPointer(r) { - v = emitLoad(fn, v) - } - c.Call.Value = prog.declaredFunc(obj) - c.Call.Args = append(c.Call.Args, v) - } else { - c.Call.Method = obj - c.Call.Value = emitLoad(fn, v) - } - for _, arg := range fn.Params[1:] { - c.Call.Args = append(c.Call.Args, arg) - } - emitTailCall(fn, &c) - fn.finishBody() - return fn -} - -// createParams creates parameters for wrapper method fn based on its -// Signature.Params, which do not include the receiver. -// start is the index of the first regular parameter to use. -// -func createParams(fn *Function, start int) { - var last *Parameter - tparams := fn.Signature.Params() - for i, n := start, tparams.Len(); i < n; i++ { - last = fn.addParamObj(tparams.At(i)) - } - if fn.Signature.Variadic() { - last.typ = types.NewSlice(last.typ) - } -} - -// -- bounds ----------------------------------------------------------- - -// makeBound returns a bound method wrapper (or "bound"), a synthetic -// function that delegates to a concrete or interface method denoted -// by obj. The resulting function has no receiver, but has one free -// variable which will be used as the method's receiver in the -// tail-call. -// -// Use MakeClosure with such a wrapper to construct a bound method -// closure. e.g.: -// -// type T int or: type T interface { meth() } -// func (t T) meth() -// var t T -// f := t.meth -// f() // calls t.meth() -// -// f is a closure of a synthetic wrapper defined as if by: -// -// f := func() { return t.meth() } -// -// Unlike makeWrapper, makeBound need perform no indirection or field -// selections because that can be done before the closure is -// constructed. -// -// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) -// -func makeBound(prog *Program, obj *types.Func) *Function { - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - fn, ok := prog.bounds[obj] - if !ok { - description := fmt.Sprintf("bound method wrapper for %s", obj) - if prog.mode&LogSource != 0 { - defer logStack("%s", description)() - } - fn = &Function{ - name: obj.Name() + "$bound", - object: obj, - Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver - Synthetic: description, - Prog: prog, - pos: obj.Pos(), - } - - fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} - fn.FreeVars = []*FreeVar{fv} - fn.startBody() - createParams(fn, 0) - var c Call - - if !isInterface(recvType(obj)) { // concrete - c.Call.Value = prog.declaredFunc(obj) - c.Call.Args = []Value{fv} - } else { - c.Call.Value = fv - c.Call.Method = obj - } - for _, arg := range fn.Params { - c.Call.Args = append(c.Call.Args, arg) - } - emitTailCall(fn, &c) - fn.finishBody() - - prog.bounds[obj] = fn - } - return fn -} - -// -- thunks ----------------------------------------------------------- - -// makeThunk returns a thunk, a synthetic function that delegates to a -// concrete or interface method denoted by sel.Obj(). The resulting -// function has no receiver, but has an additional (first) regular -// parameter. -// -// Precondition: sel.Kind() == types.MethodExpr. -// -// type T int or: type T interface { meth() } -// func (t T) meth() -// f := T.meth -// var t T -// f(t) // calls t.meth() -// -// f is a synthetic wrapper defined as if by: -// -// f := func(t T) { return t.meth() } -// -// TODO(adonovan): opt: currently the stub is created even when used -// directly in a function call: C.f(i, 0). This is less efficient -// than inlining the stub. -// -// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) -// -func makeThunk(prog *Program, sel *types.Selection) *Function { - if sel.Kind() != types.MethodExpr { - panic(sel) - } - - key := selectionKey{ - kind: sel.Kind(), - recv: sel.Recv(), - obj: sel.Obj(), - index: fmt.Sprint(sel.Index()), - indirect: sel.Indirect(), - } - - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - - // Canonicalize key.recv to avoid constructing duplicate thunks. - canonRecv, ok := prog.canon.At(key.recv).(types.Type) - if !ok { - canonRecv = key.recv - prog.canon.Set(key.recv, canonRecv) - } - key.recv = canonRecv - - fn, ok := prog.thunks[key] - if !ok { - fn = makeWrapper(prog, sel) - if fn.Signature.Recv() != nil { - panic(fn) // unexpected receiver - } - prog.thunks[key] = fn - } - return fn -} - -func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { - return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic()) -} - -// selectionKey is like types.Selection but a usable map key. -type selectionKey struct { - kind types.SelectionKind - recv types.Type // canonicalized via Program.canon - obj types.Object - index string - indirect bool -} diff --git a/go/types/api.go b/go/types/api.go deleted file mode 100644 index 5344a39e7f..0000000000 --- a/go/types/api.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2012 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 declares the data types and implements -// the algorithms for type-checking of Go packages. -// Use Check and Config.Check to invoke the type-checker. -// -// Type-checking consists of several interdependent phases: -// -// Name resolution maps each identifier (ast.Ident) in the program to the -// language object (Object) it denotes. -// Use Info.{Defs,Uses,Implicits} for the results of name resolution. -// -// Constant folding computes the exact constant value (exact.Value) for -// every expression (ast.Expr) that is a compile-time constant. -// Use Info.Types[expr].Value for the results of constant folding. -// -// Type inference computes the type (Type) of every expression (ast.Expr) -// and checks for compliance with the language specification. -// Use Info.Types[expr].Type for the results of type inference. -// -package types // import "golang.org/x/tools/go/types" - -import ( - "bytes" - "fmt" - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -// Check type-checks a package and returns the resulting complete package -// object, or a nil package and the first error. The package is specified -// by a list of *ast.Files and corresponding file set, and the import path -// the package is identified with. The clean path must not be empty or dot ("."). -// -// For more control over type-checking and results, use Config.Check. -func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) { - var conf Config - pkg, err := conf.Check(path, fset, files, nil) - if err != nil { - return nil, err - } - return pkg, nil -} - -// An Error describes a type-checking error; it implements the error interface. -// A "soft" error is an error that still permits a valid interpretation of a -// package (such as "unused variable"); "hard" errors may lead to unpredictable -// behavior if ignored. -type Error struct { - Fset *token.FileSet // file set for interpretation of Pos - Pos token.Pos // error position - Msg string // error message - Soft bool // if set, error is "soft" -} - -// Error returns an error string formatted as follows: -// filename:line:column: message -func (err Error) Error() string { - return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) -} - -// An importer resolves import paths to Packages. -// The imports map records packages already known, -// indexed by package path. The type-checker -// will invoke Import with Config.Packages. -// An importer must determine the canonical package path and -// check imports to see if it is already present in the map. -// If so, the Importer can return the map entry. Otherwise, -// the importer must load the package data for the given path -// into a new *Package, record it in imports map, and return -// the package. -// TODO(gri) Need to be clearer about requirements of completeness. -type Importer func(map[string]*Package, string) (*Package, error) - -// A Config specifies the configuration for type checking. -// The zero value for Config is a ready-to-use default configuration. -type Config struct { - // If IgnoreFuncBodies is set, function bodies are not - // type-checked. - IgnoreFuncBodies bool - - // If FakeImportC is set, `import "C"` (for packages requiring Cgo) - // declares an empty "C" package and errors are omitted for qualified - // identifiers referring to package C (which won't find an object). - // This feature is intended for the standard library cmd/api tool. - // - // Caution: Effects may be unpredictable due to follow-up errors. - // Do not use casually! - FakeImportC bool - - // Packages is used to look up (and thus canonicalize) packages by - // package path. If Packages is nil, it is set to a new empty map. - // During type-checking, imported packages are added to the map. - Packages map[string]*Package - - // If Error != nil, it is called with each error found - // during type checking; err has dynamic type Error. - // Secondary errors (for instance, to enumerate all types - // involved in an invalid recursive type declaration) have - // error strings that start with a '\t' character. - // If Error == nil, type-checking stops with the first - // error found. - Error func(err error) - - // If Import != nil, it is called for each imported package. - // Otherwise, DefaultImport is called. - Import Importer - - // If Sizes != nil, it provides the sizing functions for package unsafe. - // Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead. - Sizes Sizes - - // If DisableUnusedImportCheck is set, packages are not checked - // for unused imports. - DisableUnusedImportCheck bool -} - -// DefaultImport is the default importer invoked if Config.Import == nil. -// The declaration: -// -// import _ "golang.org/x/tools/go/gcimporter" -// -// in a client of go/types will initialize DefaultImport to gcimporter.Import. -var DefaultImport Importer - -// Info holds result type information for a type-checked package. -// Only the information for which a map is provided is collected. -// If the package has type errors, the collected information may -// be incomplete. -type Info struct { - // Types maps expressions to their types, and for constant - // expressions, their values. Invalid expressions are omitted. - // - // For (possibly parenthesized) identifiers denoting built-in - // functions, the recorded signatures are call-site specific: - // if the call result is not a constant, the recorded type is - // an argument-specific signature. Otherwise, the recorded type - // is invalid. - // - // Identifiers on the lhs of declarations (i.e., the identifiers - // which are being declared) are collected in the Defs map. - // Identifiers denoting packages are collected in the Uses maps. - Types map[ast.Expr]TypeAndValue - - // Defs maps identifiers to the objects they define (including - // package names, dots "." of dot-imports, and blank "_" identifiers). - // For identifiers that do not denote objects (e.g., the package name - // in package clauses, or symbolic variables t in t := x.(type) of - // type switch headers), the corresponding objects are nil. - // - // For an anonymous field, Defs returns the field *Var it defines. - // - // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos() - Defs map[*ast.Ident]Object - - // Uses maps identifiers to the objects they denote. - // - // For an anonymous field, Uses returns the *TypeName it denotes. - // - // Invariant: Uses[id].Pos() != id.Pos() - Uses map[*ast.Ident]Object - - // Implicits maps nodes to their implicitly declared objects, if any. - // The following node and object types may appear: - // - // node declared object - // - // *ast.ImportSpec *PkgName for dot-imports and imports without renames - // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) - // *ast.Field anonymous struct field or parameter *Var - // - Implicits map[ast.Node]Object - - // Selections maps selector expressions (excluding qualified identifiers) - // to their corresponding selections. - Selections map[*ast.SelectorExpr]*Selection - - // Scopes maps ast.Nodes to the scopes they define. Package scopes are not - // associated with a specific node but with all files belonging to a package. - // Thus, the package scope can be found in the type-checked Package object. - // Scopes nest, with the Universe scope being the outermost scope, enclosing - // the package scope, which contains (one or more) files scopes, which enclose - // function scopes which in turn enclose statement and function literal scopes. - // Note that even though package-level functions are declared in the package - // scope, the function scopes are embedded in the file scope of the file - // containing the function declaration. - // - // The following node types may appear in Scopes: - // - // *ast.File - // *ast.FuncType - // *ast.BlockStmt - // *ast.IfStmt - // *ast.SwitchStmt - // *ast.TypeSwitchStmt - // *ast.CaseClause - // *ast.CommClause - // *ast.ForStmt - // *ast.RangeStmt - // - Scopes map[ast.Node]*Scope - - // InitOrder is the list of package-level initializers in the order in which - // they must be executed. Initializers referring to variables related by an - // initialization dependency appear in topological order, the others appear - // in source order. Variables without an initialization expression do not - // appear in this list. - InitOrder []*Initializer -} - -// TypeOf returns the type of expression e, or nil if not found. -// Precondition: the Types, Uses and Defs maps are populated. -// -func (info *Info) TypeOf(e ast.Expr) Type { - if t, ok := info.Types[e]; ok { - return t.Type - } - if id, _ := e.(*ast.Ident); id != nil { - if obj := info.ObjectOf(id); obj != nil { - return obj.Type() - } - } - return nil -} - -// ObjectOf returns the object denoted by the specified id, -// or nil if not found. -// -// If id is an anonymous struct field, ObjectOf returns the field (*Var) -// it uses, not the type (*TypeName) it defines. -// -// Precondition: the Uses and Defs maps are populated. -// -func (info *Info) ObjectOf(id *ast.Ident) Object { - if obj, _ := info.Defs[id]; obj != nil { - return obj - } - return info.Uses[id] -} - -// TypeAndValue reports the type and value (for constants) -// of the corresponding expression. -type TypeAndValue struct { - mode operandMode - Type Type - Value exact.Value -} - -// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report -// "void" values as regular values but with the empty tuple type. - -// IsVoid reports whether the corresponding expression -// is a function call without results. -func (tv TypeAndValue) IsVoid() bool { - return tv.mode == novalue -} - -// IsType reports whether the corresponding expression specifies a type. -func (tv TypeAndValue) IsType() bool { - return tv.mode == typexpr -} - -// IsBuiltin reports whether the corresponding expression denotes -// a (possibly parenthesized) built-in function. -func (tv TypeAndValue) IsBuiltin() bool { - return tv.mode == builtin -} - -// IsValue reports whether the corresponding expression is a value. -// Builtins are not considered values. Constant values have a non- -// nil Value. -func (tv TypeAndValue) IsValue() bool { - switch tv.mode { - case constant, variable, mapindex, value, commaok: - return true - } - return false -} - -// IsNil reports whether the corresponding expression denotes the -// predeclared value nil. -func (tv TypeAndValue) IsNil() bool { - return tv.mode == value && tv.Type == Typ[UntypedNil] -} - -// Addressable reports whether the corresponding expression -// is addressable (http://golang.org/ref/spec#Address_operators). -func (tv TypeAndValue) Addressable() bool { - return tv.mode == variable -} - -// Assignable reports whether the corresponding expression -// is assignable to (provided a value of the right type). -func (tv TypeAndValue) Assignable() bool { - return tv.mode == variable || tv.mode == mapindex -} - -// HasOk reports whether the corresponding expression may be -// used on the lhs of a comma-ok assignment. -func (tv TypeAndValue) HasOk() bool { - return tv.mode == commaok || tv.mode == mapindex -} - -// An Initializer describes a package-level variable, or a list of variables in case -// of a multi-valued initialization expression, and the corresponding initialization -// expression. -type Initializer struct { - Lhs []*Var // var Lhs = Rhs - Rhs ast.Expr -} - -func (init *Initializer) String() string { - var buf bytes.Buffer - for i, lhs := range init.Lhs { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(lhs.Name()) - } - buf.WriteString(" = ") - WriteExpr(&buf, init.Rhs) - return buf.String() -} - -// Check type-checks a package and returns the resulting package object, -// the first error if any, and if info != nil, additional type information. -// The package is marked as complete if no errors occurred, otherwise it is -// incomplete. See Config.Error for controlling behavior in the presence of -// errors. -// -// The package is specified by a list of *ast.Files and corresponding -// file set, and the package path the package is identified with. -// The clean path must not be empty or dot ("."). -func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { - pkg := NewPackage(path, "") - return pkg, NewChecker(conf, fset, pkg, info).Files(files) -} - -// AssertableTo reports whether a value of type V can be asserted to have type T. -func AssertableTo(V *Interface, T Type) bool { - m, _ := assertableTo(V, T) - return m == nil -} - -// AssignableTo reports whether a value of type V is assignable to a variable of type T. -func AssignableTo(V, T Type) bool { - x := operand{mode: value, typ: V} - return x.assignableTo(nil, T) // config not needed for non-constant x -} - -// ConvertibleTo reports whether a value of type V is convertible to a value of type T. -func ConvertibleTo(V, T Type) bool { - x := operand{mode: value, typ: V} - return x.convertibleTo(nil, T) // config not needed for non-constant x -} - -// Implements reports whether type V implements interface T. -func Implements(V Type, T *Interface) bool { - f, _ := MissingMethod(V, T, true) - return f == nil -} diff --git a/go/types/api_test.go b/go/types/api_test.go deleted file mode 100644 index 5a0535f5b6..0000000000 --- a/go/types/api_test.go +++ /dev/null @@ -1,1060 +0,0 @@ -// 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_test - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/token" - "reflect" - "regexp" - "runtime" - "strings" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -// skipSpecialPlatforms causes the test to be skipped for platforms where -// builders (build.golang.org) don't have access to compiled packages for -// import. -func skipSpecialPlatforms(t *testing.T) { - switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform { - case "android-arm", - "nacl-amd64p32", - "nacl-386", - "nacl-arm", - "darwin-arm", - "darwin-arm64": - t.Skipf("no compiled packages available for import on %s", platform) - } -} - -func pkgFor(path, source string, info *Info) (*Package, error) { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, path, source, 0) - if err != nil { - return nil, err - } - - var conf Config - return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) -} - -func mustTypecheck(t *testing.T, path, source string, info *Info) string { - pkg, err := pkgFor(path, source, info) - if err != nil { - name := path - if pkg != nil { - name = "package " + pkg.Name() - } - t.Fatalf("%s: didn't type-check (%s)", name, err) - } - return pkg.Name() -} - -func TestValuesInfo(t *testing.T) { - var tests = []struct { - src string - expr string // constant expression - typ string // constant type - val string // constant value - }{ - {`package a0; const _ = false`, `false`, `untyped bool`, `false`}, - {`package a1; const _ = 0`, `0`, `untyped int`, `0`}, - {`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`}, - {`package a3; const _ = 0.`, `0.`, `untyped float`, `0`}, - {`package a4; const _ = 0i`, `0i`, `untyped complex`, `0`}, - {`package a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`}, - - {`package b0; var _ = false`, `false`, `bool`, `false`}, - {`package b1; var _ = 0`, `0`, `int`, `0`}, - {`package b2; var _ = 'A'`, `'A'`, `rune`, `65`}, - {`package b3; var _ = 0.`, `0.`, `float64`, `0`}, - {`package b4; var _ = 0i`, `0i`, `complex128`, `0`}, - {`package b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`}, - - {`package c0a; var _ = bool(false)`, `false`, `bool`, `false`}, - {`package c0b; var _ = bool(false)`, `bool(false)`, `bool`, `false`}, - {`package c0c; type T bool; var _ = T(false)`, `T(false)`, `c0c.T`, `false`}, - - {`package c1a; var _ = int(0)`, `0`, `int`, `0`}, - {`package c1b; var _ = int(0)`, `int(0)`, `int`, `0`}, - {`package c1c; type T int; var _ = T(0)`, `T(0)`, `c1c.T`, `0`}, - - {`package c2a; var _ = rune('A')`, `'A'`, `rune`, `65`}, - {`package c2b; var _ = rune('A')`, `rune('A')`, `rune`, `65`}, - {`package c2c; type T rune; var _ = T('A')`, `T('A')`, `c2c.T`, `65`}, - - {`package c3a; var _ = float32(0.)`, `0.`, `float32`, `0`}, - {`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`}, - {`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`}, - - {`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `0`}, - {`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `0`}, - {`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `0`}, - - {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`}, - {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`}, - {`package c5c; type T string; var _ = T("foo")`, `T("foo")`, `c5c.T`, `"foo"`}, - - {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`}, - {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`}, - {`package d2; var _ = []byte(string("foo"))`, `string("foo")`, `string`, `"foo"`}, - {`package d3; type T []byte; var _ = T("foo")`, `"foo"`, `string`, `"foo"`}, - - {`package e0; const _ = float32( 1e-200)`, `float32(1e-200)`, `float32`, `0`}, - {`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`}, - {`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`}, - {`package e3; const _ = float64(-1e-2000)`, `float64(-1e-2000)`, `float64`, `0`}, - {`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `0`}, - {`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `0`}, - {`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `0`}, - {`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `0`}, - - {`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`}, - {`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`}, - {`package f2a; var _ float64 = 1e-2000`, `1e-2000`, `float64`, `0`}, - {`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`}, - {`package f2b; var _ = 1e-2000`, `1e-2000`, `float64`, `0`}, - {`package f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`}, - {`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `0`}, - {`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `0`}, - {`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `0`}, - {`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `0`}, - {`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `0`}, - {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `0`}, - } - - for _, test := range tests { - info := Info{ - Types: make(map[ast.Expr]TypeAndValue), - } - name := mustTypecheck(t, "ValuesInfo", test.src, &info) - - // look for constant expression - var expr ast.Expr - for e := range info.Types { - if ExprString(e) == test.expr { - expr = e - break - } - } - if expr == nil { - t.Errorf("package %s: no expression found for %s", name, test.expr) - continue - } - tv := info.Types[expr] - - // check that type is correct - if got := tv.Type.String(); got != test.typ { - t.Errorf("package %s: got type %s; want %s", name, got, test.typ) - continue - } - - // check that value is correct - if got := tv.Value.String(); got != test.val { - t.Errorf("package %s: got value %s; want %s", name, got, test.val) - } - } -} - -func TestTypesInfo(t *testing.T) { - var tests = []struct { - src string - expr string // expression - typ string // value type - }{ - // single-valued expressions of untyped constants - {`package b0; var x interface{} = false`, `false`, `bool`}, - {`package b1; var x interface{} = 0`, `0`, `int`}, - {`package b2; var x interface{} = 0.`, `0.`, `float64`}, - {`package b3; var x interface{} = 0i`, `0i`, `complex128`}, - {`package b4; var x interface{} = "foo"`, `"foo"`, `string`}, - - // comma-ok expressions - {`package p0; var x interface{}; var _, _ = x.(int)`, - `x.(int)`, - `(int, bool)`, - }, - {`package p1; var x interface{}; func _() { _, _ = x.(int) }`, - `x.(int)`, - `(int, bool)`, - }, - // TODO(gri): uncomment if we accept issue 8189. - // {`package p2; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`, - // `m["foo"]`, - // `(complex128, p2.mybool)`, - // }, - // TODO(gri): remove if we accept issue 8189. - {`package p2; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`, - `m["foo"]`, - `(complex128, bool)`, - }, - {`package p3; var c chan string; var _, _ = <-c`, - `<-c`, - `(string, bool)`, - }, - - // issue 6796 - {`package issue6796_a; var x interface{}; var _, _ = (x.(int))`, - `x.(int)`, - `(int, bool)`, - }, - {`package issue6796_b; var c chan string; var _, _ = (<-c)`, - `(<-c)`, - `(string, bool)`, - }, - {`package issue6796_c; var c chan string; var _, _ = (<-c)`, - `<-c`, - `(string, bool)`, - }, - {`package issue6796_d; var c chan string; var _, _ = ((<-c))`, - `(<-c)`, - `(string, bool)`, - }, - {`package issue6796_e; func f(c chan string) { _, _ = ((<-c)) }`, - `(<-c)`, - `(string, bool)`, - }, - - // issue 7060 - {`package issue7060_a; var ( m map[int]string; x, ok = m[0] )`, - `m[0]`, - `(string, bool)`, - }, - {`package issue7060_b; var ( m map[int]string; x, ok interface{} = m[0] )`, - `m[0]`, - `(string, bool)`, - }, - {`package issue7060_c; func f(x interface{}, ok bool, m map[int]string) { x, ok = m[0] }`, - `m[0]`, - `(string, bool)`, - }, - {`package issue7060_d; var ( ch chan string; x, ok = <-ch )`, - `<-ch`, - `(string, bool)`, - }, - {`package issue7060_e; var ( ch chan string; x, ok interface{} = <-ch )`, - `<-ch`, - `(string, bool)`, - }, - {`package issue7060_f; func f(x interface{}, ok bool, ch chan string) { x, ok = <-ch }`, - `<-ch`, - `(string, bool)`, - }, - } - - for _, test := range tests { - info := Info{Types: make(map[ast.Expr]TypeAndValue)} - name := mustTypecheck(t, "TypesInfo", test.src, &info) - - // look for expression type - var typ Type - for e, tv := range info.Types { - if ExprString(e) == test.expr { - typ = tv.Type - break - } - } - if typ == nil { - t.Errorf("package %s: no type found for %s", name, test.expr) - continue - } - - // check that type is correct - if got := typ.String(); got != test.typ { - t.Errorf("package %s: got %s; want %s", name, got, test.typ) - } - } -} - -func predString(tv TypeAndValue) string { - var buf bytes.Buffer - pred := func(b bool, s string) { - if b { - if buf.Len() > 0 { - buf.WriteString(", ") - } - buf.WriteString(s) - } - } - - pred(tv.IsVoid(), "void") - pred(tv.IsType(), "type") - pred(tv.IsBuiltin(), "builtin") - pred(tv.IsValue() && tv.Value != nil, "const") - pred(tv.IsValue() && tv.Value == nil, "value") - pred(tv.IsNil(), "nil") - pred(tv.Addressable(), "addressable") - pred(tv.Assignable(), "assignable") - pred(tv.HasOk(), "hasOk") - - if buf.Len() == 0 { - return "invalid" - } - return buf.String() -} - -func TestPredicatesInfo(t *testing.T) { - skipSpecialPlatforms(t) - - var tests = []struct { - src string - expr string - pred string - }{ - // void - {`package n0; func f() { f() }`, `f()`, `void`}, - - // types - {`package t0; type _ int`, `int`, `type`}, - {`package t1; type _ []int`, `[]int`, `type`}, - {`package t2; type _ func()`, `func()`, `type`}, - - // built-ins - {`package b0; var _ = len("")`, `len`, `builtin`}, - {`package b1; var _ = (len)("")`, `(len)`, `builtin`}, - - // constants - {`package c0; var _ = 42`, `42`, `const`}, - {`package c1; var _ = "foo" + "bar"`, `"foo" + "bar"`, `const`}, - {`package c2; const (i = 1i; _ = i)`, `i`, `const`}, - - // values - {`package v0; var (a, b int; _ = a + b)`, `a + b`, `value`}, - {`package v1; var _ = &[]int{1}`, `([]int literal)`, `value`}, - {`package v2; var _ = func(){}`, `(func() literal)`, `value`}, - {`package v4; func f() { _ = f }`, `f`, `value`}, - {`package v3; var _ *int = nil`, `nil`, `value, nil`}, - {`package v3; var _ *int = (nil)`, `(nil)`, `value, nil`}, - - // addressable (and thus assignable) operands - {`package a0; var (x int; _ = x)`, `x`, `value, addressable, assignable`}, - {`package a1; var (p *int; _ = *p)`, `*p`, `value, addressable, assignable`}, - {`package a2; var (s []int; _ = s[0])`, `s[0]`, `value, addressable, assignable`}, - {`package a3; var (s struct{f int}; _ = s.f)`, `s.f`, `value, addressable, assignable`}, - {`package a4; var (a [10]int; _ = a[0])`, `a[0]`, `value, addressable, assignable`}, - {`package a5; func _(x int) { _ = x }`, `x`, `value, addressable, assignable`}, - {`package a6; func _()(x int) { _ = x; return }`, `x`, `value, addressable, assignable`}, - {`package a7; type T int; func (x T) _() { _ = x }`, `x`, `value, addressable, assignable`}, - // composite literals are not addressable - - // assignable but not addressable values - {`package s0; var (m map[int]int; _ = m[0])`, `m[0]`, `value, assignable, hasOk`}, - {`package s1; var (m map[int]int; _, _ = m[0])`, `m[0]`, `value, assignable, hasOk`}, - - // hasOk expressions - {`package k0; var (ch chan int; _ = <-ch)`, `<-ch`, `value, hasOk`}, - {`package k1; var (ch chan int; _, _ = <-ch)`, `<-ch`, `value, hasOk`}, - - // missing entries - // - package names are collected in the Uses map - // - identifiers being declared are collected in the Defs map - {`package m0; import "os"; func _() { _ = os.Stdout }`, `os`, ``}, - {`package m1; import p "os"; func _() { _ = p.Stdout }`, `p`, ``}, - {`package m2; const c = 0`, `c`, ``}, - {`package m3; type T int`, `T`, ``}, - {`package m4; var v int`, `v`, ``}, - {`package m5; func f() {}`, `f`, ``}, - {`package m6; func _(x int) {}`, `x`, ``}, - {`package m6; func _()(x int) { return }`, `x`, ``}, - {`package m6; type T int; func (x T) _() {}`, `x`, ``}, - } - - for _, test := range tests { - info := Info{Types: make(map[ast.Expr]TypeAndValue)} - name := mustTypecheck(t, "PredicatesInfo", test.src, &info) - - // look for expression predicates - got := "" - for e, tv := range info.Types { - //println(name, ExprString(e)) - if ExprString(e) == test.expr { - got = predString(tv) - break - } - } - - if got != test.pred { - t.Errorf("package %s: got %s; want %s", name, got, test.pred) - } - } -} - -func TestScopesInfo(t *testing.T) { - skipSpecialPlatforms(t) - - var tests = []struct { - src string - scopes []string // list of scope descriptors of the form kind:varlist - }{ - {`package p0`, []string{ - "file:", - }}, - {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{ - "file:fmt m", - }}, - {`package p2; func _() {}`, []string{ - "file:", "func:", - }}, - {`package p3; func _(x, y int) {}`, []string{ - "file:", "func:x y", - }}, - {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{ - "file:", "func:x y z", // redeclaration of x - }}, - {`package p5; func _(x, y int) (u, _ int) { return }`, []string{ - "file:", "func:u x y", - }}, - {`package p6; func _() { { var x int; _ = x } }`, []string{ - "file:", "func:", "block:x", - }}, - {`package p7; func _() { if true {} }`, []string{ - "file:", "func:", "if:", "block:", - }}, - {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{ - "file:", "func:", "if:x", "block:y", - }}, - {`package p9; func _() { switch x := 0; x {} }`, []string{ - "file:", "func:", "switch:x", - }}, - {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{ - "file:", "func:", "switch:x", "case:y", "case:", - }}, - {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{ - "file:", "func:t", "type switch:", - }}, - {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{ - "file:", "func:t", "type switch:t", - }}, - {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{ - "file:", "func:t", "type switch:", "case:x", // x implicitly declared - }}, - {`package p14; func _() { select{} }`, []string{ - "file:", "func:", - }}, - {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{ - "file:", "func:c", "comm:", - }}, - {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{ - "file:", "func:c", "comm:i x", - }}, - {`package p17; func _() { for{} }`, []string{ - "file:", "func:", "for:", "block:", - }}, - {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{ - "file:", "func:n", "for:i", "block:", - }}, - {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{ - "file:", "func:a", "range:i", "block:", - }}, - {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{ - "file:", "func:a", "range:i x", "block:", - }}, - } - - for _, test := range tests { - info := Info{Scopes: make(map[ast.Node]*Scope)} - name := mustTypecheck(t, "ScopesInfo", test.src, &info) - - // number of scopes must match - if len(info.Scopes) != len(test.scopes) { - t.Errorf("package %s: got %d scopes; want %d", name, len(info.Scopes), len(test.scopes)) - } - - // scope descriptions must match - for node, scope := range info.Scopes { - kind := "" - switch node.(type) { - case *ast.File: - kind = "file" - case *ast.FuncType: - kind = "func" - case *ast.BlockStmt: - kind = "block" - case *ast.IfStmt: - kind = "if" - case *ast.SwitchStmt: - kind = "switch" - case *ast.TypeSwitchStmt: - kind = "type switch" - case *ast.CaseClause: - kind = "case" - case *ast.CommClause: - kind = "comm" - case *ast.ForStmt: - kind = "for" - case *ast.RangeStmt: - kind = "range" - } - - // look for matching scope description - desc := kind + ":" + strings.Join(scope.Names(), " ") - found := false - for _, d := range test.scopes { - if desc == d { - found = true - break - } - } - if !found { - t.Errorf("package %s: no matching scope found for %s", name, desc) - } - } - } -} - -func TestInitOrderInfo(t *testing.T) { - var tests = []struct { - src string - inits []string - }{ - {`package p0; var (x = 1; y = x)`, []string{ - "x = 1", "y = x", - }}, - {`package p1; var (a = 1; b = 2; c = 3)`, []string{ - "a = 1", "b = 2", "c = 3", - }}, - {`package p2; var (a, b, c = 1, 2, 3)`, []string{ - "a = 1", "b = 2", "c = 3", - }}, - {`package p3; var _ = f(); func f() int { return 1 }`, []string{ - "_ = f()", // blank var - }}, - {`package p4; var (a = 0; x = y; y = z; z = 0)`, []string{ - "a = 0", "z = 0", "y = z", "x = y", - }}, - {`package p5; var (a, _ = m[0]; m map[int]string)`, []string{ - "a, _ = m[0]", // blank var - }}, - {`package p6; var a, b = f(); func f() (_, _ int) { return z, z }; var z = 0`, []string{ - "z = 0", "a, b = f()", - }}, - {`package p7; var (a = func() int { return b }(); b = 1)`, []string{ - "b = 1", "a = (func() int literal)()", - }}, - {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{ - "c = 1", "a, b = (func() (_, _ int) literal)()", - }}, - {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{ - "y = 1", "x = T.m", - }}, - {`package p10; var (d = c + b; a = 0; b = 0; c = 0)`, []string{ - "a = 0", "b = 0", "c = 0", "d = c + b", - }}, - {`package p11; var (a = e + c; b = d + c; c = 0; d = 0; e = 0)`, []string{ - "c = 0", "d = 0", "b = d + c", "e = 0", "a = e + c", - }}, - // emit an initializer for n:1 initializations only once (not for each node - // on the lhs which may appear in different order in the dependency graph) - {`package p12; var (a = x; b = 0; x, y = m[0]; m map[int]int)`, []string{ - "b = 0", "x, y = m[0]", "a = x", - }}, - // test case from spec section on package initialization - {`package p12 - - var ( - a = c + b - b = f() - c = f() - d = 3 - ) - - func f() int { - d++ - return d - }`, []string{ - "d = 3", "b = f()", "c = f()", "a = c + b", - }}, - // test case for issue 7131 - {`package main - - var counter int - func next() int { counter++; return counter } - - var _ = makeOrder() - func makeOrder() []int { return []int{f, b, d, e, c, a} } - - var a = next() - var b, c = next(), next() - var d, e, f = next(), next(), next() - `, []string{ - "a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()", - }}, - } - - for _, test := range tests { - info := Info{} - name := mustTypecheck(t, "InitOrderInfo", test.src, &info) - - // number of initializers must match - if len(info.InitOrder) != len(test.inits) { - t.Errorf("package %s: got %d initializers; want %d", name, len(info.InitOrder), len(test.inits)) - continue - } - - // initializers must match - for i, want := range test.inits { - got := info.InitOrder[i].String() - if got != want { - t.Errorf("package %s, init %d: got %s; want %s", name, i, got, want) - continue - } - } - } -} - -func TestMultiFileInitOrder(t *testing.T) { - fset := token.NewFileSet() - mustParse := func(src string) *ast.File { - f, err := parser.ParseFile(fset, "main", src, 0) - if err != nil { - t.Fatal(err) - } - return f - } - - fileA := mustParse(`package main; var a = 1`) - fileB := mustParse(`package main; var b = 2`) - - // The initialization order must not depend on the parse - // order of the files, only on the presentation order to - // the type-checker. - for _, test := range []struct { - files []*ast.File - want string - }{ - {[]*ast.File{fileA, fileB}, "[a = 1 b = 2]"}, - {[]*ast.File{fileB, fileA}, "[b = 2 a = 1]"}, - } { - var info Info - if _, err := new(Config).Check("main", fset, test.files, &info); err != nil { - t.Fatal(err) - } - if got := fmt.Sprint(info.InitOrder); got != test.want { - t.Fatalf("got %s; want %s", got, test.want) - } - } -} - -func TestFiles(t *testing.T) { - var sources = []string{ - "package p; type T struct{}; func (T) m1() {}", - "package p; func (T) m2() {}; var x interface{ m1(); m2() } = T{}", - "package p; func (T) m3() {}; var y interface{ m1(); m2(); m3() } = T{}", - "package p", - } - - var conf Config - fset := token.NewFileSet() - pkg := NewPackage("p", "p") - var info Info - check := NewChecker(&conf, fset, pkg, &info) - - for i, src := range sources { - filename := fmt.Sprintf("sources%d", i) - f, err := parser.ParseFile(fset, filename, src, 0) - if err != nil { - t.Fatal(err) - } - if err := check.Files([]*ast.File{f}); err != nil { - t.Error(err) - } - } - - // check InitOrder is [x y] - var vars []string - for _, init := range info.InitOrder { - for _, v := range init.Lhs { - vars = append(vars, v.Name()) - } - } - if got, want := fmt.Sprint(vars), "[x y]"; got != want { - t.Errorf("InitOrder == %s, want %s", got, want) - } -} - -func TestSelection(t *testing.T) { - selections := make(map[*ast.SelectorExpr]*Selection) - - fset := token.NewFileSet() - conf := Config{ - Packages: make(map[string]*Package), - Import: func(imports map[string]*Package, path string) (*Package, error) { - return imports[path], nil - }, - } - makePkg := func(path, src string) { - f, err := parser.ParseFile(fset, path+".go", src, 0) - if err != nil { - t.Fatal(err) - } - pkg, err := conf.Check(path, fset, []*ast.File{f}, &Info{Selections: selections}) - if err != nil { - t.Fatal(err) - } - conf.Packages[path] = pkg - } - - const libSrc = ` -package lib -type T float64 -const C T = 3 -var V T -func F() {} -func (T) M() {} -` - const mainSrc = ` -package main -import "lib" - -type A struct { - *B - C -} - -type B struct { - b int -} - -func (B) f(int) - -type C struct { - c int -} - -func (C) g() -func (*C) h() - -func main() { - // qualified identifiers - var _ lib.T - _ = lib.C - _ = lib.F - _ = lib.V - _ = lib.T.M - - // fields - _ = A{}.B - _ = new(A).B - - _ = A{}.C - _ = new(A).C - - _ = A{}.b - _ = new(A).b - - _ = A{}.c - _ = new(A).c - - // methods - _ = A{}.f - _ = new(A).f - _ = A{}.g - _ = new(A).g - _ = new(A).h - - _ = B{}.f - _ = new(B).f - - _ = C{}.g - _ = new(C).g - _ = new(C).h - - // method expressions - _ = A.f - _ = (*A).f - _ = B.f - _ = (*B).f -}` - - wantOut := map[string][2]string{ - "lib.T.M": {"method expr (lib.T) M(lib.T)", ".[0]"}, - - "A{}.B": {"field (main.A) B *main.B", ".[0]"}, - "new(A).B": {"field (*main.A) B *main.B", "->[0]"}, - "A{}.C": {"field (main.A) C main.C", ".[1]"}, - "new(A).C": {"field (*main.A) C main.C", "->[1]"}, - "A{}.b": {"field (main.A) b int", "->[0 0]"}, - "new(A).b": {"field (*main.A) b int", "->[0 0]"}, - "A{}.c": {"field (main.A) c int", ".[1 0]"}, - "new(A).c": {"field (*main.A) c int", "->[1 0]"}, - - "A{}.f": {"method (main.A) f(int)", "->[0 0]"}, - "new(A).f": {"method (*main.A) f(int)", "->[0 0]"}, - "A{}.g": {"method (main.A) g()", ".[1 0]"}, - "new(A).g": {"method (*main.A) g()", "->[1 0]"}, - "new(A).h": {"method (*main.A) h()", "->[1 1]"}, // TODO(gri) should this report .[1 1] ? - "B{}.f": {"method (main.B) f(int)", ".[0]"}, - "new(B).f": {"method (*main.B) f(int)", "->[0]"}, - "C{}.g": {"method (main.C) g()", ".[0]"}, - "new(C).g": {"method (*main.C) g()", "->[0]"}, - "new(C).h": {"method (*main.C) h()", "->[1]"}, // TODO(gri) should this report .[1] ? - - "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"}, - "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"}, - "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"}, - "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"}, - } - - makePkg("lib", libSrc) - makePkg("main", mainSrc) - - for e, sel := range selections { - sel.String() // assertion: must not panic - - start := fset.Position(e.Pos()).Offset - end := fset.Position(e.End()).Offset - syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib) - - direct := "." - if sel.Indirect() { - direct = "->" - } - got := [2]string{ - sel.String(), - fmt.Sprintf("%s%v", direct, sel.Index()), - } - want := wantOut[syntax] - if want != got { - t.Errorf("%s: got %q; want %q", syntax, got, want) - } - delete(wantOut, syntax) - - // We must explicitly assert properties of the - // Signature's receiver since it doesn't participate - // in Identical() or String(). - sig, _ := sel.Type().(*Signature) - if sel.Kind() == MethodVal { - got := sig.Recv().Type() - want := sel.Recv() - if !Identical(got, want) { - t.Errorf("%s: Recv() = %s, want %s", syntax, got, want) - } - } else if sig != nil && sig.Recv() != nil { - t.Errorf("%s: signature has receiver %s", sig, sig.Recv().Type()) - } - } - // Assert that all wantOut entries were used exactly once. - for syntax := range wantOut { - t.Errorf("no ast.Selection found with syntax %q", syntax) - } -} - -func TestIssue8518(t *testing.T) { - fset := token.NewFileSet() - conf := Config{ - Packages: make(map[string]*Package), - Error: func(err error) { t.Log(err) }, // don't exit after first error - Import: func(imports map[string]*Package, path string) (*Package, error) { - return imports[path], nil - }, - } - makePkg := func(path, src string) { - f, err := parser.ParseFile(fset, path, src, 0) - if err != nil { - t.Fatal(err) - } - pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error - conf.Packages[path] = pkg - } - - const libSrc = ` -package a -import "missing" -const C1 = foo -const C2 = missing.C -` - - const mainSrc = ` -package main -import "a" -var _ = a.C1 -var _ = a.C2 -` - - makePkg("a", libSrc) - makePkg("main", mainSrc) // don't crash when type-checking this package -} - -func TestLookupFieldOrMethod(t *testing.T) { - // Test cases assume a lookup of the form a.f or x.f, where a stands for an - // addressable value, and x for a non-addressable value (even though a variable - // for ease of test case writing). - var tests = []struct { - src string - found bool - index []int - indirect bool - }{ - // field lookups - {"var x T; type T struct{}", false, nil, false}, - {"var x T; type T struct{ f int }", true, []int{0}, false}, - {"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false}, - - // method lookups - {"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false}, - {"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true}, - {"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false}, - {"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false? - - // collisions - {"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false}, - {"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false}, - - // outside methodset - // (*T).f method exists, but value of type T is not addressable - {"var x T; type T struct{}; func (*T) f() {}", false, nil, true}, - } - - for _, test := range tests { - pkg, err := pkgFor("test", "package p;"+test.src, nil) - if err != nil { - t.Errorf("%s: incorrect test case: %s", test.src, err) - continue - } - - obj := pkg.Scope().Lookup("a") - if obj == nil { - if obj = pkg.Scope().Lookup("x"); obj == nil { - t.Errorf("%s: incorrect test case - no object a or x", test.src) - continue - } - } - - f, index, indirect := LookupFieldOrMethod(obj.Type(), obj.Name() == "a", pkg, "f") - if (f != nil) != test.found { - if f == nil { - t.Errorf("%s: got no object; want one", test.src) - } else { - t.Errorf("%s: got object = %v; want none", test.src, f) - } - } - if !sameSlice(index, test.index) { - t.Errorf("%s: got index = %v; want %v", test.src, index, test.index) - } - if indirect != test.indirect { - t.Errorf("%s: got indirect = %v; want %v", test.src, indirect, test.indirect) - } - } -} - -func sameSlice(a, b []int) bool { - if len(a) != len(b) { - return false - } - for i, x := range a { - if x != b[i] { - return false - } - } - return true -} - -// TestScopeLookupParent ensures that (*Scope).LookupParent returns -// the correct result at various positions with the source. -func TestScopeLookupParent(t *testing.T) { - fset := token.NewFileSet() - conf := Config{ - Packages: make(map[string]*Package), - Import: func(imports map[string]*Package, path string) (*Package, error) { - return imports[path], nil - }, - } - mustParse := func(src string) *ast.File { - f, err := parser.ParseFile(fset, "dummy.go", src, parser.ParseComments) - if err != nil { - t.Fatal(err) - } - return f - } - var info Info - makePkg := func(path string, files ...*ast.File) { - conf.Packages[path], _ = conf.Check(path, fset, files, &info) - } - - makePkg("lib", mustParse("package lib; var X int")) - // Each /*name=kind:line*/ comment makes the test look up the - // name at that point and checks that it resolves to a decl of - // the specified kind and line number. "undef" means undefined. - mainSrc := ` -package main -import "lib" -var Y = lib.X -func f() { - print(Y) /*Y=var:4*/ - z /*z=undef*/ := /*z=undef*/ 1 /*z=var:7*/ - print(z) - /*f=func:5*/ /*lib=pkgname:3*/ - type /*T=undef*/ T /*T=typename:10*/ *T -} -` - info.Uses = make(map[*ast.Ident]Object) - f := mustParse(mainSrc) - makePkg("main", f) - mainScope := conf.Packages["main"].Scope() - rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`) - for _, group := range f.Comments { - for _, comment := range group.List { - // Parse the assertion in the comment. - m := rx.FindStringSubmatch(comment.Text) - if m == nil { - t.Errorf("%s: bad comment: %s", - fset.Position(comment.Pos()), comment.Text) - continue - } - name, want := m[1], m[2] - - // Look up the name in the innermost enclosing scope. - inner := mainScope.Innermost(comment.Pos()) - if inner == nil { - t.Errorf("%s: at %s: can't find innermost scope", - fset.Position(comment.Pos()), comment.Text) - continue - } - got := "undef" - if _, obj := inner.LookupParent(name, comment.Pos()); obj != nil { - kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) - got = fmt.Sprintf("%s:%d", kind, fset.Position(obj.Pos()).Line) - } - if got != want { - t.Errorf("%s: at %s: %s resolved to %s, want %s", - fset.Position(comment.Pos()), comment.Text, name, got, want) - } - } - } - - // Check that for each referring identifier, - // a lookup of its name on the innermost - // enclosing scope returns the correct object. - - for id, wantObj := range info.Uses { - inner := mainScope.Innermost(id.Pos()) - if inner == nil { - t.Errorf("%s: can't find innermost scope enclosing %q", - fset.Position(id.Pos()), id.Name) - continue - } - - // Exclude selectors and qualified identifiers---lexical - // refs only. (Ideally, we'd see if the AST parent is a - // SelectorExpr, but that requires PathEnclosingInterval - // from golang.org/x/tools/go/ast/astutil.) - if id.Name == "X" { - continue - } - - _, gotObj := inner.LookupParent(id.Name, id.Pos()) - if gotObj != wantObj { - t.Errorf("%s: got %v, want %v", - fset.Position(id.Pos()), gotObj, wantObj) - continue - } - } -} diff --git a/go/types/assignments.go b/go/types/assignments.go deleted file mode 100644 index 93b842eaa0..0000000000 --- a/go/types/assignments.go +++ /dev/null @@ -1,328 +0,0 @@ -// 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. - -// This file implements initialization and assignment checks. - -package types - -import ( - "go/ast" - "go/token" -) - -// assignment reports whether x can be assigned to a variable of type T, -// if necessary by attempting to convert untyped values to the appropriate -// type. If x.mode == invalid upon return, then assignment has already -// issued an error message and the caller doesn't have to report another. -// Use T == nil to indicate assignment to an untyped blank identifier. -// -// TODO(gri) Should find a better way to handle in-band errors. -// -func (check *Checker) assignment(x *operand, T Type) bool { - switch x.mode { - case invalid: - return true // error reported before - case constant, variable, mapindex, value, commaok: - // ok - default: - unreachable() - } - - // x must be a single value - // (tuple types are never named - no need for underlying type) - if t, _ := x.typ.(*Tuple); t != nil { - assert(t.Len() > 1) - check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x) - x.mode = invalid - return false - } - - if isUntyped(x.typ) { - target := T - // spec: "If an untyped constant is assigned to a variable of interface - // type or the blank identifier, the constant is first converted to type - // bool, rune, int, float64, complex128 or string respectively, depending - // on whether the value is a boolean, rune, integer, floating-point, complex, - // or string constant." - if T == nil || IsInterface(T) { - if T == nil && x.typ == Typ[UntypedNil] { - check.errorf(x.pos(), "use of untyped nil") - x.mode = invalid - return false - } - target = defaultType(x.typ) - } - check.convertUntyped(x, target) - if x.mode == invalid { - return false - } - } - - // spec: "If a left-hand side is the blank identifier, any typed or - // non-constant value except for the predeclared identifier nil may - // be assigned to it." - return T == nil || x.assignableTo(check.conf, T) -} - -func (check *Checker) initConst(lhs *Const, x *operand) { - if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { - if lhs.typ == nil { - lhs.typ = Typ[Invalid] - } - return - } - - // rhs must be a constant - if x.mode != constant { - check.errorf(x.pos(), "%s is not constant", x) - if lhs.typ == nil { - lhs.typ = Typ[Invalid] - } - return - } - assert(isConstType(x.typ)) - - // If the lhs doesn't have a type yet, use the type of x. - if lhs.typ == nil { - lhs.typ = x.typ - } - - if !check.assignment(x, lhs.typ) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x) - } - return - } - - lhs.val = x.val -} - -// If result is set, lhs is a function result parameter and x is a return result. -func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type { - if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] { - if lhs.typ == nil { - lhs.typ = Typ[Invalid] - } - return nil - } - - // If the lhs doesn't have a type yet, use the type of x. - if lhs.typ == nil { - typ := x.typ - if isUntyped(typ) { - // convert untyped types to default types - if typ == Typ[UntypedNil] { - check.errorf(x.pos(), "use of untyped nil") - lhs.typ = Typ[Invalid] - return nil - } - typ = defaultType(typ) - } - lhs.typ = typ - } - - if !check.assignment(x, lhs.typ) { - if x.mode != invalid { - if result { - // don't refer to lhs.name because it may be an anonymous result parameter - check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ) - } else { - check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x) - } - } - return nil - } - - return x.typ -} - -func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { - if x.mode == invalid || x.typ == Typ[Invalid] { - return nil - } - - // Determine if the lhs is a (possibly parenthesized) identifier. - ident, _ := unparen(lhs).(*ast.Ident) - - // Don't evaluate lhs if it is the blank identifier. - if ident != nil && ident.Name == "_" { - check.recordDef(ident, nil) - if !check.assignment(x, nil) { - assert(x.mode == invalid) - x.typ = nil - } - return x.typ - } - - // If the lhs is an identifier denoting a variable v, this assignment - // is not a 'use' of v. Remember current value of v.used and restore - // after evaluating the lhs via check.expr. - var v *Var - var v_used bool - if ident != nil { - if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { - v, _ = obj.(*Var) - if v != nil { - v_used = v.used - } - } - } - - var z operand - check.expr(&z, lhs) - if v != nil { - v.used = v_used // restore v.used - } - - if z.mode == invalid || z.typ == Typ[Invalid] { - return nil - } - - // spec: "Each left-hand side operand must be addressable, a map index - // expression, or the blank identifier. Operands may be parenthesized." - switch z.mode { - case invalid: - return nil - case variable, mapindex: - // ok - default: - check.errorf(z.pos(), "cannot assign to %s", &z) - return nil - } - - if !check.assignment(x, z.typ) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot assign %s to %s", x, &z) - } - return nil - } - - return x.typ -} - -// If returnPos is valid, initVars is called to type-check the assignment of -// return expressions, and returnPos is the position of the return statement. -func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) { - l := len(lhs) - get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid()) - if get == nil || l != r { - // invalidate lhs and use rhs - for _, obj := range lhs { - if obj.typ == nil { - obj.typ = Typ[Invalid] - } - } - if get == nil { - return // error reported by unpack - } - check.useGetter(get, r) - if returnPos.IsValid() { - check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r) - return - } - check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) - return - } - - var x operand - if commaOk { - var a [2]Type - for i := range a { - get(&x, i) - a[i] = check.initVar(lhs[i], &x, returnPos.IsValid()) - } - check.recordCommaOkTypes(rhs[0], a) - return - } - - for i, lhs := range lhs { - get(&x, i) - check.initVar(lhs, &x, returnPos.IsValid()) - } -} - -func (check *Checker) assignVars(lhs, rhs []ast.Expr) { - l := len(lhs) - get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2) - if get == nil { - return // error reported by unpack - } - if l != r { - check.useGetter(get, r) - check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) - return - } - - var x operand - if commaOk { - var a [2]Type - for i := range a { - get(&x, i) - a[i] = check.assignVar(lhs[i], &x) - } - check.recordCommaOkTypes(rhs[0], a) - return - } - - for i, lhs := range lhs { - get(&x, i) - check.assignVar(lhs, &x) - } -} - -func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { - scope := check.scope - - // collect lhs variables - var newVars []*Var - var lhsVars = make([]*Var, len(lhs)) - for i, lhs := range lhs { - var obj *Var - if ident, _ := lhs.(*ast.Ident); ident != nil { - // Use the correct obj if the ident is redeclared. The - // variable's scope starts after the declaration; so we - // must use Scope.Lookup here and call Scope.Insert - // (via check.declare) later. - name := ident.Name - if alt := scope.Lookup(name); alt != nil { - // redeclared object must be a variable - if alt, _ := alt.(*Var); alt != nil { - obj = alt - } else { - check.errorf(lhs.Pos(), "cannot assign to %s", lhs) - } - check.recordUse(ident, alt) - } else { - // declare new variable, possibly a blank (_) variable - obj = NewVar(ident.Pos(), check.pkg, name, nil) - if name != "_" { - newVars = append(newVars, obj) - } - check.recordDef(ident, obj) - } - } else { - check.errorf(lhs.Pos(), "cannot declare %s", lhs) - } - if obj == nil { - obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable - } - lhsVars[i] = obj - } - - check.initVars(lhsVars, rhs, token.NoPos) - - // declare new variables - if len(newVars) > 0 { - // spec: "The scope of a constant or variable identifier declared inside - // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl - // for short variable declarations) and ends at the end of the innermost - // containing block." - scopePos := rhs[len(rhs)-1].End() - for _, obj := range newVars { - check.declare(scope, nil, obj, scopePos) // recordObject already called - } - } else { - check.softErrorf(pos, "no new variables on left side of :=") - } -} diff --git a/go/types/builtins.go b/go/types/builtins.go deleted file mode 100644 index f45f930a42..0000000000 --- a/go/types/builtins.go +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright 2012 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. - -// This file implements typechecking of builtin function calls. - -package types - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -// builtin type-checks a call to the built-in specified by id and -// returns true if the call is valid, with *x holding the result; -// but x.expr is not set. If the call is invalid, the result is -// false, and *x is undefined. -// -func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { - // append is the only built-in that permits the use of ... for the last argument - bin := predeclaredFuncs[id] - if call.Ellipsis.IsValid() && id != _Append { - check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name) - check.use(call.Args...) - return - } - - // For len(x) and cap(x) we need to know if x contains any function calls or - // receive operations. Save/restore current setting and set hasCallOrRecv to - // false for the evaluation of x so that we can check it afterwards. - // Note: We must do this _before_ calling unpack because unpack evaluates the - // first argument before we even call arg(x, 0)! - if id == _Len || id == _Cap { - defer func(b bool) { - check.hasCallOrRecv = b - }(check.hasCallOrRecv) - check.hasCallOrRecv = false - } - - // determine actual arguments - var arg getter - nargs := len(call.Args) - switch id { - default: - // make argument getter - arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false) - if arg == nil { - return - } - // evaluate first argument, if present - if nargs > 0 { - arg(x, 0) - if x.mode == invalid { - return - } - } - case _Make, _New, _Offsetof, _Trace: - // arguments require special handling - } - - // check argument count - { - msg := "" - if nargs < bin.nargs { - msg = "not enough" - } else if !bin.variadic && nargs > bin.nargs { - msg = "too many" - } - if msg != "" { - check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs) - return - } - } - - switch id { - case _Append: - // append(s S, x ...T) S, where T is the element type of S - // spec: "The variadic function append appends zero or more values x to s of type - // S, which must be a slice type, and returns the resulting slice, also of type S. - // The values x are passed to a parameter of type ...T where T is the element type - // of S and the respective parameter passing rules apply." - S := x.typ - var T Type - if s, _ := S.Underlying().(*Slice); s != nil { - T = s.elem - } else { - check.invalidArg(x.pos(), "%s is not a slice", x) - return - } - - // remember arguments that have been evaluated already - alist := []operand{*x} - - // spec: "As a special case, append also accepts a first argument assignable - // to type []byte with a second argument of string type followed by ... . - // This form appends the bytes of the string. - if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) { - arg(x, 1) - if x.mode == invalid { - return - } - if isString(x.typ) { - if check.Types != nil { - sig := makeSig(S, S, x.typ) - sig.variadic = true - check.recordBuiltinType(call.Fun, sig) - } - x.mode = value - x.typ = S - break - } - alist = append(alist, *x) - // fallthrough - } - - // check general case by creating custom signature - sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature - sig.variadic = true - check.arguments(x, call, sig, func(x *operand, i int) { - // only evaluate arguments that have not been evaluated before - if i < len(alist) { - *x = alist[i] - return - } - arg(x, i) - }, nargs) - // ok to continue even if check.arguments reported errors - - x.mode = value - x.typ = S - if check.Types != nil { - check.recordBuiltinType(call.Fun, sig) - } - - case _Cap, _Len: - // cap(x) - // len(x) - mode := invalid - var typ Type - var val exact.Value - switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { - case *Basic: - if isString(t) && id == _Len { - if x.mode == constant { - mode = constant - val = exact.MakeInt64(int64(len(exact.StringVal(x.val)))) - } else { - mode = value - } - } - - case *Array: - mode = value - // spec: "The expressions len(s) and cap(s) are constants - // if the type of s is an array or pointer to an array and - // the expression s does not contain channel receives or - // function calls; in this case s is not evaluated." - if !check.hasCallOrRecv { - mode = constant - val = exact.MakeInt64(t.len) - } - - case *Slice, *Chan: - mode = value - - case *Map: - if id == _Len { - mode = value - } - } - - if mode == invalid { - check.invalidArg(x.pos(), "%s for %s", x, bin.name) - return - } - - x.mode = mode - x.typ = Typ[Int] - x.val = val - if check.Types != nil && mode != constant { - check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) - } - - case _Close: - // close(c) - c, _ := x.typ.Underlying().(*Chan) - if c == nil { - check.invalidArg(x.pos(), "%s is not a channel", x) - return - } - if c.dir == RecvOnly { - check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) - return - } - - x.mode = novalue - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, c)) - } - - case _Complex: - // complex(x, y realT) complexT - if !check.complexArg(x) { - return - } - - var y operand - arg(&y, 1) - if y.mode == invalid { - return - } - if !check.complexArg(&y) { - return - } - - check.convertUntyped(x, y.typ) - if x.mode == invalid { - return - } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - return - } - - if !Identical(x.typ, y.typ) { - check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) - return - } - - if x.mode == constant && y.mode == constant { - x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val)) - } else { - x.mode = value - } - - realT := x.typ - complexT := Typ[Invalid] - switch realT.Underlying().(*Basic).kind { - case Float32: - complexT = Typ[Complex64] - case Float64: - complexT = Typ[Complex128] - case UntypedInt, UntypedRune, UntypedFloat: - if x.mode == constant { - realT = defaultType(realT).(*Basic) - complexT = Typ[UntypedComplex] - } else { - // untyped but not constant; probably because one - // operand is a non-constant shift of untyped lhs - realT = Typ[Float64] - complexT = Typ[Complex128] - } - default: - check.invalidArg(x.pos(), "float32 or float64 arguments expected") - return - } - - x.typ = complexT - if check.Types != nil && x.mode != constant { - check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) - } - - if x.mode != constant { - // The arguments have now their final types, which at run- - // time will be materialized. Update the expression trees. - // If the current types are untyped, the materialized type - // is the respective default type. - // (If the result is constant, the arguments are never - // materialized and there is nothing to do.) - check.updateExprType(x.expr, realT, true) - check.updateExprType(y.expr, realT, true) - } - - case _Copy: - // copy(x, y []T) int - var dst Type - if t, _ := x.typ.Underlying().(*Slice); t != nil { - dst = t.elem - } - - var y operand - arg(&y, 1) - if y.mode == invalid { - return - } - var src Type - switch t := y.typ.Underlying().(type) { - case *Basic: - if isString(y.typ) { - src = universeByte - } - case *Slice: - src = t.elem - } - - if dst == nil || src == nil { - check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) - return - } - - if !Identical(dst, src) { - check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) - return - } - - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ)) - } - x.mode = value - x.typ = Typ[Int] - - case _Delete: - // delete(m, k) - m, _ := x.typ.Underlying().(*Map) - if m == nil { - check.invalidArg(x.pos(), "%s is not a map", x) - return - } - arg(x, 1) // k - if x.mode == invalid { - return - } - - if !x.assignableTo(check.conf, m.key) { - check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key) - return - } - - x.mode = novalue - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) - } - - case _Imag, _Real: - // imag(complexT) realT - // real(complexT) realT - if !isComplex(x.typ) { - check.invalidArg(x.pos(), "%s must be a complex number", x) - return - } - if x.mode == constant { - if id == _Real { - x.val = exact.Real(x.val) - } else { - x.val = exact.Imag(x.val) - } - } else { - x.mode = value - } - var k BasicKind - switch x.typ.Underlying().(*Basic).kind { - case Complex64: - k = Float32 - case Complex128: - k = Float64 - case UntypedComplex: - k = UntypedFloat - default: - unreachable() - } - - if check.Types != nil && x.mode != constant { - check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) - } - x.typ = Typ[k] - - case _Make: - // make(T, n) - // make(T, n, m) - // (no argument evaluated yet) - arg0 := call.Args[0] - T := check.typ(arg0) - if T == Typ[Invalid] { - return - } - - var min int // minimum number of arguments - switch T.Underlying().(type) { - case *Slice: - min = 2 - case *Map, *Chan: - min = 1 - default: - check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) - return - } - if nargs < min || min+1 < nargs { - check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs) - return - } - var sizes []int64 // constant integer arguments, if any - for _, arg := range call.Args[1:] { - if s, ok := check.index(arg, -1); ok && s >= 0 { - sizes = append(sizes, s) - } - } - if len(sizes) == 2 && sizes[0] > sizes[1] { - check.invalidArg(call.Args[1].Pos(), "length and capacity swapped") - // safe to continue - } - x.mode = value - x.typ = T - if check.Types != nil { - params := [...]Type{T, Typ[Int], Typ[Int]} - check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...)) - } - - case _New: - // new(T) - // (no argument evaluated yet) - T := check.typ(call.Args[0]) - if T == Typ[Invalid] { - return - } - - x.mode = value - x.typ = &Pointer{base: T} - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(x.typ, T)) - } - - case _Panic: - // panic(x) - T := new(Interface) - if !check.assignment(x, T) { - assert(x.mode == invalid) - return - } - - x.mode = novalue - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, T)) - } - - case _Print, _Println: - // print(x, y, ...) - // println(x, y, ...) - var params []Type - if nargs > 0 { - params = make([]Type, nargs) - for i := 0; i < nargs; i++ { - if i > 0 { - arg(x, i) // first argument already evaluated - } - if !check.assignment(x, nil) { - assert(x.mode == invalid) - return - } - params[i] = x.typ - } - } - - x.mode = novalue - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, params...)) - } - - case _Recover: - // recover() interface{} - x.mode = value - x.typ = new(Interface) - if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(x.typ)) - } - - case _Alignof: - // unsafe.Alignof(x T) uintptr - if !check.assignment(x, nil) { - assert(x.mode == invalid) - return - } - - x.mode = constant - x.val = exact.MakeInt64(check.conf.alignof(x.typ)) - x.typ = Typ[Uintptr] - // result is constant - no need to record signature - - case _Offsetof: - // unsafe.Offsetof(x T) uintptr, where x must be a selector - // (no argument evaluated yet) - arg0 := call.Args[0] - selx, _ := unparen(arg0).(*ast.SelectorExpr) - if selx == nil { - check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) - check.use(arg0) - return - } - - check.expr(x, selx.X) - if x.mode == invalid { - return - } - - base := derefStructPtr(x.typ) - sel := selx.Sel.Name - obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) - switch obj.(type) { - case nil: - check.invalidArg(x.pos(), "%s has no single field %s", base, sel) - return - case *Func: - // TODO(gri) Using derefStructPtr may result in methods being found - // that don't actually exist. An error either way, but the error - // message is confusing. See: http://play.golang.org/p/al75v23kUy , - // but go/types reports: "invalid argument: x.m is a method value". - check.invalidArg(arg0.Pos(), "%s is a method value", arg0) - return - } - if indirect { - check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) - return - } - - // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? - check.recordSelection(selx, FieldVal, base, obj, index, false) - - offs := check.conf.offsetof(base, index) - x.mode = constant - x.val = exact.MakeInt64(offs) - x.typ = Typ[Uintptr] - // result is constant - no need to record signature - - case _Sizeof: - // unsafe.Sizeof(x T) uintptr - if !check.assignment(x, nil) { - assert(x.mode == invalid) - return - } - - x.mode = constant - x.val = exact.MakeInt64(check.conf.sizeof(x.typ)) - x.typ = Typ[Uintptr] - // result is constant - no need to record signature - - case _Assert: - // assert(pred) causes a typechecker error if pred is false. - // The result of assert is the value of pred if there is no error. - // Note: assert is only available in self-test mode. - if x.mode != constant || !isBoolean(x.typ) { - check.invalidArg(x.pos(), "%s is not a boolean constant", x) - return - } - if x.val.Kind() != exact.Bool { - check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) - return - } - if !exact.BoolVal(x.val) { - check.errorf(call.Pos(), "%s failed", call) - // compile-time assertion failure - safe to continue - } - // result is constant - no need to record signature - - case _Trace: - // trace(x, y, z, ...) dumps the positions, expressions, and - // values of its arguments. The result of trace is the value - // of the first argument. - // Note: trace is only available in self-test mode. - // (no argument evaluated yet) - if nargs == 0 { - check.dump("%s: trace() without arguments", call.Pos()) - x.mode = novalue - break - } - var t operand - x1 := x - for _, arg := range call.Args { - check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) - check.dump("%s: %s", x1.pos(), x1) - x1 = &t // use incoming x only for first argument - } - // trace is only available in test mode - no need to record signature - - default: - unreachable() - } - - return true -} - -// makeSig makes a signature for the given argument and result types. -// Default types are used for untyped arguments, and res may be nil. -func makeSig(res Type, args ...Type) *Signature { - list := make([]*Var, len(args)) - for i, param := range args { - list[i] = NewVar(token.NoPos, nil, "", defaultType(param)) - } - params := NewTuple(list...) - var result *Tuple - if res != nil { - assert(!isUntyped(res)) - result = NewTuple(NewVar(token.NoPos, nil, "", res)) - } - return &Signature{params: params, results: result} -} - -// implicitArrayDeref returns A if typ is of the form *A and A is an array; -// otherwise it returns typ. -// -func implicitArrayDeref(typ Type) Type { - if p, ok := typ.(*Pointer); ok { - if a, ok := p.base.Underlying().(*Array); ok { - return a - } - } - return typ -} - -// unparen returns e with any enclosing parentheses stripped. -func unparen(e ast.Expr) ast.Expr { - for { - p, ok := e.(*ast.ParenExpr) - if !ok { - return e - } - e = p.X - } -} - -func (check *Checker) complexArg(x *operand) bool { - 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) - return false -} diff --git a/go/types/builtins_test.go b/go/types/builtins_test.go deleted file mode 100644 index e7857994ac..0000000000 --- a/go/types/builtins_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// 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_test - -import ( - "fmt" - "go/ast" - "go/parser" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -var builtinCalls = []struct { - name, src, sig string -}{ - {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`}, - {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`}, - {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`}, - {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`}, - {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`}, - {"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`}, - {"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`}, - - {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant - {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant - {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`}, - {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, - - {"len", `_ = len("foo")`, `invalid type`}, // constant - {"len", `var s string; _ = len(s)`, `func(string) int`}, - {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant - {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant - {"len", `var s []int64; _ = len(s)`, `func([]int64) int`}, - {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`}, - {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`}, - - {"close", `var c chan int; close(c)`, `func(chan int)`}, - {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, - - {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant - {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`}, - {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`}, - {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`}, - {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`}, - - {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`}, - {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`}, - {"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`}, - {"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`}, - {"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`}, - - {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`}, - {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`}, - - {"imag", `_ = imag(1i)`, `invalid type`}, // constant - {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`}, - {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`}, - {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`}, - {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`}, - - {"real", `_ = real(1i)`, `invalid type`}, // constant - {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`}, - {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`}, - {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`}, - {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`}, - - {"make", `_ = make([]int, 10)`, `func([]int, int) []int`}, - {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`}, - - {"new", `_ = new(int)`, `func(int) *int`}, - {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`}, - - {"panic", `panic(0)`, `func(interface{})`}, - {"panic", `panic("foo")`, `func(interface{})`}, - - {"print", `print()`, `func()`}, - {"print", `print(0)`, `func(int)`}, - {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, - - {"println", `println()`, `func()`}, - {"println", `println(0)`, `func(int)`}, - {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`}, - - {"recover", `recover()`, `func() interface{}`}, - {"recover", `_ = recover()`, `func() interface{}`}, - - {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant - {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant - - {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant - {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant - - {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant - {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant - - {"assert", `assert(true)`, `invalid type`}, // constant - {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant - - // no tests for trace since it produces output as a side-effect -} - -func TestBuiltinSignatures(t *testing.T) { - DefPredeclaredTestFuncs() - - seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually - for _, call := range builtinCalls { - testBuiltinSignature(t, call.name, call.src, call.sig) - seen[call.name] = true - } - - // make sure we didn't miss one - for _, name := range Universe.Names() { - if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] { - t.Errorf("missing test for %s", name) - } - } - for _, name := range Unsafe.Scope().Names() { - if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] { - t.Errorf("missing test for unsafe.%s", name) - } - } -} - -func testBuiltinSignature(t *testing.T, name, src0, want string) { - src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Errorf("%s: %s", src0, err) - return - } - - var conf Config - uses := make(map[*ast.Ident]Object) - types := make(map[ast.Expr]TypeAndValue) - _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types}) - if err != nil { - t.Errorf("%s: %s", src0, err) - return - } - - // find called function - n := 0 - var fun ast.Expr - for x := range types { - if call, _ := x.(*ast.CallExpr); call != nil { - fun = call.Fun - n++ - } - } - if n != 1 { - t.Errorf("%s: got %d CallExprs; want 1", src0, n) - return - } - - // check recorded types for fun and descendents (may be parenthesized) - for { - // the recorded type for the built-in must match the wanted signature - typ := types[fun].Type - if typ == nil { - t.Errorf("%s: no type recorded for %s", src0, ExprString(fun)) - return - } - if got := typ.String(); got != want { - t.Errorf("%s: got type %s; want %s", src0, got, want) - return - } - - // called function must be a (possibly parenthesized, qualified) - // identifier denoting the expected built-in - switch p := fun.(type) { - case *ast.Ident: - obj := uses[p] - if obj == nil { - t.Errorf("%s: no object found for %s", src0, p) - return - } - bin, _ := obj.(*Builtin) - if bin == nil { - t.Errorf("%s: %s does not denote a built-in", src0, p) - return - } - if bin.Name() != name { - t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name) - return - } - return // we're done - - case *ast.ParenExpr: - fun = p.X // unpack - - case *ast.SelectorExpr: - // built-in from package unsafe - ignore details - return // we're done - - default: - t.Errorf("%s: invalid function call", src0) - return - } - } -} diff --git a/go/types/call.go b/go/types/call.go deleted file mode 100644 index 1e94212398..0000000000 --- a/go/types/call.go +++ /dev/null @@ -1,441 +0,0 @@ -// 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. - -// This file implements typechecking of call and selector expressions. - -package types - -import ( - "go/ast" - "go/token" -) - -func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { - check.exprOrType(x, e.Fun) - - switch x.mode { - case invalid: - check.use(e.Args...) - x.mode = invalid - x.expr = e - return statement - - case typexpr: - // conversion - T := x.typ - x.mode = invalid - switch n := len(e.Args); n { - case 0: - check.errorf(e.Rparen, "missing argument in conversion to %s", T) - case 1: - check.expr(x, e.Args[0]) - if x.mode != invalid { - check.conversion(x, T) - } - default: - check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) - } - x.expr = e - return conversion - - case builtin: - id := x.id - if !check.builtin(x, e, id) { - x.mode = invalid - } - x.expr = e - // a non-constant result implies a function call - if x.mode != invalid && x.mode != constant { - check.hasCallOrRecv = true - } - return predeclaredFuncs[id].kind - - default: - // function/method call - sig, _ := x.typ.Underlying().(*Signature) - if sig == nil { - check.invalidOp(x.pos(), "cannot call non-function %s", x) - x.mode = invalid - x.expr = e - return statement - } - - arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false) - if arg == nil { - x.mode = invalid - x.expr = e - return statement - } - - check.arguments(x, e, sig, arg, n) - - // determine result - switch sig.results.Len() { - case 0: - x.mode = novalue - case 1: - x.mode = value - x.typ = sig.results.vars[0].typ // unpack tuple - default: - x.mode = value - x.typ = sig.results - } - x.expr = e - check.hasCallOrRecv = true - - return statement - } -} - -// use type-checks each argument. -// Useful to make sure expressions are evaluated -// (and variables are "used") in the presence of other errors. -func (check *Checker) use(arg ...ast.Expr) { - var x operand - for _, e := range arg { - check.rawExpr(&x, e, nil) - } -} - -// useGetter is like use, but takes a getter instead of a list of expressions. -// It should be called instead of use if a getter is present to avoid repeated -// evaluation of the first argument (since the getter was likely obtained via -// unpack, which may have evaluated the first argument already). -func (check *Checker) useGetter(get getter, n int) { - var x operand - for i := 0; i < n; i++ { - get(&x, i) - } -} - -// A getter sets x as the i'th operand, where 0 <= i < n and n is the total -// number of operands (context-specific, and maintained elsewhere). A getter -// type-checks the i'th operand; the details of the actual check are getter- -// specific. -type getter func(x *operand, i int) - -// unpack takes a getter get and a number of operands n. If n == 1, unpack -// calls the incoming getter for the first operand. If that operand is -// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a -// function call, or a comma-ok expression and allowCommaOk is set, the result -// is a new getter and operand count providing access to the function results, -// or comma-ok values, respectively. The third result value reports if it -// is indeed the comma-ok case. In all other cases, the incoming getter and -// operand count are returned unchanged, and the third result value is false. -// -// In other words, if there's exactly one operand that - after type-checking -// by calling get - stands for multiple operands, the resulting getter provides -// access to those operands instead. -// -// If the returned getter is called at most once for a given operand index i -// (including i == 0), that operand is guaranteed to cause only one call of -// the incoming getter with that i. -// -func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) { - if n == 1 { - // possibly result of an n-valued function call or comma,ok value - var x0 operand - get(&x0, 0) - if x0.mode == invalid { - return nil, 0, false - } - - if t, ok := x0.typ.(*Tuple); ok { - // result of an n-valued function call - return func(x *operand, i int) { - x.mode = value - x.expr = x0.expr - x.typ = t.At(i).typ - }, t.Len(), false - } - - if x0.mode == mapindex || x0.mode == commaok { - // comma-ok value - if allowCommaOk { - a := [2]Type{x0.typ, Typ[UntypedBool]} - return func(x *operand, i int) { - x.mode = value - x.expr = x0.expr - x.typ = a[i] - }, 2, true - } - x0.mode = value - } - - // single value - return func(x *operand, i int) { - if i != 0 { - unreachable() - } - *x = x0 - }, 1, false - } - - // zero or multiple values - return get, n, false -} - -// arguments checks argument passing for the call with the given signature. -// The arg function provides the operand for the i'th argument. -func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) { - if call.Ellipsis.IsValid() { - // last argument is of the form x... - if len(call.Args) == 1 && n > 1 { - // f()... is not permitted if f() is multi-valued - check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0]) - check.useGetter(arg, n) - return - } - if !sig.variadic { - check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun) - check.useGetter(arg, n) - return - } - } - - // evaluate arguments - for i := 0; i < n; i++ { - arg(x, i) - if x.mode != invalid { - var ellipsis token.Pos - if i == n-1 && call.Ellipsis.IsValid() { - ellipsis = call.Ellipsis - } - check.argument(sig, i, x, ellipsis) - } - } - - // check argument count - if sig.variadic { - // a variadic function accepts an "empty" - // last argument: count one extra - n++ - } - if n < sig.params.Len() { - check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun) - // ok to continue - } -} - -// argument checks passing of argument x to the i'th parameter of the given signature. -// If ellipsis is valid, the argument is followed by ... at that position in the call. -func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) { - n := sig.params.Len() - - // determine parameter type - var typ Type - switch { - case i < n: - typ = sig.params.vars[i].typ - case sig.variadic: - typ = sig.params.vars[n-1].typ - if debug { - if _, ok := typ.(*Slice); !ok { - check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) - } - } - default: - check.errorf(x.pos(), "too many arguments") - return - } - - if ellipsis.IsValid() { - // argument is of the form x... - if i != n-1 { - check.errorf(ellipsis, "can only use ... with matching parameter") - return - } - switch t := x.typ.Underlying().(type) { - case *Slice: - // ok - case *Tuple: - check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x) - return - default: - check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ) - return - } - } else if sig.variadic && i >= n-1 { - // use the variadic parameter slice's element type - typ = typ.(*Slice).elem - } - - if !check.assignment(x, typ) && x.mode != invalid { - check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ) - } -} - -func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { - // these must be declared before the "goto Error" statements - var ( - obj Object - index []int - indirect bool - ) - - sel := e.Sel.Name - // If the identifier refers to a package, handle everything here - // so we don't need a "package" mode for operands: package names - // can only appear in qualified identifiers which are mapped to - // selector expressions. - if ident, ok := e.X.(*ast.Ident); ok { - _, obj := check.scope.LookupParent(ident.Name, check.pos) - if pkg, _ := obj.(*PkgName); pkg != nil { - assert(pkg.pkg == check.pkg) - check.recordUse(ident, pkg) - pkg.used = true - exp := pkg.imported.scope.Lookup(sel) - if exp == nil { - if !pkg.imported.fake { - check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) - } - goto Error - } - if !exp.Exported() { - check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) - // ok to continue - } - check.recordUse(e.Sel, exp) - // Simplified version of the code for *ast.Idents: - // - imported objects are always fully initialized - switch exp := exp.(type) { - case *Const: - assert(exp.Val() != nil) - x.mode = constant - x.typ = exp.typ - x.val = exp.val - case *TypeName: - x.mode = typexpr - x.typ = exp.typ - case *Var: - x.mode = variable - x.typ = exp.typ - case *Func: - x.mode = value - x.typ = exp.typ - case *Builtin: - x.mode = builtin - x.typ = exp.typ - x.id = exp.id - default: - unreachable() - } - x.expr = e - return - } - } - - check.exprOrType(x, e.X) - if x.mode == invalid { - goto Error - } - - obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) - if obj == nil { - switch { - case index != nil: - // TODO(gri) should provide actual type where the conflict happens - check.invalidOp(e.Pos(), "ambiguous selector %s", sel) - case indirect: - check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) - default: - check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) - } - goto Error - } - - if x.mode == typexpr { - // method expression - m, _ := obj.(*Func) - if m == nil { - check.invalidOp(e.Pos(), "%s has no method %s", x, sel) - goto Error - } - - check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) - - // the receiver type becomes the type of the first function - // argument of the method expression's function type - var params []*Var - sig := m.typ.(*Signature) - if sig.params != nil { - params = sig.params.vars - } - x.mode = value - x.typ = &Signature{ - params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...), - results: sig.results, - variadic: sig.variadic, - } - - check.addDeclDep(m) - - } else { - // regular selector - switch obj := obj.(type) { - case *Var: - check.recordSelection(e, FieldVal, x.typ, obj, index, indirect) - if x.mode == variable || indirect { - x.mode = variable - } else { - x.mode = value - } - x.typ = obj.typ - - case *Func: - // TODO(gri) If we needed to take into account the receiver's - // addressability, should we report the type &(x.typ) instead? - check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) - - if debug { - // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. - typ := x.typ - if x.mode == variable { - // If typ is not an (unnamed) pointer or an interface, - // use *typ instead, because the method set of *typ - // includes the methods of typ. - // Variables are addressable, so we can always take their - // address. - if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) { - typ = &Pointer{base: typ} - } - } - // If we created a synthetic pointer type above, we will throw - // away the method set computed here after use. - // TODO(gri) Method set computation should probably always compute - // both, the value and the pointer receiver method set and represent - // them in a single structure. - // TODO(gri) Consider also using a method set cache for the lifetime - // of checker once we rely on MethodSet lookup instead of individual - // lookup. - mset := NewMethodSet(typ) - if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj { - check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m) - check.dump("%s\n", mset) - panic("method sets and lookup don't agree") - } - } - - x.mode = value - - // remove receiver - sig := *obj.typ.(*Signature) - sig.recv = nil - x.typ = &sig - - check.addDeclDep(obj) - - default: - unreachable() - } - } - - // everything went well - x.expr = e - return - -Error: - x.mode = invalid - x.expr = e -} diff --git a/go/types/check.go b/go/types/check.go deleted file mode 100644 index 964d2bde03..0000000000 --- a/go/types/check.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2011 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. - -// This file implements the Check function, which drives type-checking. - -package types - -import ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -// debugging/development support -const ( - debug = false // leave on during development - trace = false // turn on for detailed type resolution traces -) - -// If Strict is set, the type-checker enforces additional -// rules not specified by the Go 1 spec, but which will -// catch guaranteed run-time errors if the respective -// code is executed. In other words, programs passing in -// Strict mode are Go 1 compliant, but not all Go 1 programs -// will pass in Strict mode. The additional rules are: -// -// - A type assertion x.(T) where T is an interface type -// is invalid if any (statically known) method that exists -// for both x and T have different signatures. -// -const strict = false - -// exprInfo stores information about an untyped expression. -type exprInfo struct { - isLhs bool // expression is lhs operand of a shift with delayed type-check - mode operandMode - typ *Basic - val exact.Value // constant value; or nil (if not a constant) -} - -// funcInfo stores the information required for type-checking a function. -type funcInfo struct { - name string // for debugging/tracing only - decl *declInfo // for cycle detection - sig *Signature - body *ast.BlockStmt -} - -// A context represents the context within which an object is type-checked. -type context struct { - decl *declInfo // package-level declaration whose init expression/function body is checked - scope *Scope // top-most scope for lookups - iota exact.Value // value of iota in a constant declaration; nil otherwise - sig *Signature // function signature if inside a function; nil otherwise - hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions - hasCallOrRecv bool // set if an expression contains a function call or channel receive operation -} - -// A Checker maintains the state of the type checker. -// It must be created with NewChecker. -type Checker struct { - // package information - // (initialized by NewChecker, valid for the life-time of checker) - conf *Config - fset *token.FileSet - pkg *Package - *Info - objMap map[Object]*declInfo // maps package-level object to declaration info - - // information collected during type-checking of a set of package files - // (initialized by Files, valid only for the duration of check.Files; - // maps and lists are allocated on demand) - files []*ast.File // package files - unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope - - firstErr error // first error encountered - methods map[string][]*Func // maps type names to associated methods - untyped map[ast.Expr]exprInfo // map of expressions without final type - funcs []funcInfo // list of functions to type-check - delayed []func() // delayed checks requiring fully setup types - - // context within which the current object is type-checked - // (valid only for the duration of type-checking a specific object) - context - pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) - - // debugging - indent int // indentation for tracing -} - -// addUnusedImport adds the position of a dot-imported package -// pkg to the map of dot imports for the given file scope. -func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) { - mm := check.unusedDotImports - if mm == nil { - mm = make(map[*Scope]map[*Package]token.Pos) - check.unusedDotImports = mm - } - m := mm[scope] - if m == nil { - m = make(map[*Package]token.Pos) - mm[scope] = m - } - m[pkg] = pos -} - -// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists -func (check *Checker) addDeclDep(to Object) { - from := check.decl - if from == nil { - return // not in a package-level init expression - } - if _, found := check.objMap[to]; !found { - return // to is not a package-level object - } - from.addDep(to) -} - -func (check *Checker) assocMethod(tname string, meth *Func) { - m := check.methods - if m == nil { - m = make(map[string][]*Func) - check.methods = m - } - m[tname] = append(m[tname], meth) -} - -func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val exact.Value) { - m := check.untyped - if m == nil { - m = make(map[ast.Expr]exprInfo) - check.untyped = m - } - m[e] = exprInfo{lhs, mode, typ, val} -} - -func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) { - check.funcs = append(check.funcs, funcInfo{name, decl, sig, body}) -} - -func (check *Checker) delay(f func()) { - check.delayed = append(check.delayed, f) -} - -// NewChecker returns a new Checker instance for a given package. -// Package files may be added incrementally via checker.Files. -func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker { - // make sure we have a configuration - if conf == nil { - conf = new(Config) - } - - // make sure we have a package canonicalization map - if conf.Packages == nil { - conf.Packages = make(map[string]*Package) - } - - // make sure we have an info struct - if info == nil { - info = new(Info) - } - - return &Checker{ - conf: conf, - fset: fset, - pkg: pkg, - Info: info, - objMap: make(map[Object]*declInfo), - } -} - -// initFiles initializes the files-specific portion of checker. -// The provided files must all belong to the same package. -func (check *Checker) initFiles(files []*ast.File) { - // start with a clean slate (check.Files may be called multiple times) - check.files = nil - check.unusedDotImports = nil - - check.firstErr = nil - check.methods = nil - check.untyped = nil - check.funcs = nil - check.delayed = nil - - // determine package name and collect valid files - pkg := check.pkg - for _, file := range files { - switch name := file.Name.Name; pkg.name { - case "": - if name != "_" { - pkg.name = name - } else { - check.errorf(file.Name.Pos(), "invalid package name _") - } - fallthrough - - case name: - check.files = append(check.files, file) - - default: - check.errorf(file.Package, "package %s; expected %s", name, pkg.name) - // ignore this file - } - } -} - -// A bailout panic is used for early termination. -type bailout struct{} - -func (check *Checker) handleBailout(err *error) { - switch p := recover().(type) { - case nil, bailout: - // normal return or early exit - *err = check.firstErr - default: - // re-panic - panic(p) - } -} - -// Files checks the provided files as part of the checker's package. -func (check *Checker) Files(files []*ast.File) (err error) { - defer check.handleBailout(&err) - - check.initFiles(files) - - check.collectObjects() - - check.packageObjects(check.resolveOrder()) - - check.functionBodies() - - check.initOrder() - - if !check.conf.DisableUnusedImportCheck { - check.unusedImports() - } - - // perform delayed checks - for _, f := range check.delayed { - f() - } - - check.recordUntyped() - - check.pkg.complete = true - return -} - -func (check *Checker) recordUntyped() { - if !debug && check.Types == nil { - return // nothing to do - } - - for x, info := range check.untyped { - if debug && isTyped(info.typ) { - check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ) - unreachable() - } - check.recordTypeAndValue(x, info.mode, info.typ, info.val) - } -} - -func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val exact.Value) { - assert(x != nil) - assert(typ != nil) - if mode == invalid { - return // omit - } - assert(typ != nil) - if mode == constant { - assert(val != nil) - assert(typ == Typ[Invalid] || isConstType(typ)) - } - if m := check.Types; m != nil { - m[x] = TypeAndValue{mode, typ, val} - } -} - -func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) { - // f must be a (possibly parenthesized) identifier denoting a built-in - // (built-ins in package unsafe always produce a constant result and - // we don't record their signatures, so we don't see qualified idents - // here): record the signature for f and possible children. - for { - check.recordTypeAndValue(f, builtin, sig, nil) - switch p := f.(type) { - case *ast.Ident: - return // we're done - case *ast.ParenExpr: - f = p.X - default: - unreachable() - } - } -} - -func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { - assert(x != nil) - if a[0] == nil || a[1] == nil { - return - } - assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) - if m := check.Types; m != nil { - for { - tv := m[x] - assert(tv.Type != nil) // should have been recorded already - pos := x.Pos() - tv.Type = NewTuple( - NewVar(pos, check.pkg, "", a[0]), - NewVar(pos, check.pkg, "", a[1]), - ) - m[x] = tv - // if x is a parenthesized expression (p.X), update p.X - p, _ := x.(*ast.ParenExpr) - if p == nil { - break - } - x = p.X - } - } -} - -func (check *Checker) recordDef(id *ast.Ident, obj Object) { - assert(id != nil) - if m := check.Defs; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordUse(id *ast.Ident, obj Object) { - assert(id != nil) - assert(obj != nil) - if m := check.Uses; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordImplicit(node ast.Node, obj Object) { - assert(node != nil) - assert(obj != nil) - if m := check.Implicits; m != nil { - m[node] = obj - } -} - -func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { - assert(obj != nil && (recv == nil || len(index) > 0)) - check.recordUse(x.Sel, obj) - // TODO(gri) Should we also call recordTypeAndValue? - if m := check.Selections; m != nil { - m[x] = &Selection{kind, recv, obj, index, indirect} - } -} - -func (check *Checker) recordScope(node ast.Node, scope *Scope) { - assert(node != nil) - assert(scope != nil) - if m := check.Scopes; m != nil { - m[node] = scope - } -} diff --git a/go/types/check_test.go b/go/types/check_test.go deleted file mode 100644 index fd4dadb00e..0000000000 --- a/go/types/check_test.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2011 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. - -// This file implements a typechecker test harness. The packages specified -// in tests are typechecked. Error messages reported by the typechecker are -// compared against the error messages expected in the test files. -// -// Expected errors are indicated in the test files by putting a comment -// of the form /* ERROR "rx" */ immediately following an offending token. -// The harness will verify that an error matching the regular expression -// rx is reported at that source position. Consecutive comments may be -// used to indicate multiple errors for the same token position. -// -// For instance, the following test file indicates that a "not declared" -// error should be reported for the undeclared variable x: -// -// package p -// func f() { -// _ = x /* ERROR "not declared" */ + 1 -// } - -package types_test - -import ( - "flag" - "go/ast" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "regexp" - "runtime" - "strings" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -var ( - listErrors = flag.Bool("list", false, "list errors") - testFiles = flag.String("files", "", "space-separated list of test files") -) - -// The test filenames do not end in .go so that they are invisible -// to gofmt since they contain comments that must not change their -// positions relative to surrounding tokens. - -// Each tests entry is list of files belonging to the same package. -var tests = []struct { - files string // blank-separated list of file names - cond func() bool // condition under which the test should be run; nil means always -}{ - {"testdata/errors.src", nil}, - {"testdata/importdecl0a.src testdata/importdecl0b.src", nil}, - {"testdata/importdecl1a.src testdata/importdecl1b.src", nil}, - {"testdata/cycles.src", nil}, - {"testdata/cycles1.src", nil}, - {"testdata/cycles2.src", nil}, - {"testdata/cycles3.src", nil}, - {"testdata/cycles4.src", nil}, - {"testdata/init0.src", nil}, - {"testdata/init1.src", nil}, - {"testdata/init2.src", nil}, - {"testdata/decls0.src", nil}, - {"testdata/decls1.src", nil}, - {"testdata/decls2a.src testdata/decls2b.src", nil}, - {"testdata/decls3.src", nil}, - {"testdata/const0.src", nil}, - {"testdata/const1.src", nil}, - {"testdata/constdecl.src", notGo1_4}, // Go 1.4 parser doesn't report certain errors - {"testdata/vardecl.src", notGo1_4}, // Go 1.4 parser doesn't report certain errors - {"testdata/expr0.src", nil}, - {"testdata/expr1.src", nil}, - {"testdata/expr2.src", nil}, - {"testdata/expr3.src", notGo1_4}, // Go 1.4 parser doesn't permit omitting key type in map literals - {"testdata/methodsets.src", nil}, - {"testdata/shifts.src", nil}, - {"testdata/builtins.src", nil}, - {"testdata/conversions.src", nil}, - {"testdata/stmt0.src", nil}, - {"testdata/stmt1.src", nil}, - {"testdata/gotos.src", nil}, - {"testdata/labels.src", nil}, - {"testdata/issues.src", nil}, - {"testdata/blank.src", nil}, -} - -func notGo1_4() bool { - return !strings.HasPrefix(runtime.Version(), "go1.4") -} - -var fset = token.NewFileSet() - -// Positioned errors are of the form filename:line:column: message . -var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`) - -// splitError splits an error's error message into a position string -// and the actual error message. If there's no position information, -// pos is the empty string, and msg is the entire error message. -// -func splitError(err error) (pos, msg string) { - msg = err.Error() - if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 { - pos = m[1] - msg = m[2] - } - return -} - -func parseFiles(t *testing.T, filenames string) ([]*ast.File, []error) { - var files []*ast.File - var errlist []error - for _, filename := range strings.Split(filenames, " ") { - file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) - if file == nil { - t.Fatalf("%s: %s", filename, err) - } - files = append(files, file) - if err != nil { - if list, _ := err.(scanner.ErrorList); len(list) > 0 { - for _, err := range list { - errlist = append(errlist, err) - } - } else { - errlist = append(errlist, err) - } - } - } - return files, errlist -} - -// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where -// rx is a regular expression that matches the expected error message. -// Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"` -// for error messages that are located immediately after rather than -// at a token's position. -// -var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`) - -// errMap collects the regular expressions of ERROR comments found -// in files and returns them as a map of error positions to error messages. -// -func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string { - // map of position strings to lists of error message patterns - errmap := make(map[string][]string) - - for _, file := range files { - filename := fset.Position(file.Package).Filename - src, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatalf("%s: could not read %s", testname, filename) - } - - var s scanner.Scanner - s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments) - var prev token.Pos // position of last non-comment, non-semicolon token - var here token.Pos // position immediately after the token at position prev - - scanFile: - for { - pos, tok, lit := s.Scan() - switch tok { - case token.EOF: - break scanFile - case token.COMMENT: - if lit[1] == '*' { - lit = lit[:len(lit)-2] // strip trailing */ - } - if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 { - pos := prev - if s[1] == "HERE" { - pos = here - } - p := fset.Position(pos).String() - errmap[p] = append(errmap[p], strings.TrimSpace(s[2])) - } - case token.SEMICOLON: - // ignore automatically inserted semicolon - if lit == "\n" { - continue scanFile - } - fallthrough - default: - prev = pos - var l int // token length - if tok.IsLiteral() { - l = len(lit) - } else { - l = len(tok.String()) - } - here = prev + token.Pos(l) - } - } - } - - return errmap -} - -func eliminate(t *testing.T, errmap map[string][]string, errlist []error) { - for _, err := range errlist { - pos, gotMsg := splitError(err) - list := errmap[pos] - index := -1 // list index of matching message, if any - // we expect one of the messages in list to match the error at pos - for i, wantRx := range list { - rx, err := regexp.Compile(wantRx) - if err != nil { - t.Errorf("%s: %v", pos, err) - continue - } - if rx.MatchString(gotMsg) { - index = i - break - } - } - if index >= 0 { - // eliminate from list - if n := len(list) - 1; n > 0 { - // not the last entry - swap in last element and shorten list by 1 - list[index] = list[n] - errmap[pos] = list[:n] - } else { - // last entry - remove list from map - delete(errmap, pos) - } - } else { - t.Errorf("%s: no error expected: %q", pos, gotMsg) - } - } -} - -func checkFiles(t *testing.T, filenames string) { - // parse files and collect parser errors - files, errlist := parseFiles(t, filenames) - - pkgName := "" - if len(files) > 0 { - pkgName = files[0].Name.Name - } - - if *listErrors && len(errlist) > 0 { - t.Errorf("--- %s:", pkgName) - for _, err := range errlist { - t.Error(err) - } - } - - // typecheck and collect typechecker errors - var conf Config - conf.Error = func(err error) { - if *listErrors { - t.Error(err) - return - } - // Ignore secondary error messages starting with "\t"; - // they are clarifying messages for a primary error. - if !strings.Contains(err.Error(), ": \t") { - errlist = append(errlist, err) - } - } - conf.Check(pkgName, fset, files, nil) - - if *listErrors { - return - } - - // match and eliminate errors; - // we are expecting the following errors - errmap := errMap(t, pkgName, files) - eliminate(t, errmap, errlist) - - // there should be no expected errors left - if len(errmap) > 0 { - t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap)) - for pos, list := range errmap { - for _, rx := range list { - t.Errorf("%s: %q", pos, rx) - } - } - } -} - -func TestCheck(t *testing.T) { - skipSpecialPlatforms(t) - - // Declare builtins for testing. - DefPredeclaredTestFuncs() - - // If explicit test files are specified, only check those. - if *testFiles != "" { - checkFiles(t, *testFiles) - return - } - - // Otherwise, run all the tests. - for _, test := range tests { - if test.cond == nil || test.cond() { - checkFiles(t, test.files) - } - } -} diff --git a/go/types/conversions.go b/go/types/conversions.go deleted file mode 100644 index 6e279ca3ee..0000000000 --- a/go/types/conversions.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2012 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. - -// This file implements typechecking of conversions. - -package types - -import "golang.org/x/tools/go/exact" - -// Conversion type-checks the conversion T(x). -// The result is in x. -func (check *Checker) conversion(x *operand, T Type) { - constArg := x.mode == constant - - var ok bool - switch { - case constArg && isConstType(T): - // constant conversion - switch t := T.Underlying().(*Basic); { - case representableConst(x.val, check.conf, t.kind, &x.val): - ok = true - case isInteger(x.typ) && isString(t): - codepoint := int64(-1) - if i, ok := exact.Int64Val(x.val); ok { - codepoint = i - } - // If codepoint < 0 the absolute value is too large (or unknown) for - // conversion. This is the same as converting any other out-of-range - // value - let string(codepoint) do the work. - x.val = exact.MakeString(string(codepoint)) - ok = true - } - case x.convertibleTo(check.conf, T): - // non-constant conversion - x.mode = value - ok = true - } - - if !ok { - check.errorf(x.pos(), "cannot convert %s to %s", x, T) - x.mode = invalid - return - } - - // The conversion argument types are final. For untyped values the - // conversion provides the type, per the spec: "A constant may be - // given a type explicitly by a constant declaration or conversion,...". - final := x.typ - if isUntyped(x.typ) { - final = T - // - For conversions to interfaces, use the argument's default type. - // - For conversions of untyped constants to non-constant types, also - // use the default type (e.g., []byte("foo") should report string - // not []byte as type for the constant "foo"). - // - Keep untyped nil for untyped nil arguments. - if IsInterface(T) || constArg && !isConstType(T) { - final = defaultType(x.typ) - } - check.updateExprType(x.expr, final, true) - } - - x.typ = T -} - -func (x *operand) convertibleTo(conf *Config, T Type) bool { - // "x is assignable to T" - if x.assignableTo(conf, T) { - return true - } - - // "x's type and T have identical underlying types" - V := x.typ - Vu := V.Underlying() - Tu := T.Underlying() - if Identical(Vu, Tu) { - return true - } - - // "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 Identical(V.base.Underlying(), T.base.Underlying()) { - return true - } - } - } - - // "x's type and T are both integer or floating point types" - if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) { - return true - } - - // "x's type and T are both complex types" - if isComplex(V) && isComplex(T) { - return true - } - - // "x is an integer or a slice of bytes or runes and T is a string type" - if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { - return true - } - - // "x is a string and T is a slice of bytes or runes" - if isString(V) && isBytesOrRunes(Tu) { - return true - } - - // package unsafe: - // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" - if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { - return true - } - // "and vice versa" - if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { - return true - } - - return false -} - -func isUintptr(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.kind == Uintptr -} - -func isUnsafePointer(typ Type) bool { - // TODO(gri): Is this (typ.Underlying() instead of just typ) correct? - // The spec does not say so, but gc claims it is. See also - // issue 6326. - t, ok := typ.Underlying().(*Basic) - return ok && t.kind == UnsafePointer -} - -func isPointer(typ Type) bool { - _, ok := typ.Underlying().(*Pointer) - return ok -} - -func isBytesOrRunes(typ Type) bool { - if s, ok := typ.(*Slice); ok { - t, ok := s.elem.Underlying().(*Basic) - return ok && (t.kind == Byte || t.kind == Rune) - } - return false -} diff --git a/go/types/decl.go b/go/types/decl.go deleted file mode 100644 index 9eba85c1c3..0000000000 --- a/go/types/decl.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2014 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 ( - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -func (check *Checker) reportAltDecl(obj Object) { - if pos := obj.Pos(); pos.IsValid() { - // We use "other" rather than "previous" here because - // the first declaration seen may not be textually - // earlier in the source. - check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented - } -} - -func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token.Pos) { - // spec: "The blank identifier, represented by the underscore - // character _, may be used in a declaration like any other - // identifier but the declaration does not introduce a new - // binding." - if obj.Name() != "_" { - if alt := scope.Insert(obj); alt != nil { - check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) - check.reportAltDecl(alt) - return - } - obj.setScopePos(pos) - } - if id != nil { - check.recordDef(id, obj) - } -} - -// objDecl type-checks the declaration of obj in its respective (file) context. -// See check.typ for the details on def and path. -func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) { - if obj.Type() != nil { - return // already checked - nothing to do - } - - if trace { - check.trace(obj.Pos(), "-- declaring %s", obj.Name()) - check.indent++ - defer func() { - check.indent-- - check.trace(obj.Pos(), "=> %s", obj) - }() - } - - d := check.objMap[obj] - if d == nil { - check.dump("%s: %s should have been declared", obj.Pos(), obj.Name()) - unreachable() - } - - // save/restore current context and setup object context - defer func(ctxt context) { - check.context = ctxt - }(check.context) - check.context = context{ - scope: d.file, - } - - // Const and var declarations must not have initialization - // cycles. We track them by remembering the current declaration - // in check.decl. Initialization expressions depending on other - // consts, vars, or functions, add dependencies to the current - // check.decl. - switch obj := obj.(type) { - case *Const: - check.decl = d // new package-level const decl - check.constDecl(obj, d.typ, d.init) - case *Var: - check.decl = d // new package-level var decl - check.varDecl(obj, d.lhs, d.typ, d.init) - case *TypeName: - // invalid recursive types are detected via path - check.typeDecl(obj, d.typ, def, path) - case *Func: - // functions may be recursive - no need to track dependencies - check.funcDecl(obj, d) - default: - unreachable() - } -} - -func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) { - assert(obj.typ == nil) - - if obj.visited { - obj.typ = Typ[Invalid] - return - } - obj.visited = true - - // use the correct value of iota - assert(check.iota == nil) - check.iota = obj.val - defer func() { check.iota = nil }() - - // provide valid constant value under all circumstances - obj.val = exact.MakeUnknown() - - // determine type, if any - if typ != nil { - t := check.typ(typ) - if !isConstType(t) { - check.errorf(typ.Pos(), "invalid constant type %s", t) - obj.typ = Typ[Invalid] - return - } - obj.typ = t - } - - // check initialization - var x operand - if init != nil { - check.expr(&x, init) - } - check.initConst(obj, &x) -} - -func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { - assert(obj.typ == nil) - - if obj.visited { - obj.typ = Typ[Invalid] - return - } - obj.visited = true - - // var declarations cannot use iota - assert(check.iota == nil) - - // determine type, if any - if typ != nil { - obj.typ = check.typ(typ) - } - - // check initialization - if init == nil { - if typ == nil { - // error reported before by arityMatch - obj.typ = Typ[Invalid] - } - return - } - - if lhs == nil || len(lhs) == 1 { - assert(lhs == nil || lhs[0] == obj) - var x operand - check.expr(&x, init) - check.initVar(obj, &x, false) - return - } - - if debug { - // obj must be one of lhs - found := false - for _, lhs := range lhs { - if obj == lhs { - found = true - break - } - } - if !found { - panic("inconsistent lhs") - } - } - check.initVars(lhs, []ast.Expr{init}, token.NoPos) -} - -// underlying returns the underlying type of typ; possibly by following -// forward chains of named types. Such chains only exist while named types -// are incomplete. -func underlying(typ Type) Type { - for { - n, _ := typ.(*Named) - if n == nil { - break - } - typ = n.underlying - } - return typ -} - -func (n *Named) setUnderlying(typ Type) { - if n != nil { - n.underlying = typ - } -} - -func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) { - assert(obj.typ == nil) - - // type declarations cannot use iota - assert(check.iota == nil) - - named := &Named{obj: obj} - def.setUnderlying(named) - obj.typ = named // make sure recursive type declarations terminate - - // determine underlying type of named - check.typExpr(typ, named, append(path, obj)) - - // The underlying type of named may be itself a named type that is - // incomplete: - // - // type ( - // A B - // B *C - // C A - // ) - // - // The type of C is the (named) type of A which is incomplete, - // and which has as its underlying type the named type B. - // Determine the (final, unnamed) underlying type by resolving - // any forward chain (they always end in an unnamed type). - named.underlying = underlying(named.underlying) - - // check and add associated methods - // TODO(gri) It's easy to create pathological cases where the - // current approach is incorrect: In general we need to know - // and add all methods _before_ type-checking the type. - // See http://play.golang.org/p/WMpE0q2wK8 - check.addMethodDecls(obj) -} - -func (check *Checker) addMethodDecls(obj *TypeName) { - // get associated methods - methods := check.methods[obj.name] - if len(methods) == 0 { - return // no methods - } - delete(check.methods, obj.name) - - // use an objset to check for name conflicts - var mset objset - - // spec: "If the base type is a struct type, the non-blank method - // and field names must be distinct." - base := obj.typ.(*Named) - if t, _ := base.underlying.(*Struct); t != nil { - for _, fld := range t.fields { - if fld.name != "_" { - assert(mset.insert(fld) == nil) - } - } - } - - // Checker.Files may be called multiple times; additional package files - // may add methods to already type-checked types. Add pre-existing methods - // so that we can detect redeclarations. - for _, m := range base.methods { - assert(m.name != "_") - assert(mset.insert(m) == nil) - } - - // type-check methods - for _, m := range methods { - // spec: "For a base type, the non-blank names of methods bound - // to it must be unique." - if m.name != "_" { - if alt := mset.insert(m); alt != nil { - switch alt.(type) { - case *Var: - check.errorf(m.pos, "field and method with the same name %s", m.name) - case *Func: - check.errorf(m.pos, "method %s already declared for %s", m.name, base) - default: - unreachable() - } - check.reportAltDecl(alt) - continue - } - } - check.objDecl(m, nil, nil) - // methods with blank _ names cannot be found - don't keep them - if m.name != "_" { - base.methods = append(base.methods, m) - } - } -} - -func (check *Checker) funcDecl(obj *Func, decl *declInfo) { - assert(obj.typ == nil) - - // func declarations cannot use iota - assert(check.iota == nil) - - sig := new(Signature) - obj.typ = sig // guard against cycles - fdecl := decl.fdecl - check.funcType(sig, fdecl.Recv, fdecl.Type) - if sig.recv == nil && 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 - } - - // function body must be type-checked after global declarations - // (functions implemented elsewhere have no body) - if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { - check.later(obj.name, decl, sig, fdecl.Body) - } -} - -func (check *Checker) declStmt(decl ast.Decl) { - pkg := check.pkg - - switch d := decl.(type) { - case *ast.BadDecl: - // ignore - - case *ast.GenDecl: - var last *ast.ValueSpec // last ValueSpec with type or init exprs seen - for iota, spec := range d.Specs { - switch s := spec.(type) { - case *ast.ValueSpec: - switch d.Tok { - case token.CONST: - // determine which init exprs to use - switch { - case s.Type != nil || len(s.Values) > 0: - last = s - case last == nil: - last = new(ast.ValueSpec) // make sure last exists - } - - // declare all constants - lhs := make([]*Const, len(s.Names)) - for i, name := range s.Names { - obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) - lhs[i] = obj - - var init ast.Expr - if i < len(last.Values) { - init = last.Values[i] - } - - check.constDecl(obj, last.Type, init) - } - - check.arityMatch(s, last) - - // spec: "The scope of a constant or variable identifier declared - // inside a function begins at the end of the ConstSpec or VarSpec - // (ShortVarDecl for short variable declarations) and ends at the - // end of the innermost containing block." - scopePos := s.End() - for i, name := range s.Names { - check.declare(check.scope, name, lhs[i], scopePos) - } - - case token.VAR: - lhs0 := make([]*Var, len(s.Names)) - for i, name := range s.Names { - lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) - } - - // initialize all variables - for i, obj := range lhs0 { - var lhs []*Var - var init ast.Expr - switch len(s.Values) { - case len(s.Names): - // lhs and rhs match - init = s.Values[i] - case 1: - // rhs is expected to be a multi-valued expression - lhs = lhs0 - init = s.Values[0] - default: - if i < len(s.Values) { - init = s.Values[i] - } - } - check.varDecl(obj, lhs, s.Type, init) - if len(s.Values) == 1 { - // If we have a single lhs variable we are done either way. - // If we have a single rhs expression, it must be a multi- - // valued expression, in which case handling the first lhs - // variable will cause all lhs variables to have a type - // assigned, and we are done as well. - if debug { - for _, obj := range lhs0 { - assert(obj.typ != nil) - } - } - break - } - } - - check.arityMatch(s, nil) - - // declare all variables - // (only at this point are the variable scopes (parents) set) - scopePos := s.End() // see constant declarations - for i, name := range s.Names { - // see constant declarations - check.declare(check.scope, name, lhs0[i], scopePos) - } - - default: - check.invalidAST(s.Pos(), "invalid token %s", d.Tok) - } - - case *ast.TypeSpec: - obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - // spec: "The scope of a type identifier declared inside a function - // begins at the identifier in the TypeSpec and ends at the end of - // the innermost containing block." - scopePos := s.Name.Pos() - check.declare(check.scope, s.Name, obj, scopePos) - check.typeDecl(obj, s.Type, nil, nil) - - default: - check.invalidAST(s.Pos(), "const, type, or var declaration expected") - } - } - - default: - check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) - } -} diff --git a/go/types/errors.go b/go/types/errors.go deleted file mode 100644 index 0c0049b1f3..0000000000 --- a/go/types/errors.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2012 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. - -// This file implements various error reporters. - -package types - -import ( - "fmt" - "go/ast" - "go/token" - "strings" -) - -func assert(p bool) { - if !p { - panic("assertion failed") - } -} - -func unreachable() { - panic("unreachable") -} - -func (check *Checker) qualifier(pkg *Package) string { - if pkg != check.pkg { - return pkg.path - } - return "" -} - -func (check *Checker) sprintf(format string, args ...interface{}) string { - for i, arg := range args { - switch a := arg.(type) { - case nil: - arg = "" - case operand: - panic("internal error: should always pass *operand") - case *operand: - arg = operandString(a, check.qualifier) - case token.Pos: - arg = check.fset.Position(a).String() - case ast.Expr: - arg = ExprString(a) - case Object: - arg = ObjectString(a, check.qualifier) - case Type: - arg = TypeString(a, check.qualifier) - } - args[i] = arg - } - return fmt.Sprintf(format, args...) -} - -func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) { - fmt.Printf("%s:\t%s%s\n", - check.fset.Position(pos), - strings.Repeat(". ", check.indent), - check.sprintf(format, args...), - ) -} - -// dump is only needed for debugging -func (check *Checker) dump(format string, args ...interface{}) { - fmt.Println(check.sprintf(format, args...)) -} - -func (check *Checker) err(pos token.Pos, msg string, soft bool) { - err := Error{check.fset, pos, msg, soft} - if check.firstErr == nil { - check.firstErr = err - } - f := check.conf.Error - if f == nil { - panic(bailout{}) // report only first error - } - f(err) -} - -func (check *Checker) error(pos token.Pos, msg string) { - check.err(pos, msg, false) -} - -func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) { - check.err(pos, check.sprintf(format, args...), false) -} - -func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) { - check.err(pos, check.sprintf(format, args...), true) -} - -func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) { - check.errorf(pos, "invalid AST: "+format, args...) -} - -func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) { - check.errorf(pos, "invalid argument: "+format, args...) -} - -func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) { - check.errorf(pos, "invalid operation: "+format, args...) -} diff --git a/go/types/eval.go b/go/types/eval.go deleted file mode 100644 index c09f2a3ba4..0000000000 --- a/go/types/eval.go +++ /dev/null @@ -1,87 +0,0 @@ -// 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 ( - "fmt" - "go/parser" - "go/token" -) - -// Eval returns the type and, if constant, the value for the -// expression expr, evaluated at position pos of package pkg, -// which must have been derived from type-checking an AST with -// complete position information relative to the provided file -// set. -// -// If the expression contains function literals, their bodies -// are ignored (i.e., the bodies are not type-checked). -// -// If pkg == nil, the Universe scope is used and the provided -// position pos is ignored. If pkg != nil, and pos is invalid, -// the package scope is used. Otherwise, pos must belong to the -// package. -// -// An error is returned if pos is not within the package or -// if the node cannot be evaluated. -// -// Note: Eval should not be used instead of running Check to compute -// types and values, but in addition to Check. Eval will re-evaluate -// its argument each time, and it also does not know about the context -// in which an expression is used (e.g., an assignment). Thus, top- -// level untyped constants will return an untyped type rather then the -// respective context-specific type. -// -func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (tv TypeAndValue, err error) { - // determine scope - var scope *Scope - if pkg == nil { - scope = Universe - pos = token.NoPos - } else if !pos.IsValid() { - scope = pkg.scope - } else { - // The package scope extent (position information) may be - // incorrect (files spread accross a wide range of fset - // positions) - ignore it and just consider its children - // (file scopes). - for _, fscope := range pkg.scope.children { - if scope = fscope.Innermost(pos); scope != nil { - break - } - } - if scope == nil || debug { - s := scope - for s != nil && s != pkg.scope { - s = s.parent - } - // s == nil || s == pkg.scope - if s == nil { - return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name) - } - } - } - - // parse expressions - // BUG(gri) In case of type-checking errors below, the type checker - // doesn't have the correct file set for expr. The correct - // solution requires a ParseExpr that uses the incoming - // file set fset. - node, err := parser.ParseExpr(expr) - if err != nil { - return TypeAndValue{}, err - } - - // initialize checker - check := NewChecker(nil, fset, pkg, nil) - check.scope = scope - check.pos = pos - defer check.handleBailout(&err) - - // evaluate node - var x operand - check.rawExpr(&x, node, nil) - return TypeAndValue{x.mode, x.typ, x.val}, err -} diff --git a/go/types/eval_test.go b/go/types/eval_test.go deleted file mode 100644 index b68b244f95..0000000000 --- a/go/types/eval_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// 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. - -// This file contains tests for Eval. - -package types_test - -import ( - "go/ast" - "go/parser" - "go/token" - "strings" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) { - gotTv, err := Eval(fset, pkg, pos, expr) - if err != nil { - t.Errorf("Eval(%q) failed: %s", expr, err) - return - } - if gotTv.Type == nil { - t.Errorf("Eval(%q) got nil type but no error", expr) - return - } - - // compare types - if typ != nil { - // we have a type, check identity - if !Identical(gotTv.Type, typ) { - t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ) - return - } - } else { - // we have a string, compare type string - gotStr := gotTv.Type.String() - if gotStr != typStr { - t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr) - return - } - } - - // compare values - gotStr := "" - if gotTv.Value != nil { - gotStr = gotTv.Value.String() - } - if gotStr != valStr { - t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr) - } -} - -func TestEvalBasic(t *testing.T) { - fset := token.NewFileSet() - for _, typ := range Typ[Bool : String+1] { - testEval(t, fset, nil, token.NoPos, typ.Name(), typ, "", "") - } -} - -func TestEvalComposite(t *testing.T) { - fset := token.NewFileSet() - for _, test := range independentTestTypes { - testEval(t, fset, nil, token.NoPos, test.src, nil, test.str, "") - } -} - -func TestEvalArith(t *testing.T) { - var tests = []string{ - `true`, - `false == false`, - `12345678 + 87654321 == 99999999`, - `10 * 20 == 200`, - `(1<<1000)*2 >> 100 == 2<<900`, - `"foo" + "bar" == "foobar"`, - `"abc" <= "bcd"`, - `len([10]struct{}{}) == 2*5`, - } - fset := token.NewFileSet() - for _, test := range tests { - testEval(t, fset, nil, token.NoPos, test, Typ[UntypedBool], "", "true") - } -} - -func TestEvalPos(t *testing.T) { - skipSpecialPlatforms(t) - - // The contents of /*-style comments are of the form - // expr => value, type - // where value may be the empty string. - // Each expr is evaluated at the position of the comment - // and the result is compared with the expected value - // and type. - var sources = []string{ - ` - package p - import "fmt" - import m "math" - const c = 3.0 - type T []int - func f(a int, s string) float64 { - fmt.Println("calling f") - _ = m.Pi // use package math - const d int = c + 1 - var x int - x = a + len(s) - return float64(x) - /* true => true, untyped bool */ - /* fmt.Println => , func(a ...interface{}) (n int, err error) */ - /* c => 3, untyped float */ - /* T => , p.T */ - /* a => , int */ - /* s => , string */ - /* d => 4, int */ - /* x => , int */ - /* d/c => 1, int */ - /* c/2 => 3/2, untyped float */ - /* m.Pi < m.E => false, untyped bool */ - } - `, - ` - package p - /* c => 3, untyped float */ - type T1 /* T1 => , p.T1 */ struct {} - var v1 /* v1 => , int */ = 42 - func /* f1 => , func(v1 float64) */ f1(v1 float64) { - /* f1 => , func(v1 float64) */ - /* v1 => , float64 */ - var c /* c => 3, untyped float */ = "foo" /* c => , string */ - { - var c struct { - c /* c => , string */ int - } - /* c => , struct{c int} */ - _ = c - } - _ = func(a, b, c int) /* c => , string */ { - /* c => , int */ - } - _ = c - type FT /* FT => , p.FT */ interface{} - } - `, - ` - package p - /* T => , p.T */ - `, - } - - fset := token.NewFileSet() - var files []*ast.File - for i, src := range sources { - file, err := parser.ParseFile(fset, "p", src, parser.ParseComments) - if err != nil { - t.Fatalf("could not parse file %d: %s", i, err) - } - files = append(files, file) - } - - pkg, err := Check("p", fset, files) - if err != nil { - t.Fatal(err) - } - - for _, file := range files { - for _, group := range file.Comments { - for _, comment := range group.List { - s := comment.Text - if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" { - str, typ := split(s[2:len(s)-2], ", ") - str, val := split(str, "=>") - testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val) - } - } - } - } -} - -// split splits string s at the first occurrence of s. -func split(s, sep string) (string, string) { - i := strings.Index(s, sep) - return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):]) -} diff --git a/go/types/expr.go b/go/types/expr.go deleted file mode 100644 index 79dad12e06..0000000000 --- a/go/types/expr.go +++ /dev/null @@ -1,1497 +0,0 @@ -// Copyright 2012 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. - -// This file implements typechecking of expressions. - -package types - -import ( - "fmt" - "go/ast" - "go/token" - "math" - - "golang.org/x/tools/go/exact" -) - -/* -Basic algorithm: - -Expressions are checked recursively, top down. Expression checker functions -are generally of the form: - - func f(x *operand, e *ast.Expr, ...) - -where e is the expression to be checked, and x is the result of the check. -The check performed by f may fail in which case x.mode == invalid, and -related error messages will have been issued by f. - -If a hint argument is present, it is the composite literal element type -of an outer composite literal; it is used to type-check composite literal -elements that have no explicit type specification in the source -(e.g.: []T{{...}, {...}}, the hint is the type T in this case). - -All expressions are checked via rawExpr, which dispatches according -to expression kind. Upon returning, rawExpr is recording the types and -constant values for all expressions that have an untyped type (those types -may change on the way up in the expression tree). Usually these are constants, -but the results of comparisons or non-constant shifts of untyped constants -may also be untyped, but not constant. - -Untyped expressions may eventually become fully typed (i.e., not untyped), -typically when the value is assigned to a variable, or is used otherwise. -The updateExprType method is used to record this final type and update -the recorded types: the type-checked expression tree is again traversed down, -and the new type is propagated as needed. Untyped constant expression values -that become fully typed must now be representable by the full type (constant -sub-expression trees are left alone except for their roots). This mechanism -ensures that a client sees the actual (run-time) type an untyped value would -have. It also permits type-checking of lhs shift operands "as if the shift -were not present": when updateExprType visits an untyped lhs shift operand -and assigns it it's final type, that type must be an integer type, and a -constant lhs must be representable as an integer. - -When an expression gets its final type, either on the way out from rawExpr, -on the way down in updateExprType, or at the end of the type checker run, -the type (and constant value, if any) is recorded via Info.Types, if present. -*/ - -type opPredicates map[token.Token]func(Type) bool - -var unaryOpPredicates = opPredicates{ - token.ADD: isNumeric, - token.SUB: isNumeric, - token.XOR: isInteger, - token.NOT: isBoolean, -} - -func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool { - if pred := m[op]; pred != nil { - if !pred(x.typ) { - check.invalidOp(x.pos(), "operator %s not defined for %s", op, x) - return false - } - } else { - check.invalidAST(x.pos(), "unknown operator %s", op) - return false - } - return true -} - -// The unary expression e may be nil. It's passed in for better error messages only. -func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) { - switch op { - case token.AND: - // spec: "As an exception to the addressability - // requirement x may also be a composite literal." - if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable { - check.invalidOp(x.pos(), "cannot take address of %s", x) - x.mode = invalid - return - } - x.mode = value - x.typ = &Pointer{base: x.typ} - return - - case token.ARROW: - typ, ok := x.typ.Underlying().(*Chan) - if !ok { - check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) - x.mode = invalid - return - } - if typ.dir == SendOnly { - check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) - x.mode = invalid - return - } - x.mode = commaok - x.typ = typ.elem - check.hasCallOrRecv = true - return - } - - if !check.op(unaryOpPredicates, x, op) { - x.mode = invalid - return - } - - if x.mode == constant { - typ := x.typ.Underlying().(*Basic) - size := -1 - if isUnsigned(typ) { - size = int(check.conf.sizeof(typ)) - } - x.val = exact.UnaryOp(op, x.val, size) - // Typed constants must be representable in - // their type after each constant operation. - if isTyped(typ) { - if e != nil { - x.expr = e // for better error message - } - check.representable(x, typ) - } - return - } - - x.mode = value - // x.typ remains unchanged -} - -func isShift(op token.Token) bool { - return op == token.SHL || op == token.SHR -} - -func isComparison(op token.Token) bool { - // Note: tokens are not ordered well to make this much easier - switch op { - case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: - return true - } - return false -} - -func fitsFloat32(x exact.Value) bool { - f32, _ := exact.Float32Val(x) - f := float64(f32) - return !math.IsInf(f, 0) -} - -func roundFloat32(x exact.Value) exact.Value { - f32, _ := exact.Float32Val(x) - f := float64(f32) - if !math.IsInf(f, 0) { - return exact.MakeFloat64(f) - } - return nil -} - -func fitsFloat64(x exact.Value) bool { - f, _ := exact.Float64Val(x) - return !math.IsInf(f, 0) -} - -func roundFloat64(x exact.Value) exact.Value { - f, _ := exact.Float64Val(x) - if !math.IsInf(f, 0) { - return exact.MakeFloat64(f) - } - return nil -} - -// representableConst reports whether x can be represented as -// value of the given basic type kind and for the configuration -// provided (only needed for int/uint sizes). -// -// If rounded != nil, *rounded is set to the rounded value of x for -// representable floating-point values; it is left alone otherwise. -// It is ok to provide the addressof the first argument for rounded. -func representableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool { - switch x.Kind() { - case exact.Unknown: - return true - - case exact.Bool: - return as == Bool || as == UntypedBool - - case exact.Int: - if x, ok := exact.Int64Val(x); ok { - switch as { - case Int: - var s = uint(conf.sizeof(Typ[as])) * 8 - return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 - case Int8: - const s = 8 - return -1<<(s-1) <= x && x <= 1<<(s-1)-1 - case Int16: - const s = 16 - return -1<<(s-1) <= x && x <= 1<<(s-1)-1 - case Int32: - const s = 32 - return -1<<(s-1) <= x && x <= 1<<(s-1)-1 - case Int64: - return true - case Uint, Uintptr: - if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 { - return 0 <= x && x <= int64(1)<= 0 && n <= int(s) - case Uint64: - return exact.Sign(x) >= 0 && n <= 64 - case Float32, Complex64: - if rounded == nil { - return fitsFloat32(x) - } - r := roundFloat32(x) - if r != nil { - *rounded = r - return true - } - case Float64, Complex128: - if rounded == nil { - return fitsFloat64(x) - } - r := roundFloat64(x) - if r != nil { - *rounded = r - return true - } - case UntypedInt, UntypedFloat, UntypedComplex: - return true - } - - case exact.Float: - switch as { - case Float32, Complex64: - if rounded == nil { - return fitsFloat32(x) - } - r := roundFloat32(x) - if r != nil { - *rounded = r - return true - } - case Float64, Complex128: - if rounded == nil { - return fitsFloat64(x) - } - r := roundFloat64(x) - if r != nil { - *rounded = r - return true - } - case UntypedFloat, UntypedComplex: - return true - } - - case exact.Complex: - switch as { - case Complex64: - if rounded == nil { - return fitsFloat32(exact.Real(x)) && fitsFloat32(exact.Imag(x)) - } - re := roundFloat32(exact.Real(x)) - im := roundFloat32(exact.Imag(x)) - if re != nil && im != nil { - *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) - return true - } - case Complex128: - if rounded == nil { - return fitsFloat64(exact.Real(x)) && fitsFloat64(exact.Imag(x)) - } - re := roundFloat64(exact.Real(x)) - im := roundFloat64(exact.Imag(x)) - if re != nil && im != nil { - *rounded = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) - return true - } - case UntypedComplex: - return true - } - - case exact.String: - return as == String || as == UntypedString - - default: - unreachable() - } - - return false -} - -// representable checks that a constant operand is representable in the given basic type. -func (check *Checker) representable(x *operand, typ *Basic) { - assert(x.mode == constant) - if !representableConst(x.val, check.conf, typ.kind, &x.val) { - var msg string - if isNumeric(x.typ) && isNumeric(typ) { - // numeric conversion : error msg - // - // integer -> integer : overflows - // integer -> float : overflows (actually not possible) - // float -> integer : truncated - // float -> float : overflows - // - if !isInteger(x.typ) && isInteger(typ) { - msg = "%s truncated to %s" - } else { - msg = "%s overflows %s" - } - } else { - msg = "cannot convert %s to %s" - } - check.errorf(x.pos(), msg, x, typ) - x.mode = invalid - } -} - -// updateExprType updates the type of x to typ and invokes itself -// recursively for the operands of x, depending on expression kind. -// If typ is still an untyped and not the final type, updateExprType -// only updates the recorded untyped type for x and possibly its -// operands. Otherwise (i.e., typ is not an untyped type anymore, -// or it is the final type for x), the type and value are recorded. -// Also, if x is a constant, it must be representable as a value of typ, -// and if x is the (formerly untyped) lhs operand of a non-constant -// shift, it must be an integer value. -// -func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) { - old, found := check.untyped[x] - if !found { - return // nothing to do - } - - // update operands of x if necessary - switch x := x.(type) { - case *ast.BadExpr, - *ast.FuncLit, - *ast.CompositeLit, - *ast.IndexExpr, - *ast.SliceExpr, - *ast.TypeAssertExpr, - *ast.StarExpr, - *ast.KeyValueExpr, - *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - // These expression are never untyped - nothing to do. - // The respective sub-expressions got their final types - // upon assignment or use. - if debug { - check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ) - unreachable() - } - return - - case *ast.CallExpr: - // Resulting in an untyped constant (e.g., built-in complex). - // The respective calls take care of calling updateExprType - // for the arguments if necessary. - - case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr: - // An identifier denoting a constant, a constant literal, - // or a qualified identifier (imported untyped constant). - // No operands to take care of. - - case *ast.ParenExpr: - check.updateExprType(x.X, typ, final) - - case *ast.UnaryExpr: - // If x is a constant, the operands were constants. - // They don't need to be updated since they never - // get "materialized" into a typed value; and they - // will be processed at the end of the type check. - if old.val != nil { - break - } - check.updateExprType(x.X, typ, final) - - case *ast.BinaryExpr: - if old.val != nil { - break // see comment for unary expressions - } - if isComparison(x.Op) { - // The result type is independent of operand types - // and the operand types must have final types. - } else if isShift(x.Op) { - // The result type depends only on lhs operand. - // The rhs type was updated when checking the shift. - check.updateExprType(x.X, typ, final) - } else { - // The operand types match the result type. - check.updateExprType(x.X, typ, final) - check.updateExprType(x.Y, typ, final) - } - - default: - unreachable() - } - - // If the new type is not final and still untyped, just - // update the recorded type. - if !final && isUntyped(typ) { - old.typ = typ.Underlying().(*Basic) - check.untyped[x] = old - return - } - - // Otherwise we have the final (typed or untyped type). - // Remove it from the map of yet untyped expressions. - delete(check.untyped, x) - - // If x is the lhs of a shift, its final type must be integer. - // We already know from the shift check that it is representable - // as an integer if it is a constant. - if old.isLhs && !isInteger(typ) { - check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ) - return - } - - // Everything's fine, record final type and value for x. - check.recordTypeAndValue(x, old.mode, typ, old.val) -} - -// updateExprVal updates the value of x to val. -func (check *Checker) updateExprVal(x ast.Expr, val exact.Value) { - if info, ok := check.untyped[x]; ok { - info.val = val - check.untyped[x] = info - } -} - -// convertUntyped attempts to set the type of an untyped value to the target type. -func (check *Checker) convertUntyped(x *operand, target Type) { - if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { - return - } - - // TODO(gri) Sloppy code - clean up. This function is central - // to assignment and expression checking. - - if isUntyped(target) { - // both x and target are untyped - xkind := x.typ.(*Basic).kind - tkind := target.(*Basic).kind - if isNumeric(x.typ) && isNumeric(target) { - if xkind < tkind { - x.typ = target - check.updateExprType(x.expr, target, false) - } - } else if xkind != tkind { - goto Error - } - return - } - - // typed target - switch t := target.Underlying().(type) { - case *Basic: - if x.mode == constant { - check.representable(x, t) - if x.mode == invalid { - return - } - // expression value may have been rounded - update if needed - // TODO(gri) A floating-point value may silently underflow to - // zero. If it was negative, the sign is lost. See issue 6898. - check.updateExprVal(x.expr, x.val) - } else { - // Non-constant untyped values may appear as the - // result of comparisons (untyped bool), intermediate - // (delayed-checked) rhs operands of shifts, and as - // the value nil. - switch x.typ.(*Basic).kind { - case UntypedBool: - if !isBoolean(target) { - goto Error - } - case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex: - if !isNumeric(target) { - goto Error - } - case UntypedString: - // Non-constant untyped string values are not - // permitted by the spec and should not occur. - unreachable() - case UntypedNil: - // Unsafe.Pointer is a basic type that includes nil. - if !hasNil(target) { - goto Error - } - default: - goto Error - } - } - case *Interface: - if !x.isNil() && !t.Empty() /* empty interfaces are ok */ { - goto Error - } - // Update operand types to the default type rather then - // the target (interface) type: values must have concrete - // dynamic types. If the value is nil, keep it untyped - // (this is important for tools such as go vet which need - // the dynamic type for argument checking of say, print - // functions) - if x.isNil() { - target = Typ[UntypedNil] - } else { - // cannot assign untyped values to non-empty interfaces - if !t.Empty() { - goto Error - } - target = defaultType(x.typ) - } - case *Pointer, *Signature, *Slice, *Map, *Chan: - if !x.isNil() { - goto Error - } - // keep nil untyped - see comment for interfaces, above - target = Typ[UntypedNil] - default: - goto Error - } - - x.typ = target - check.updateExprType(x.expr, target, true) // UntypedNils are final - return - -Error: - check.errorf(x.pos(), "cannot convert %s to %s", x, target) - x.mode = invalid -} - -func (check *Checker) comparison(x, y *operand, op token.Token) { - // spec: "In any comparison, the first operand must be assignable - // to the type of the second operand, or vice versa." - err := "" - if x.assignableTo(check.conf, y.typ) || y.assignableTo(check.conf, x.typ) { - defined := false - switch op { - case token.EQL, token.NEQ: - // spec: "The equality operators == and != apply to operands that are comparable." - defined = Comparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) - case token.LSS, token.LEQ, token.GTR, token.GEQ: - // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." - defined = isOrdered(x.typ) - default: - unreachable() - } - if !defined { - typ := x.typ - if x.isNil() { - typ = y.typ - } - err = check.sprintf("operator %s not defined for %s", op, typ) - } - } else { - err = check.sprintf("mismatched types %s and %s", x.typ, y.typ) - } - - if err != "" { - check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err) - x.mode = invalid - return - } - - if x.mode == constant && y.mode == constant { - x.val = exact.MakeBool(exact.Compare(x.val, op, y.val)) - // The operands are never materialized; no need to update - // their types. - } else { - x.mode = value - // The operands have now their final types, which at run- - // time will be materialized. Update the expression trees. - // If the current types are untyped, the materialized type - // is the respective default type. - check.updateExprType(x.expr, defaultType(x.typ), true) - check.updateExprType(y.expr, defaultType(y.typ), true) - } - - // spec: "Comparison operators compare two operands and yield - // an untyped boolean value." - x.typ = Typ[UntypedBool] -} - -func (check *Checker) shift(x, y *operand, op token.Token) { - untypedx := isUntyped(x.typ) - - // The lhs must be of integer type or be representable - // as an integer; otherwise the shift has no chance. - if !x.isInteger() { - check.invalidOp(x.pos(), "shifted operand %s must be integer", x) - x.mode = invalid - return - } - - // spec: "The right operand in a shift expression must have unsigned - // integer type or be an untyped constant that can be converted to - // unsigned integer type." - switch { - case isInteger(y.typ) && isUnsigned(y.typ): - // nothing to do - case isUntyped(y.typ): - check.convertUntyped(y, Typ[UntypedInt]) - if y.mode == invalid { - x.mode = invalid - return - } - default: - check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) - x.mode = invalid - return - } - - if x.mode == constant { - if y.mode == constant { - // rhs must be an integer value - if !y.isInteger() { - check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) - x.mode = invalid - return - } - // rhs must be within reasonable bounds - const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64 - s, ok := exact.Uint64Val(y.val) - if !ok || s > stupidShift { - check.invalidOp(y.pos(), "stupid shift count %s", y) - x.mode = invalid - return - } - // The lhs is representable as an integer but may not be an integer - // (e.g., 2.0, an untyped float) - this can only happen for untyped - // non-integer numeric constants. Correct the type so that the shift - // result is of integer type. - if !isInteger(x.typ) { - x.typ = Typ[UntypedInt] - } - x.val = exact.Shift(x.val, op, uint(s)) - return - } - - // non-constant shift with constant lhs - if untypedx { - // spec: "If the left operand of a non-constant shift - // expression is an untyped constant, the type of the - // constant is what it would be if the shift expression - // were replaced by its left operand alone.". - // - // Delay operand checking until we know the final type: - // The lhs expression must be in the untyped map, mark - // the entry as lhs shift operand. - info, found := check.untyped[x.expr] - assert(found) - info.isLhs = true - check.untyped[x.expr] = info - // keep x's type - x.mode = value - return - } - } - - // constant rhs must be >= 0 - if y.mode == constant && exact.Sign(y.val) < 0 { - check.invalidOp(y.pos(), "shift count %s must not be negative", y) - } - - // non-constant shift - lhs must be an integer - if !isInteger(x.typ) { - check.invalidOp(x.pos(), "shifted operand %s must be integer", x) - x.mode = invalid - return - } - - x.mode = value -} - -var binaryOpPredicates = opPredicates{ - token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) }, - token.SUB: isNumeric, - token.MUL: isNumeric, - token.QUO: isNumeric, - token.REM: isInteger, - - token.AND: isInteger, - token.OR: isInteger, - token.XOR: isInteger, - token.AND_NOT: isInteger, - - token.LAND: isBoolean, - token.LOR: isBoolean, -} - -// The binary expression e may be nil. It's passed in for better error messages only. -func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token) { - var y operand - - check.expr(x, lhs) - check.expr(&y, rhs) - - if x.mode == invalid { - return - } - if y.mode == invalid { - x.mode = invalid - x.expr = y.expr - return - } - - if isShift(op) { - check.shift(x, &y, op) - return - } - - check.convertUntyped(x, y.typ) - if x.mode == invalid { - return - } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - x.mode = invalid - return - } - - if isComparison(op) { - check.comparison(x, &y, op) - return - } - - if !Identical(x.typ, y.typ) { - // only report an error if we have valid types - // (otherwise we had an error reported elsewhere already) - if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] { - check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ) - } - x.mode = invalid - return - } - - if !check.op(binaryOpPredicates, x, op) { - x.mode = invalid - return - } - - if (op == token.QUO || op == token.REM) && (x.mode == constant || isInteger(x.typ)) && y.mode == constant && exact.Sign(y.val) == 0 { - check.invalidOp(y.pos(), "division by zero") - x.mode = invalid - return - } - - if x.mode == constant && y.mode == constant { - typ := x.typ.Underlying().(*Basic) - // force integer division of integer operands - if op == token.QUO && isInteger(typ) { - op = token.QUO_ASSIGN - } - x.val = exact.BinaryOp(x.val, op, y.val) - // Typed constants must be representable in - // their type after each constant operation. - if isTyped(typ) { - if e != nil { - x.expr = e // for better error message - } - check.representable(x, typ) - } - return - } - - x.mode = value - // x.typ is unchanged -} - -// index checks an index expression for validity. -// If max >= 0, it is the upper bound for index. -// If index is valid and the result i >= 0, then i is the constant value of index. -func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { - var x operand - check.expr(&x, index) - if x.mode == invalid { - return - } - - // an untyped constant must be representable as Int - check.convertUntyped(&x, Typ[Int]) - if x.mode == invalid { - return - } - - // the index must be of integer type - if !isInteger(x.typ) { - check.invalidArg(x.pos(), "index %s must be integer", &x) - return - } - - // a constant index i must be in bounds - if x.mode == constant { - if exact.Sign(x.val) < 0 { - check.invalidArg(x.pos(), "index %s must not be negative", &x) - return - } - i, valid = exact.Int64Val(x.val) - if !valid || max >= 0 && i >= max { - check.errorf(x.pos(), "index %s is out of bounds", &x) - return i, false - } - // 0 <= i [ && i < max ] - return i, true - } - - return -1, true -} - -// indexElts checks the elements (elts) of an array or slice composite literal -// against the literal's element type (typ), and the element indices against -// the literal length if known (length >= 0). It returns the length of the -// literal (maximum index value + 1). -// -func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 { - visited := make(map[int64]bool, len(elts)) - var index, max int64 - for _, e := range elts { - // determine and check index - validIndex := false - eval := e - if kv, _ := e.(*ast.KeyValueExpr); kv != nil { - if i, ok := check.index(kv.Key, length); ok { - if i >= 0 { - index = i - validIndex = true - } else { - check.errorf(e.Pos(), "index %s must be integer constant", kv.Key) - } - } - eval = kv.Value - } else if length >= 0 && index >= length { - check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length) - } else { - validIndex = true - } - - // if we have a valid index, check for duplicate entries - if validIndex { - if visited[index] { - check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index) - } - visited[index] = true - } - index++ - if index > max { - max = index - } - - // check element against composite literal element type - var x operand - check.exprWithHint(&x, eval, typ) - if !check.assignment(&x, typ) && x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ) - } - } - return max -} - -// exprKind describes the kind of an expression; the kind -// determines if an expression is valid in 'statement context'. -type exprKind int - -const ( - conversion exprKind = iota - expression - statement -) - -// rawExpr typechecks expression e and initializes x with the expression -// value or type. If an error occurred, x.mode is set to invalid. -// If hint != nil, it is the type of a composite literal element. -// -func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { - if trace { - check.trace(e.Pos(), "%s", e) - check.indent++ - defer func() { - check.indent-- - check.trace(e.Pos(), "=> %s", x) - }() - } - - kind := check.exprInternal(x, e, hint) - - // convert x into a user-friendly set of values - // TODO(gri) this code can be simplified - var typ Type - var val exact.Value - switch x.mode { - case invalid: - typ = Typ[Invalid] - case novalue: - typ = (*Tuple)(nil) - case constant: - typ = x.typ - val = x.val - default: - typ = x.typ - } - assert(x.expr != nil && typ != nil) - - if isUntyped(typ) { - // delay type and value recording until we know the type - // or until the end of type checking - check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) - } else { - check.recordTypeAndValue(e, x.mode, typ, val) - } - - return kind -} - -// exprInternal contains the core of type checking of expressions. -// Must only be called by rawExpr. -// -func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { - // make sure x has a valid state in case of bailout - // (was issue 5770) - x.mode = invalid - x.typ = Typ[Invalid] - - switch e := e.(type) { - case *ast.BadExpr: - goto Error // error was reported before - - case *ast.Ident: - check.ident(x, e, nil, nil) - - case *ast.Ellipsis: - // ellipses are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.error(e.Pos(), "invalid use of '...'") - goto Error - - case *ast.BasicLit: - x.setConst(e.Kind, e.Value) - if x.mode == invalid { - check.invalidAST(e.Pos(), "invalid literal %v", e.Value) - goto Error - } - - case *ast.FuncLit: - if sig, ok := check.typ(e.Type).(*Signature); ok { - // Anonymous functions are considered part of the - // init expression/func declaration which contains - // them: use existing package-level declaration info. - check.funcBody(check.decl, "", sig, e.Body) - x.mode = value - x.typ = sig - } else { - check.invalidAST(e.Pos(), "invalid function literal %s", e) - goto Error - } - - case *ast.CompositeLit: - typ := hint - openArray := false - if e.Type != nil { - // [...]T array types may only appear with composite literals. - // Check for them here so we don't have to handle ... in general. - typ = nil - if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { - if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { - // 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, elem: check.typ(atyp.Elt)} - openArray = true - } - } - if typ == nil { - typ = check.typ(e.Type) - } - } - if typ == nil { - // TODO(gri) provide better error messages depending on context - check.error(e.Pos(), "missing type in composite literal") - goto Error - } - - switch typ, _ := deref(typ); utyp := typ.Underlying().(type) { - case *Struct: - if len(e.Elts) == 0 { - break - } - fields := utyp.fields - if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { - // all elements must have keys - visited := make([]bool, len(fields)) - for _, e := range e.Elts { - kv, _ := e.(*ast.KeyValueExpr) - if kv == nil { - check.error(e.Pos(), "mixture of field:value and value elements in struct literal") - continue - } - key, _ := kv.Key.(*ast.Ident) - if key == nil { - check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) - continue - } - i := fieldIndex(utyp.fields, check.pkg, key.Name) - if i < 0 { - check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) - continue - } - fld := fields[i] - check.recordUse(key, fld) - // 0 <= i < len(fields) - if visited[i] { - check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) - continue - } - visited[i] = true - check.expr(x, kv.Value) - etyp := fld.typ - if !check.assignment(x, etyp) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) - } - continue - } - } - } else { - // no element must have a key - for i, e := range e.Elts { - if kv, _ := e.(*ast.KeyValueExpr); kv != nil { - check.error(kv.Pos(), "mixture of field:value and value elements in struct literal") - continue - } - check.expr(x, e) - if i >= len(fields) { - check.error(x.pos(), "too many values in struct literal") - break // cannot continue - } - // i < len(fields) - fld := fields[i] - if !fld.Exported() && fld.pkg != check.pkg { - check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ) - continue - } - etyp := fld.typ - if !check.assignment(x, etyp) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) - } - continue - } - } - if len(e.Elts) < len(fields) { - check.error(e.Rbrace, "too few values in struct literal") - // ok to continue - } - } - - case *Array: - n := check.indexedElts(e.Elts, utyp.elem, utyp.len) - // if we have an "open" [...]T array, set the length now that we know it - if openArray { - utyp.len = n - } - - case *Slice: - check.indexedElts(e.Elts, utyp.elem, -1) - - case *Map: - visited := make(map[interface{}][]Type, len(e.Elts)) - for _, e := range e.Elts { - kv, _ := e.(*ast.KeyValueExpr) - if kv == nil { - check.error(e.Pos(), "missing key in map literal") - continue - } - check.exprWithHint(x, kv.Key, 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) - } - continue - } - if x.mode == constant { - duplicate := false - // if the key is of interface type, the type is also significant when checking for duplicates - if _, ok := utyp.key.Underlying().(*Interface); ok { - for _, vtyp := range visited[x.val] { - if Identical(vtyp, x.typ) { - duplicate = true - break - } - } - visited[x.val] = append(visited[x.val], x.typ) - } else { - _, duplicate = visited[x.val] - visited[x.val] = nil - } - if duplicate { - check.errorf(x.pos(), "duplicate key %s in map literal", x.val) - continue - } - } - check.exprWithHint(x, kv.Value, utyp.elem) - if !check.assignment(x, utyp.elem) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elem) - } - continue - } - } - - default: - // if utyp is invalid, an error was reported before - if utyp != Typ[Invalid] { - check.errorf(e.Pos(), "invalid composite literal type %s", typ) - goto Error - } - } - - x.mode = value - x.typ = typ - - case *ast.ParenExpr: - kind := check.rawExpr(x, e.X, nil) - x.expr = e - return kind - - case *ast.SelectorExpr: - check.selector(x, e) - - case *ast.IndexExpr: - check.expr(x, e.X) - if x.mode == invalid { - goto Error - } - - valid := false - length := int64(-1) // valid if >= 0 - switch typ := x.typ.Underlying().(type) { - case *Basic: - if isString(typ) { - valid = true - if x.mode == constant { - length = int64(len(exact.StringVal(x.val))) - } - // an indexed string always yields a byte value - // (not a constant) even if the string and the - // index are constant - x.mode = value - x.typ = universeByte // use 'byte' name - } - - case *Array: - valid = true - length = typ.len - if x.mode != variable { - x.mode = value - } - x.typ = typ.elem - - case *Pointer: - if typ, _ := typ.base.Underlying().(*Array); typ != nil { - valid = true - length = typ.len - x.mode = variable - x.typ = typ.elem - } - - case *Slice: - valid = true - x.mode = variable - x.typ = typ.elem - - case *Map: - var key operand - check.expr(&key, e.Index) - 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) - } - goto Error - } - x.mode = mapindex - x.typ = typ.elem - x.expr = e - return expression - } - - if !valid { - check.invalidOp(x.pos(), "cannot index %s", x) - goto Error - } - - if e.Index == nil { - check.invalidAST(e.Pos(), "missing index for %s", x) - goto Error - } - - check.index(e.Index, length) - // ok to continue - - case *ast.SliceExpr: - check.expr(x, e.X) - if x.mode == invalid { - goto Error - } - - valid := false - length := int64(-1) // valid if >= 0 - switch typ := x.typ.Underlying().(type) { - case *Basic: - if isString(typ) { - if e.Slice3 { - check.invalidOp(x.pos(), "3-index slice of string") - goto Error - } - valid = true - if x.mode == constant { - length = int64(len(exact.StringVal(x.val))) - } - // spec: "For untyped string operands the result - // is a non-constant value of type string." - if typ.kind == UntypedString { - x.typ = Typ[String] - } - } - - case *Array: - valid = true - length = typ.len - if x.mode != variable { - check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) - goto Error - } - x.typ = &Slice{elem: typ.elem} - - case *Pointer: - if typ, _ := typ.base.Underlying().(*Array); typ != nil { - valid = true - length = typ.len - x.typ = &Slice{elem: typ.elem} - } - - case *Slice: - valid = true - // x.typ doesn't change - } - - if !valid { - check.invalidOp(x.pos(), "cannot slice %s", x) - goto Error - } - - x.mode = value - - // spec: "Only the first index may be omitted; it defaults to 0." - if e.Slice3 && (e.High == nil || e.Max == nil) { - check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice") - goto Error - } - - // check indices - var ind [3]int64 - for i, expr := range []ast.Expr{e.Low, e.High, e.Max} { - x := int64(-1) - switch { - case expr != nil: - // The "capacity" is only known statically for strings, arrays, - // and pointers to arrays, and it is the same as the length for - // those types. - max := int64(-1) - if length >= 0 { - max = length + 1 - } - if t, ok := check.index(expr, max); ok && t >= 0 { - x = t - } - case i == 0: - // default is 0 for the first index - x = 0 - case length >= 0: - // default is length (== capacity) otherwise - x = length - } - ind[i] = x - } - - // constant indices must be in range - // (check.index already checks that existing indices >= 0) - L: - for i, x := range ind[:len(ind)-1] { - if x > 0 { - for _, y := range ind[i+1:] { - if y >= 0 && x > y { - check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y) - break L // only report one error, ok to continue - } - } - } - } - - case *ast.TypeAssertExpr: - check.expr(x, e.X) - if x.mode == invalid { - goto Error - } - xtyp, _ := x.typ.Underlying().(*Interface) - if xtyp == nil { - check.invalidOp(x.pos(), "%s is not an interface", x) - goto Error - } - // x.(type) expressions are handled explicitly in type switches - if e.Type == nil { - check.invalidAST(e.Pos(), "use of .(type) outside type switch") - goto Error - } - T := check.typ(e.Type) - if T == Typ[Invalid] { - goto Error - } - check.typeAssertion(x.pos(), x, xtyp, T) - x.mode = commaok - x.typ = T - - case *ast.CallExpr: - return check.call(x, e) - - case *ast.StarExpr: - check.exprOrType(x, e.X) - switch x.mode { - case invalid: - goto Error - case typexpr: - x.typ = &Pointer{base: x.typ} - default: - if typ, ok := x.typ.Underlying().(*Pointer); ok { - x.mode = variable - x.typ = typ.base - } else { - check.invalidOp(x.pos(), "cannot indirect %s", x) - goto Error - } - } - - case *ast.UnaryExpr: - check.expr(x, e.X) - if x.mode == invalid { - goto Error - } - check.unary(x, e, e.Op) - if x.mode == invalid { - goto Error - } - if e.Op == token.ARROW { - x.expr = e - return statement // receive operations may appear in statement context - } - - case *ast.BinaryExpr: - check.binary(x, e, e.X, e.Y, e.Op) - if x.mode == invalid { - goto Error - } - - case *ast.KeyValueExpr: - // key:value expressions are handled in composite literals - check.invalidAST(e.Pos(), "no key:value expected") - goto Error - - case *ast.ArrayType, *ast.StructType, *ast.FuncType, - *ast.InterfaceType, *ast.MapType, *ast.ChanType: - x.mode = typexpr - x.typ = check.typ(e) - // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue - // even though check.typ has already called it. This is fine as both - // times the same expression and type are recorded. It is also not a - // performance issue because we only reach here for composite literal - // types, which are comparatively rare. - - default: - panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e)) - } - - // everything went well - x.expr = e - return expression - -Error: - x.mode = invalid - x.expr = e - return statement // avoid follow-up errors -} - -// typeAssertion checks that x.(T) is legal; xtyp must be the type of x. -func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) { - method, wrongType := assertableTo(xtyp, T) - if method == nil { - return - } - - var msg string - if wrongType { - msg = "wrong type for method" - } else { - msg = "missing method" - } - check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name) -} - -// expr typechecks expression e and initializes x with the expression value. -// If an error occurred, x.mode is set to invalid. -// -func (check *Checker) expr(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) - var msg string - switch x.mode { - default: - return - case novalue: - msg = "used as value" - case builtin: - msg = "must be called" - case typexpr: - msg = "is not an expression" - } - check.errorf(x.pos(), "%s %s", x, msg) - x.mode = invalid -} - -// exprWithHint typechecks expression e and initializes x with the expression value. -// If an error occurred, x.mode is set to invalid. -// If hint != nil, it is the type of a composite literal element. -// -func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) { - assert(hint != nil) - check.rawExpr(x, e, hint) - var msg string - switch x.mode { - default: - return - case novalue: - msg = "used as value" - case builtin: - msg = "must be called" - case typexpr: - msg = "is not an expression" - } - check.errorf(x.pos(), "%s %s", x, msg) - x.mode = invalid -} - -// exprOrType typechecks expression or type e and initializes x with the expression value or type. -// If an error occurred, x.mode is set to invalid. -// -func (check *Checker) exprOrType(x *operand, e ast.Expr) { - check.rawExpr(x, e, nil) - if x.mode == novalue { - check.errorf(x.pos(), "%s used as value or type", x) - x.mode = invalid - } -} diff --git a/go/types/exprstring.go b/go/types/exprstring.go deleted file mode 100644 index 370bdf3532..0000000000 --- a/go/types/exprstring.go +++ /dev/null @@ -1,220 +0,0 @@ -// 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. - -// This file implements printing of expressions. - -package types - -import ( - "bytes" - "go/ast" -) - -// ExprString returns the (possibly simplified) string representation for x. -func ExprString(x ast.Expr) string { - var buf bytes.Buffer - WriteExpr(&buf, x) - return buf.String() -} - -// WriteExpr writes the (possibly simplified) string representation for x to buf. -func WriteExpr(buf *bytes.Buffer, x ast.Expr) { - // The AST preserves source-level parentheses so there is - // no need to introduce them here to correct for different - // operator precedences. (This assumes that the AST was - // generated by a Go parser.) - - switch x := x.(type) { - default: - buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr - - case *ast.Ident: - buf.WriteString(x.Name) - - case *ast.Ellipsis: - buf.WriteString("...") - if x.Elt != nil { - WriteExpr(buf, x.Elt) - } - - case *ast.BasicLit: - buf.WriteString(x.Value) - - case *ast.FuncLit: - buf.WriteByte('(') - WriteExpr(buf, x.Type) - buf.WriteString(" literal)") // simplified - - case *ast.CompositeLit: - buf.WriteByte('(') - WriteExpr(buf, x.Type) - buf.WriteString(" literal)") // simplified - - case *ast.ParenExpr: - buf.WriteByte('(') - WriteExpr(buf, x.X) - buf.WriteByte(')') - - case *ast.SelectorExpr: - WriteExpr(buf, x.X) - buf.WriteByte('.') - buf.WriteString(x.Sel.Name) - - case *ast.IndexExpr: - WriteExpr(buf, x.X) - buf.WriteByte('[') - WriteExpr(buf, x.Index) - buf.WriteByte(']') - - case *ast.SliceExpr: - WriteExpr(buf, x.X) - buf.WriteByte('[') - if x.Low != nil { - WriteExpr(buf, x.Low) - } - buf.WriteByte(':') - if x.High != nil { - WriteExpr(buf, x.High) - } - if x.Slice3 { - buf.WriteByte(':') - if x.Max != nil { - WriteExpr(buf, x.Max) - } - } - buf.WriteByte(']') - - case *ast.TypeAssertExpr: - WriteExpr(buf, x.X) - buf.WriteString(".(") - WriteExpr(buf, x.Type) - buf.WriteByte(')') - - case *ast.CallExpr: - WriteExpr(buf, x.Fun) - buf.WriteByte('(') - for i, arg := range x.Args { - if i > 0 { - buf.WriteString(", ") - } - WriteExpr(buf, arg) - } - if x.Ellipsis.IsValid() { - buf.WriteString("...") - } - buf.WriteByte(')') - - case *ast.StarExpr: - buf.WriteByte('*') - WriteExpr(buf, x.X) - - case *ast.UnaryExpr: - buf.WriteString(x.Op.String()) - WriteExpr(buf, x.X) - - case *ast.BinaryExpr: - WriteExpr(buf, x.X) - buf.WriteByte(' ') - buf.WriteString(x.Op.String()) - buf.WriteByte(' ') - WriteExpr(buf, x.Y) - - case *ast.ArrayType: - buf.WriteByte('[') - if x.Len != nil { - WriteExpr(buf, x.Len) - } - buf.WriteByte(']') - WriteExpr(buf, x.Elt) - - case *ast.StructType: - buf.WriteString("struct{") - writeFieldList(buf, x.Fields, "; ", false) - buf.WriteByte('}') - - case *ast.FuncType: - buf.WriteString("func") - writeSigExpr(buf, x) - - case *ast.InterfaceType: - buf.WriteString("interface{") - writeFieldList(buf, x.Methods, "; ", true) - buf.WriteByte('}') - - case *ast.MapType: - buf.WriteString("map[") - WriteExpr(buf, x.Key) - buf.WriteByte(']') - WriteExpr(buf, x.Value) - - case *ast.ChanType: - var s string - switch x.Dir { - case ast.SEND: - s = "chan<- " - case ast.RECV: - s = "<-chan " - default: - s = "chan " - } - buf.WriteString(s) - WriteExpr(buf, x.Value) - } -} - -func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { - buf.WriteByte('(') - writeFieldList(buf, sig.Params, ", ", false) - buf.WriteByte(')') - - res := sig.Results - n := res.NumFields() - if n == 0 { - // no result - return - } - - buf.WriteByte(' ') - if n == 1 && len(res.List[0].Names) == 0 { - // single unnamed result - WriteExpr(buf, res.List[0].Type) - return - } - - // multiple or named result(s) - buf.WriteByte('(') - writeFieldList(buf, res, ", ", false) - buf.WriteByte(')') -} - -func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) { - for i, f := range fields.List { - if i > 0 { - buf.WriteString(sep) - } - - // field list names - for i, name := range f.Names { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(name.Name) - } - - // types of interface methods consist of signatures only - if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { - writeSigExpr(buf, sig) - continue - } - - // named fields are separated with a blank from the field type - if len(f.Names) > 0 { - buf.WriteByte(' ') - } - - WriteExpr(buf, f.Type) - - // ignore tag - } -} diff --git a/go/types/exprstring_test.go b/go/types/exprstring_test.go deleted file mode 100644 index cfd1472216..0000000000 --- a/go/types/exprstring_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// 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_test - -import ( - "go/parser" - "testing" - - . "golang.org/x/tools/go/types" -) - -var testExprs = []testEntry{ - // basic type literals - dup("x"), - dup("true"), - dup("42"), - dup("3.1415"), - dup("2.71828i"), - dup(`'a'`), - dup(`"foo"`), - dup("`bar`"), - - // func and composite literals - {"func(){}", "(func() literal)"}, - {"func(x int) complex128 {}", "(func(x int) complex128 literal)"}, - {"[]int{1, 2, 3}", "([]int literal)"}, - - // non-type expressions - dup("(x)"), - dup("x.f"), - dup("a[i]"), - - dup("s[:]"), - dup("s[i:]"), - dup("s[:j]"), - dup("s[i:j]"), - dup("s[:j:k]"), - dup("s[i:j:k]"), - - dup("x.(T)"), - - dup("x.([10]int)"), - dup("x.([...]int)"), - - dup("x.(struct{})"), - dup("x.(struct{x int; y, z float32; E})"), - - dup("x.(func())"), - dup("x.(func(x int))"), - dup("x.(func() int)"), - dup("x.(func(x, y int, z float32) (r int))"), - dup("x.(func(a, b, c int))"), - dup("x.(func(x ...T))"), - - dup("x.(interface{})"), - dup("x.(interface{m(); n(x int); E})"), - dup("x.(interface{m(); n(x int) T; E; F})"), - - dup("x.(map[K]V)"), - - dup("x.(chan E)"), - dup("x.(<-chan E)"), - dup("x.(chan<- chan int)"), - dup("x.(chan<- <-chan int)"), - dup("x.(<-chan chan int)"), - dup("x.(chan (<-chan int))"), - - dup("f()"), - dup("f(x)"), - dup("int(x)"), - dup("f(x, x + y)"), - dup("f(s...)"), - dup("f(a, s...)"), - - dup("*x"), - dup("&x"), - dup("x + y"), - dup("x + y << (2 * s)"), -} - -func TestExprString(t *testing.T) { - for _, test := range testExprs { - x, err := parser.ParseExpr(test.src) - if err != nil { - t.Errorf("%s: %s", test.src, err) - continue - } - if got := ExprString(x); got != test.str { - t.Errorf("%s: got %s, want %s", test.src, got, test.str) - } - } -} diff --git a/go/types/hilbert_test.go b/go/types/hilbert_test.go deleted file mode 100644 index b555721819..0000000000 --- a/go/types/hilbert_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// 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_test - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "testing" - - . "golang.org/x/tools/go/types" -) - -var ( - H = flag.Int("H", 5, "Hilbert matrix size") - out = flag.String("out", "", "write generated program to out") -) - -func TestHilbert(t *testing.T) { - // generate source - src := program(*H, *out) - if *out != "" { - ioutil.WriteFile(*out, src, 0666) - return - } - - // parse source - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "hilbert.go", src, 0) - if err != nil { - t.Fatal(err) - } - - // type-check file - DefPredeclaredTestFuncs() // define assert built-in - _, err = Check(f.Name.Name, fset, []*ast.File{f}) - if err != nil { - t.Fatal(err) - } -} - -func program(n int, out string) []byte { - var g gen - - g.p(`// WARNING: GENERATED FILE - DO NOT MODIFY MANUALLY! -// (To generate, in go/types directory: go test -run=Hilbert -H=%d -out=%q) - -// This program tests arbitrary precision constant arithmetic -// by generating the constant elements of a Hilbert matrix H, -// its inverse I, and the product P = H*I. The product should -// be the identity matrix. -package main - -func main() { - if !ok { - printProduct() - return - } - println("PASS") -} - -`, n, out) - g.hilbert(n) - g.inverse(n) - g.product(n) - g.verify(n) - g.printProduct(n) - g.binomials(2*n - 1) - g.factorials(2*n - 1) - - return g.Bytes() -} - -type gen struct { - bytes.Buffer -} - -func (g *gen) p(format string, args ...interface{}) { - fmt.Fprintf(&g.Buffer, format, args...) -} - -func (g *gen) hilbert(n int) { - g.p(`// Hilbert matrix, n = %d -const ( -`, n) - for i := 0; i < n; i++ { - g.p("\t") - for j := 0; j < n; j++ { - if j > 0 { - g.p(", ") - } - g.p("h%d_%d", i, j) - } - if i == 0 { - g.p(" = ") - for j := 0; j < n; j++ { - if j > 0 { - g.p(", ") - } - g.p("1.0/(iota + %d)", j+1) - } - } - g.p("\n") - } - g.p(")\n\n") -} - -func (g *gen) inverse(n int) { - g.p(`// Inverse Hilbert matrix -const ( -`) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - s := "+" - if (i+j)&1 != 0 { - s = "-" - } - g.p("\ti%d_%d = %s%d * b%d_%d * b%d_%d * b%d_%d * b%d_%d\n", - i, j, s, i+j+1, n+i, n-j-1, n+j, n-i-1, i+j, i, i+j, i) - } - g.p("\n") - } - g.p(")\n\n") -} - -func (g *gen) product(n int) { - g.p(`// Product matrix -const ( -`) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - g.p("\tp%d_%d = ", i, j) - for k := 0; k < n; k++ { - if k > 0 { - g.p(" + ") - } - g.p("h%d_%d*i%d_%d", i, k, k, j) - } - g.p("\n") - } - g.p("\n") - } - g.p(")\n\n") -} - -func (g *gen) verify(n int) { - g.p(`// Verify that product is the identity matrix -const ok = -`) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - if j == 0 { - g.p("\t") - } else { - g.p(" && ") - } - v := 0 - if i == j { - v = 1 - } - g.p("p%d_%d == %d", i, j, v) - } - g.p(" &&\n") - } - g.p("\ttrue\n\n") - - // verify ok at type-check time - if *out == "" { - g.p("const _ = assert(ok)\n\n") - } -} - -func (g *gen) printProduct(n int) { - g.p("func printProduct() {\n") - for i := 0; i < n; i++ { - g.p("\tprintln(") - for j := 0; j < n; j++ { - if j > 0 { - g.p(", ") - } - g.p("p%d_%d", i, j) - } - g.p(")\n") - } - g.p("}\n\n") -} - -func (g *gen) mulRange(a, b int) { - if a > b { - g.p("1") - return - } - for i := a; i <= b; i++ { - if i > a { - g.p("*") - } - g.p("%d", i) - } -} - -func (g *gen) binomials(n int) { - g.p(`// Binomials -const ( -`) - for j := 0; j <= n; j++ { - if j > 0 { - g.p("\n") - } - for k := 0; k <= j; k++ { - g.p("\tb%d_%d = f%d / (f%d*f%d)\n", j, k, j, k, j-k) - } - } - g.p(")\n\n") -} - -func (g *gen) factorials(n int) { - g.p(`// Factorials -const ( - f0 = 1 - f1 = 1 -`) - for i := 2; i <= n; i++ { - g.p("\tf%d = f%d * %d\n", i, i-1, i) - } - g.p(")\n\n") -} diff --git a/go/types/initorder.go b/go/types/initorder.go deleted file mode 100644 index 0fd567b269..0000000000 --- a/go/types/initorder.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2014 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 ( - "container/heap" - "fmt" -) - -// initOrder computes the Info.InitOrder for package variables. -func (check *Checker) initOrder() { - // An InitOrder may already have been computed if a package is - // built from several calls to (*Checker).Files. Clear it. - check.Info.InitOrder = check.Info.InitOrder[:0] - - // compute the object dependency graph and - // initialize a priority queue with the list - // of graph nodes - pq := nodeQueue(dependencyGraph(check.objMap)) - heap.Init(&pq) - - const debug = false - if debug { - fmt.Printf("package %s: object dependency graph\n", check.pkg.Name()) - for _, n := range pq { - for _, o := range n.out { - fmt.Printf("\t%s -> %s\n", n.obj.Name(), o.obj.Name()) - } - } - fmt.Println() - fmt.Printf("package %s: initialization order\n", check.pkg.Name()) - } - - // determine initialization order by removing the highest priority node - // (the one with the fewest dependencies) and its edges from the graph, - // repeatedly, until there are no nodes left. - // In a valid Go program, those nodes always have zero dependencies (after - // removing all incoming dependencies), otherwise there are initialization - // cycles. - mark := 0 - emitted := make(map[*declInfo]bool) - for len(pq) > 0 { - // get the next node - n := heap.Pop(&pq).(*objNode) - - // if n still depends on other nodes, we have a cycle - if n.in > 0 { - mark++ // mark nodes using a different value each time - cycle := findPath(n, n, mark) - if i := valIndex(cycle); i >= 0 { - check.reportCycle(cycle, i) - } - // ok to continue, but the variable initialization order - // will be incorrect at this point since it assumes no - // cycle errors - } - - // reduce dependency count of all dependent nodes - // and update priority queue - for _, out := range n.out { - out.in-- - heap.Fix(&pq, out.index) - } - - // record the init order for variables with initializers only - v, _ := n.obj.(*Var) - info := check.objMap[v] - if v == nil || !info.hasInitializer() { - continue - } - - // n:1 variable declarations such as: a, b = f() - // introduce a node for each lhs variable (here: a, b); - // but they all have the same initializer - emit only - // one, for the first variable seen - if emitted[info] { - continue // initializer already emitted, if any - } - emitted[info] = true - - infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment) - if infoLhs == nil { - infoLhs = []*Var{v} - } - init := &Initializer{infoLhs, info.init} - check.Info.InitOrder = append(check.Info.InitOrder, init) - - if debug { - fmt.Printf("\t%s\n", init) - } - } - - if debug { - fmt.Println() - } -} - -// findPath returns the (reversed) list of nodes z, ... c, b, a, -// such that there is a path (list of edges) from a to z. -// If there is no such path, the result is nil. -// Nodes marked with the value mark are considered "visited"; -// unvisited nodes are marked during the graph search. -func findPath(a, z *objNode, mark int) []*objNode { - if a.mark == mark { - return nil // node already seen - } - a.mark = mark - - for _, n := range a.out { - if n == z { - return []*objNode{z} - } - if P := findPath(n, z, mark); P != nil { - return append(P, n) - } - } - - return nil -} - -// valIndex returns the index of the first constant or variable in a, -// if any; or a value < 0. -func valIndex(a []*objNode) int { - for i, n := range a { - switch n.obj.(type) { - case *Const, *Var: - return i - } - } - return -1 -} - -// reportCycle reports an error for the cycle starting at i. -func (check *Checker) reportCycle(cycle []*objNode, i int) { - obj := cycle[i].obj - check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name()) - // print cycle - for _ = range cycle { - check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented - i++ - if i >= len(cycle) { - i = 0 - } - obj = cycle[i].obj - } - check.errorf(obj.Pos(), "\t%s", obj.Name()) -} - -// An objNode represents a node in the object dependency graph. -// Each node b in a.out represents an edge a->b indicating that -// b depends on a. -// Nodes may be marked for cycle detection. A node n is marked -// if n.mark corresponds to the current mark value. -type objNode struct { - obj Object // object represented by this node - in int // number of nodes this node depends on - out []*objNode // list of nodes that depend on this node - index int // node index in list of nodes - mark int // for cycle detection -} - -// dependencyGraph computes the transposed object dependency graph -// from the given objMap. The transposed graph is returned as a list -// of nodes; an edge d->n indicates that node n depends on node d. -func dependencyGraph(objMap map[Object]*declInfo) []*objNode { - // M maps each object to its corresponding node - M := make(map[Object]*objNode, len(objMap)) - for obj := range objMap { - M[obj] = &objNode{obj: obj} - } - - // G is the graph of nodes n - G := make([]*objNode, len(M)) - i := 0 - for obj, n := range M { - deps := objMap[obj].deps - n.in = len(deps) - for d := range deps { - d := M[d] // node n depends on node d - d.out = append(d.out, n) // add edge d->n - } - - G[i] = n - n.index = i - i++ - } - - return G -} - -// nodeQueue implements the container/heap interface; -// a nodeQueue may be used as a priority queue. -type nodeQueue []*objNode - -func (a nodeQueue) Len() int { return len(a) } - -func (a nodeQueue) Swap(i, j int) { - x, y := a[i], a[j] - a[i], a[j] = y, x - x.index, y.index = j, i -} - -func (a nodeQueue) Less(i, j int) bool { - x, y := a[i], a[j] - // nodes are prioritized by number of incoming dependencies (1st key) - // and source order (2nd key) - return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order() -} - -func (a *nodeQueue) Push(x interface{}) { - panic("unreachable") -} - -func (a *nodeQueue) Pop() interface{} { - n := len(*a) - x := (*a)[n-1] - x.index = -1 // for safety - *a = (*a)[:n-1] - return x -} diff --git a/go/types/issues_test.go b/go/types/issues_test.go deleted file mode 100644 index 04d8b37192..0000000000 --- a/go/types/issues_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// 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. - -// This file implements tests for various issues. - -package types_test - -import ( - "fmt" - "go/ast" - "go/parser" - "sort" - "strings" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -func TestIssue5770(t *testing.T) { - src := `package p; type S struct{T}` - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - - _, err = Check(f.Name.Name, fset, []*ast.File{f}) // do not crash - want := "undeclared name: T" - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("got: %v; want: %s", err, want) - } -} - -func TestIssue5849(t *testing.T) { - src := ` -package p -var ( - s uint - _ = uint8(8) - _ = uint16(16) << s - _ = uint32(32 << s) - _ = uint64(64 << s + s) - _ = (interface{})("foo") - _ = (interface{})(nil) -)` - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - - var conf Config - types := make(map[ast.Expr]TypeAndValue) - _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) - if err != nil { - t.Fatal(err) - } - - for x, tv := range types { - var want Type - switch x := x.(type) { - case *ast.BasicLit: - switch x.Value { - case `8`: - want = Typ[Uint8] - case `16`: - want = Typ[Uint16] - case `32`: - want = Typ[Uint32] - case `64`: - want = Typ[Uint] // because of "+ s", s is of type uint - case `"foo"`: - want = Typ[String] - } - case *ast.Ident: - if x.Name == "nil" { - want = Typ[UntypedNil] - } - } - if want != nil && !Identical(tv.Type, want) { - t.Errorf("got %s; want %s", tv.Type, want) - } - } -} - -func TestIssue6413(t *testing.T) { - src := ` -package p -func f() int { - defer f() - go f() - return 0 -} -` - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - - var conf Config - types := make(map[ast.Expr]TypeAndValue) - _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) - if err != nil { - t.Fatal(err) - } - - want := Typ[Int] - n := 0 - for x, tv := range types { - if _, ok := x.(*ast.CallExpr); ok { - if tv.Type != want { - t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want) - } - n++ - } - } - - if n != 2 { - t.Errorf("got %d CallExprs; want 2", n) - } -} - -func TestIssue7245(t *testing.T) { - src := ` -package p -func (T) m() (res bool) { return } -type T struct{} // receiver type after method declaration -` - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - - var conf Config - defs := make(map[*ast.Ident]Object) - _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs}) - if err != nil { - t.Fatal(err) - } - - m := f.Decls[0].(*ast.FuncDecl) - res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0) - res2 := defs[m.Type.Results.List[0].Names[0]].(*Var) - - if res1 != res2 { - t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2) - } -} - -// This tests that uses of existing vars on the LHS of an assignment -// are Uses, not Defs; and also that the (illegal) use of a non-var on -// the LHS of an assignment is a Use nonetheless. -func TestIssue7827(t *testing.T) { - const src = ` -package p -func _() { - const w = 1 // defs w - x, y := 2, 3 // defs x, y - w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w - _, _, _ = x, y, z // uses x, y, z -} -` - const want = `L3 defs func p._() -L4 defs const w untyped int -L5 defs var x int -L5 defs var y int -L6 defs var z int -L6 uses const w untyped int -L6 uses var x int -L7 uses var x int -L7 uses var y int -L7 uses var z int` - - f, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - - // don't abort at the first error - conf := Config{Error: func(err error) { t.Log(err) }} - defs := make(map[*ast.Ident]Object) - uses := make(map[*ast.Ident]Object) - _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses}) - if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") { - t.Errorf("Check: unexpected error: %s", s) - } - - var facts []string - for id, obj := range defs { - if obj != nil { - fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj) - facts = append(facts, fact) - } - } - for id, obj := range uses { - fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj) - facts = append(facts, fact) - } - sort.Strings(facts) - - got := strings.Join(facts, "\n") - if got != want { - t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want) - } -} diff --git a/go/types/labels.go b/go/types/labels.go deleted file mode 100644 index 7364d4dbe6..0000000000 --- a/go/types/labels.go +++ /dev/null @@ -1,268 +0,0 @@ -// 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 ( - "go/ast" - "go/token" -) - -// labels checks correct label use in body. -func (check *Checker) labels(body *ast.BlockStmt) { - // set of all labels in this body - all := NewScope(nil, body.Pos(), body.End(), "label") - - fwdJumps := check.blockBranches(all, nil, nil, body.List) - - // If there are any forward jumps left, no label was found for - // the corresponding goto statements. Either those labels were - // never defined, or they are inside blocks and not reachable - // for the respective gotos. - for _, jmp := range fwdJumps { - var msg string - name := jmp.Label.Name - if alt := all.Lookup(name); alt != nil { - msg = "goto %s jumps into block" - alt.(*Label).used = true // avoid another error - } else { - msg = "label %s not declared" - } - check.errorf(jmp.Label.Pos(), msg, name) - } - - // spec: "It is illegal to define a label that is never used." - for _, obj := range all.elems { - if lbl := obj.(*Label); !lbl.used { - check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name) - } - } -} - -// A block tracks label declarations in a block and its enclosing blocks. -type block struct { - parent *block // enclosing block - lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil - labels map[string]*ast.LabeledStmt // allocated lazily -} - -// insert records a new label declaration for the current block. -// The label must not have been declared before in any block. -func (b *block) insert(s *ast.LabeledStmt) { - name := s.Label.Name - if debug { - assert(b.gotoTarget(name) == nil) - } - labels := b.labels - if labels == nil { - labels = make(map[string]*ast.LabeledStmt) - b.labels = labels - } - labels[name] = s -} - -// gotoTarget returns the labeled statement in the current -// or an enclosing block with the given label name, or nil. -func (b *block) gotoTarget(name string) *ast.LabeledStmt { - for s := b; s != nil; s = s.parent { - if t := s.labels[name]; t != nil { - return t - } - } - return nil -} - -// enclosingTarget returns the innermost enclosing labeled -// statement with the given label name, or nil. -func (b *block) enclosingTarget(name string) *ast.LabeledStmt { - for s := b; s != nil; s = s.parent { - if t := s.lstmt; t != nil && t.Label.Name == name { - return t - } - } - return nil -} - -// blockBranches processes a block's statement list and returns the set of outgoing forward jumps. -// all is the scope of all declared labels, parent the set of labels declared in the immediately -// enclosing block, and lstmt is the labeled statement this block is associated with (or nil). -func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt { - b := &block{parent: parent, lstmt: lstmt} - - var ( - varDeclPos token.Pos - fwdJumps, badJumps []*ast.BranchStmt - ) - - // All forward jumps jumping over a variable declaration are possibly - // invalid (they may still jump out of the block and be ok). - // recordVarDecl records them for the given position. - recordVarDecl := func(pos token.Pos) { - varDeclPos = pos - badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps - } - - jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool { - if varDeclPos.IsValid() { - for _, bad := range badJumps { - if jmp == bad { - return true - } - } - } - return false - } - - blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) { - // Unresolved forward jumps inside the nested block - // become forward jumps in the current block. - fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...) - } - - var stmtBranches func(ast.Stmt) - stmtBranches = func(s ast.Stmt) { - switch s := s.(type) { - case *ast.DeclStmt: - if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR { - recordVarDecl(d.Pos()) - } - - case *ast.LabeledStmt: - // declare non-blank label - if name := s.Label.Name; name != "_" { - lbl := NewLabel(s.Label.Pos(), check.pkg, name) - if alt := all.Insert(lbl); alt != nil { - check.softErrorf(lbl.pos, "label %s already declared", name) - check.reportAltDecl(alt) - // ok to continue - } else { - b.insert(s) - check.recordDef(s.Label, lbl) - } - // resolve matching forward jumps and remove them from fwdJumps - i := 0 - for _, jmp := range fwdJumps { - if jmp.Label.Name == name { - // match - lbl.used = true - check.recordUse(jmp.Label, lbl) - if jumpsOverVarDecl(jmp) { - check.softErrorf( - jmp.Label.Pos(), - "goto %s jumps over variable declaration at line %d", - name, - check.fset.Position(varDeclPos).Line, - ) - // ok to continue - } - } else { - // no match - record new forward jump - fwdJumps[i] = jmp - i++ - } - } - fwdJumps = fwdJumps[:i] - lstmt = s - } - stmtBranches(s.Stmt) - - case *ast.BranchStmt: - if s.Label == nil { - return // checked in 1st pass (check.stmt) - } - - // determine and validate target - name := s.Label.Name - switch s.Tok { - case token.BREAK: - // spec: "If there is a label, it must be that of an enclosing - // "for", "switch", or "select" statement, and that is the one - // whose execution terminates." - valid := false - if t := b.enclosingTarget(name); t != nil { - switch t.Stmt.(type) { - case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt: - valid = true - } - } - if !valid { - check.errorf(s.Label.Pos(), "invalid break label %s", name) - return - } - - case token.CONTINUE: - // spec: "If there is a label, it must be that of an enclosing - // "for" statement, and that is the one whose execution advances." - valid := false - if t := b.enclosingTarget(name); t != nil { - switch t.Stmt.(type) { - case *ast.ForStmt, *ast.RangeStmt: - valid = true - } - } - if !valid { - check.errorf(s.Label.Pos(), "invalid continue label %s", name) - return - } - - case token.GOTO: - if b.gotoTarget(name) == nil { - // label may be declared later - add branch to forward jumps - fwdJumps = append(fwdJumps, s) - return - } - - default: - check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name) - return - } - - // record label use - obj := all.Lookup(name) - obj.(*Label).used = true - check.recordUse(s.Label, obj) - - case *ast.AssignStmt: - if s.Tok == token.DEFINE { - recordVarDecl(s.Pos()) - } - - case *ast.BlockStmt: - blockBranches(lstmt, s.List) - - case *ast.IfStmt: - stmtBranches(s.Body) - if s.Else != nil { - stmtBranches(s.Else) - } - - case *ast.CaseClause: - blockBranches(nil, s.Body) - - case *ast.SwitchStmt: - stmtBranches(s.Body) - - case *ast.TypeSwitchStmt: - stmtBranches(s.Body) - - case *ast.CommClause: - blockBranches(nil, s.Body) - - case *ast.SelectStmt: - stmtBranches(s.Body) - - case *ast.ForStmt: - stmtBranches(s.Body) - - case *ast.RangeStmt: - stmtBranches(s.Body) - } - } - - for _, s := range list { - stmtBranches(s) - } - - return fwdJumps -} diff --git a/go/types/lookup.go b/go/types/lookup.go deleted file mode 100644 index 3caca5519b..0000000000 --- a/go/types/lookup.go +++ /dev/null @@ -1,341 +0,0 @@ -// 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. - -// This file implements various field and method lookup functions. - -package types - -// LookupFieldOrMethod looks up a field or method with given package and name -// in T and returns the corresponding *Var or *Func, an index sequence, and a -// bool indicating if there were any pointer indirections on the path to the -// field or method. If addressable is set, T is the type of an addressable -// variable (only matters for method lookups). -// -// The last index entry is the field or method index in the (possibly embedded) -// type where the entry was found, either: -// -// 1) the list of declared methods of a named type; or -// 2) the list of all methods (method set) of an interface type; or -// 3) the list of fields of a struct type. -// -// The earlier index entries are the indices of the anonymous struct fields -// traversed to get to the found entry, starting at depth 0. -// -// If no entry is found, a nil object is returned. In this case, the returned -// index and indirect values have the following meaning: -// -// - If index != nil, the index sequence points to an ambiguous entry -// (the same name appeared more than once at the same embedding level). -// -// - If indirect is set, a method with a pointer receiver type was found -// but there was no pointer on the path from the actual receiver type to -// the method's formal receiver base type, nor was the receiver addressable. -// -func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - // Methods cannot be associated to a named pointer type - // (spec: "The type denoted by T is called the receiver base type; - // it must not be a pointer or interface type and it must be declared - // in the same package as the method."). - // Thus, if we have a named pointer type, proceed with the underlying - // pointer type but discard the result if it is a method since we would - // not have found it for T (see also issue 8590). - if t, _ := T.(*Named); t != nil { - if p, _ := t.underlying.(*Pointer); p != nil { - obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name) - if _, ok := obj.(*Func); ok { - return nil, nil, false - } - return - } - } - - return lookupFieldOrMethod(T, addressable, pkg, name) -} - -// TODO(gri) The named type consolidation and seen maps below must be -// indexed by unique keys for a given type. Verify that named -// types always have only one representation (even when imported -// indirectly via different packages.) - -func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and NewMethodSet should be kept in sync. - - if name == "_" { - return // blank fields/methods are never found - } - - typ, isPtr := deref(T) - named, _ := typ.(*Named) - - // *typ where typ is an interface has no methods. - if isPtr { - utyp := typ - if named != nil { - utyp = named.underlying - } - if _, ok := utyp.(*Interface); ok { - return - } - } - - // Start with typ as single entry at shallowest depth. - // If typ is not a named type, insert a nil type instead. - current := []embeddedType{{named, nil, isPtr, false}} - - // named types that we have seen already, allocated lazily - var seen map[*Named]bool - - // search current depth - for len(current) > 0 { - var next []embeddedType // embedded types found at current depth - - // look for (pkg, name) in all types at current depth - for _, e := range current { - // The very first time only, e.typ may be nil. - // In this case, we don't have a named type and - // we simply continue with the underlying type. - if e.typ != nil { - if seen[e.typ] { - // We have seen this type before, at a more shallow depth - // (note that multiples of this type at the current depth - // were consolidated before). The type at that depth shadows - // this same type at the current depth, so we can ignore - // this one. - continue - } - if seen == nil { - seen = make(map[*Named]bool) - } - seen[e.typ] = true - - // look for a matching attached method - if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil { - // potential match - assert(m.typ != nil) - index = concat(e.index, i) - if obj != nil || e.multiples { - return nil, index, false // collision - } - obj = m - indirect = e.indirect - continue // we can't have a matching field or interface method - } - - // continue with underlying type - typ = e.typ.underlying - } - - switch t := typ.(type) { - case *Struct: - // look for a matching field and collect embedded types - for i, f := range t.fields { - if f.sameId(pkg, name) { - assert(f.typ != nil) - index = concat(e.index, i) - if obj != nil || e.multiples { - return nil, index, false // collision - } - obj = f - indirect = e.indirect - continue // we can't have a matching interface method - } - // Collect embedded struct fields for searching the next - // lower depth, but only if we have not seen a match yet - // (if we have a match it is either the desired field or - // we have a name collision on the same depth; in either - // case we don't need to look further). - // Embedded fields are always of the form T or *T where - // T is a named type. If e.typ appeared multiple times at - // this depth, f.typ appears multiple times at the next - // depth. - if obj == nil && f.anonymous { - // Ignore embedded basic types - only user-defined - // named types can have methods or struct fields. - typ, isPtr := deref(f.typ) - if t, _ := typ.(*Named); t != nil { - next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) - } - } - } - - case *Interface: - // look for a matching method - // TODO(gri) t.allMethods is sorted - use binary search - if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { - assert(m.typ != nil) - index = concat(e.index, i) - if obj != nil || e.multiples { - return nil, index, false // collision - } - obj = m - indirect = e.indirect - } - } - } - - if obj != nil { - // found a potential match - // spec: "A method call x.m() is valid if the method set of (the type of) x - // contains m and the argument list can be assigned to the parameter - // list of m. If x is addressable and &x's method set contains m, x.m() - // is shorthand for (&x).m()". - if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable { - return nil, nil, true // pointer/addressable receiver required - } - return - } - - current = consolidateMultiples(next) - } - - return nil, nil, false // not found -} - -// embeddedType represents an embedded named type -type embeddedType struct { - typ *Named // nil means use the outer typ variable instead - index []int // embedded field indices, starting with index at depth 0 - indirect bool // if set, there was a pointer indirection on the path to this field - multiples bool // if set, typ appears multiple times at this depth -} - -// consolidateMultiples collects multiple list entries with the same type -// into a single entry marked as containing multiples. The result is the -// consolidated list. -func consolidateMultiples(list []embeddedType) []embeddedType { - if len(list) <= 1 { - return list // at most one entry - nothing to do - } - - n := 0 // number of entries w/ unique type - prev := make(map[*Named]int) // index at which type was previously seen - for _, e := range list { - if i, found := prev[e.typ]; found { - list[i].multiples = true - // ignore this entry - } else { - prev[e.typ] = n - list[n] = e - n++ - } - } - return list[:n] -} - -// MissingMethod returns (nil, false) if V implements T, otherwise it -// returns a missing method required by T and whether it is missing or -// just has the wrong type. -// -// For non-interface types V, or if static is set, V implements T if all -// methods of T are present in V. Otherwise (V is an interface and static -// is not set), MissingMethod only checks that methods of T which are also -// present in V have matching types (e.g., for a type assertion x.(T) where -// x is of interface type V). -// -func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { - // fast path for common case - if T.Empty() { - return - } - - // TODO(gri) Consider using method sets here. Might be more efficient. - - if ityp, _ := V.Underlying().(*Interface); ityp != nil { - // TODO(gri) allMethods is sorted - can do this more efficiently - for _, m := range T.allMethods { - _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name) - switch { - case obj == nil: - if static { - return m, false - } - case !Identical(obj.Type(), m.typ): - return m, true - } - } - return - } - - // A concrete type implements T if it implements all methods of T. - for _, m := range T.allMethods { - obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) - - f, _ := obj.(*Func) - if f == nil { - return m, false - } - - if !Identical(f.typ, m.typ) { - return m, true - } - } - - return -} - -// assertableTo reports whether a value of type V can be asserted to have type T. -// It returns (nil, false) as affirmative answer. Otherwise it returns a missing -// method required by V and whether it is missing or just has the wrong type. -func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) { - // no static check is required if T is an interface - // spec: "If T is an interface type, x.(T) asserts that the - // dynamic type of x implements the interface T." - if _, ok := T.Underlying().(*Interface); ok && !strict { - return - } - return MissingMethod(T, V, false) -} - -// deref dereferences typ if it is a *Pointer and returns its base and true. -// Otherwise it returns (typ, false). -func deref(typ Type) (Type, bool) { - if p, _ := typ.(*Pointer); p != nil { - return p.base, true - } - return typ, false -} - -// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a -// (named or unnamed) struct and returns its base. Otherwise it returns typ. -func derefStructPtr(typ Type) Type { - if p, _ := typ.Underlying().(*Pointer); p != nil { - if _, ok := p.base.Underlying().(*Struct); ok { - return p.base - } - } - return typ -} - -// concat returns the result of concatenating list and i. -// The result does not share its underlying array with list. -func concat(list []int, i int) []int { - var t []int - t = append(t, list...) - return append(t, i) -} - -// fieldIndex returns the index for the field with matching package and name, or a value < 0. -func fieldIndex(fields []*Var, pkg *Package, name string) int { - if name != "_" { - for i, f := range fields { - if f.sameId(pkg, name) { - return i - } - } - } - return -1 -} - -// lookupMethod returns the index of and method with matching package and name, or (-1, nil). -func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { - if name != "_" { - for i, m := range methods { - if m.sameId(pkg, name) { - return i, m - } - } - } - return -1, nil -} diff --git a/go/types/methodset.go b/go/types/methodset.go deleted file mode 100644 index 8aff6f9ba4..0000000000 --- a/go/types/methodset.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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. - -// This file implements method sets. - -package types - -import ( - "bytes" - "fmt" - "sort" -) - -// A MethodSet is an ordered set of concrete or abstract (interface) methods; -// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id(). -// The zero value for a MethodSet is a ready-to-use empty method set. -type MethodSet struct { - list []*Selection -} - -func (s *MethodSet) String() string { - if s.Len() == 0 { - return "MethodSet {}" - } - - var buf bytes.Buffer - fmt.Fprintln(&buf, "MethodSet {") - for _, f := range s.list { - fmt.Fprintf(&buf, "\t%s\n", f) - } - fmt.Fprintln(&buf, "}") - return buf.String() -} - -// Len returns the number of methods in s. -func (s *MethodSet) Len() int { return len(s.list) } - -// At returns the i'th method in s for 0 <= i < s.Len(). -func (s *MethodSet) At(i int) *Selection { return s.list[i] } - -// Lookup returns the method with matching package and name, or nil if not found. -func (s *MethodSet) Lookup(pkg *Package, name string) *Selection { - if s.Len() == 0 { - return nil - } - - key := Id(pkg, name) - i := sort.Search(len(s.list), func(i int) bool { - m := s.list[i] - return m.obj.Id() >= key - }) - if i < len(s.list) { - m := s.list[i] - if m.obj.Id() == key { - return m - } - } - return nil -} - -// Shared empty method set. -var emptyMethodSet MethodSet - -// NewMethodSet returns the method set for the given type T. It -// always returns a non-nil method set, even if it is empty. -// -// A MethodSetCache handles repeat queries more efficiently. -// -func NewMethodSet(T Type) *MethodSet { - // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and lookupFieldOrMethod should be kept in sync. - - // method set up to the current depth, allocated lazily - var base methodSet - - typ, isPtr := deref(T) - named, _ := typ.(*Named) - - // *typ where typ is an interface has no methods. - if isPtr { - utyp := typ - if named != nil { - utyp = named.underlying - } - if _, ok := utyp.(*Interface); ok { - return &emptyMethodSet - } - } - - // Start with typ as single entry at shallowest depth. - // If typ is not a named type, insert a nil type instead. - current := []embeddedType{{named, nil, isPtr, false}} - - // named types that we have seen already, allocated lazily - var seen map[*Named]bool - - // collect methods at current depth - for len(current) > 0 { - var next []embeddedType // embedded types found at current depth - - // field and method sets at current depth, allocated lazily - var fset fieldSet - var mset methodSet - - for _, e := range current { - // The very first time only, e.typ may be nil. - // In this case, we don't have a named type and - // we simply continue with the underlying type. - if e.typ != nil { - if seen[e.typ] { - // We have seen this type before, at a more shallow depth - // (note that multiples of this type at the current depth - // were consolidated before). The type at that depth shadows - // this same type at the current depth, so we can ignore - // this one. - continue - } - if seen == nil { - seen = make(map[*Named]bool) - } - seen[e.typ] = true - - mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples) - - // continue with underlying type - typ = e.typ.underlying - } - - switch t := typ.(type) { - case *Struct: - for i, f := range t.fields { - fset = fset.add(f, e.multiples) - - // Embedded fields are always of the form T or *T where - // T is a named type. If typ appeared multiple times at - // this depth, f.Type appears multiple times at the next - // depth. - if f.anonymous { - // Ignore embedded basic types - only user-defined - // named types can have methods or struct fields. - typ, isPtr := deref(f.typ) - if t, _ := typ.(*Named); t != nil { - next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) - } - } - } - - case *Interface: - mset = mset.add(t.allMethods, e.index, true, e.multiples) - } - } - - // Add methods and collisions at this depth to base if no entries with matching - // names exist already. - for k, m := range mset { - if _, found := base[k]; !found { - // Fields collide with methods of the same name at this depth. - if _, found := fset[k]; found { - m = nil // collision - } - if base == nil { - base = make(methodSet) - } - base[k] = m - } - } - - // Multiple fields with matching names collide at this depth and shadow all - // entries further down; add them as collisions to base if no entries with - // matching names exist already. - for k, f := range fset { - if f == nil { - if _, found := base[k]; !found { - if base == nil { - base = make(methodSet) - } - base[k] = nil // collision - } - } - } - - current = consolidateMultiples(next) - } - - if len(base) == 0 { - return &emptyMethodSet - } - - // collect methods - var list []*Selection - for _, m := range base { - if m != nil { - m.recv = T - list = append(list, m) - } - } - sort.Sort(byUniqueName(list)) - return &MethodSet{list} -} - -// A fieldSet is a set of fields and name collisions. -// A collision indicates that multiple fields with the -// same unique id appeared. -type fieldSet map[string]*Var // a nil entry indicates a name collision - -// Add adds field f to the field set s. -// If multiples is set, f appears multiple times -// and is treated as a collision. -func (s fieldSet) add(f *Var, multiples bool) fieldSet { - if s == nil { - s = make(fieldSet) - } - key := f.Id() - // if f is not in the set, add it - if !multiples { - if _, found := s[key]; !found { - s[key] = f - return s - } - } - s[key] = nil // collision - return s -} - -// A methodSet is a set of methods and name collisions. -// A collision indicates that multiple methods with the -// same unique id appeared. -type methodSet map[string]*Selection // a nil entry indicates a name collision - -// Add adds all functions in list to the method set s. -// If multiples is set, every function in list appears multiple times -// and is treated as a collision. -func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet { - if len(list) == 0 { - return s - } - if s == nil { - s = make(methodSet) - } - for i, f := range list { - key := f.Id() - // if f is not in the set, add it - if !multiples { - // TODO(gri) A found method may not be added because it's not in the method set - // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method - // set and may not collide with the first one, thus leading to a false positive. - // Is that possible? Investigate. - if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { - s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} - continue - } - } - s[key] = nil // collision - } - return s -} - -// ptrRecv reports whether the receiver is of the form *T. -// The receiver must exist. -func ptrRecv(f *Func) bool { - _, isPtr := deref(f.typ.(*Signature).recv.typ) - return isPtr -} - -// byUniqueName function lists can be sorted by their unique names. -type byUniqueName []*Selection - -func (a byUniqueName) Len() int { return len(a) } -func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } -func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/go/types/object.go b/go/types/object.go deleted file mode 100644 index a9b6c43f5a..0000000000 --- a/go/types/object.go +++ /dev/null @@ -1,361 +0,0 @@ -// 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" - "go/token" - - "golang.org/x/tools/go/exact" -) - -// TODO(gri) Document factory, accessor methods, and fields. General clean-up. - -// An Object describes a named language entity such as a package, -// constant, type, variable, function (incl. methods), or label. -// All objects implement the Object interface. -// -type Object interface { - Parent() *Scope // scope in which this object is declared - Pos() token.Pos // position of object identifier in declaration - Pkg() *Package // nil for objects in the Universe scope and labels - Name() string // package local object name - Type() Type // object type - Exported() bool // reports whether the name starts with a capital letter - Id() string // object id (see Id below) - - // String returns a human-readable string of the object. - String() string - - // order reflects a package-level object's source order: if object - // a is before object b in the source, then a.order() < b.order(). - // order returns a value > 0 for package-level objects; it returns - // 0 for all other objects (including objects in file scopes). - order() uint32 - - // setOrder sets the order number of the object. It must be > 0. - setOrder(uint32) - - // setParent sets the parent scope of the object. - setParent(*Scope) - - // sameId reports whether obj.Id() and Id(pkg, name) are the same. - sameId(pkg *Package, name string) bool - - // scopePos returns the start position of the scope of this Object - scopePos() token.Pos - - // setScopePos sets the start position of the scope for this Object. - setScopePos(pos token.Pos) -} - -// Id returns name if it is exported, otherwise it -// returns the name qualified with the package path. -func Id(pkg *Package, name string) string { - if ast.IsExported(name) { - return name - } - // unexported names need the package path for differentiation - // (if there's no package, make sure we don't start with '.' - // as that may change the order of methods between a setup - // inside a package and outside a package - which breaks some - // tests) - path := "_" - // TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition? - // if pkg == nil { - // panic("nil package in lookup of unexported name") - // } - if pkg != nil { - path = pkg.path - if path == "" { - path = "_" - } - } - return path + "." + name -} - -// An object implements the common parts of an Object. -type object struct { - parent *Scope - pos token.Pos - pkg *Package - name string - typ Type - order_ uint32 - scopePos_ token.Pos -} - -func (obj *object) Parent() *Scope { return obj.parent } -func (obj *object) Pos() token.Pos { return obj.pos } -func (obj *object) Pkg() *Package { return obj.pkg } -func (obj *object) Name() string { return obj.name } -func (obj *object) Type() Type { return obj.typ } -func (obj *object) Exported() bool { return ast.IsExported(obj.name) } -func (obj *object) Id() string { return Id(obj.pkg, obj.name) } -func (obj *object) String() string { panic("abstract") } -func (obj *object) order() uint32 { return obj.order_ } -func (obj *object) scopePos() token.Pos { return obj.scopePos_ } - -func (obj *object) setParent(parent *Scope) { obj.parent = parent } -func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order } -func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos } - -func (obj *object) sameId(pkg *Package, name string) bool { - // spec: - // "Two identifiers are different if they are spelled differently, - // or if they appear in different packages and are not exported. - // Otherwise, they are the same." - if name != obj.name { - return false - } - // obj.Name == name - if obj.Exported() { - return true - } - // not exported, so packages must be the same (pkg == nil for - // fields in Universe scope; this can only happen for types - // introduced via Eval) - if pkg == nil || obj.pkg == nil { - return pkg == obj.pkg - } - // pkg != nil && obj.pkg != nil - return pkg.path == obj.pkg.path -} - -// A PkgName represents an imported Go package. -type PkgName struct { - object - imported *Package - used bool // set if the package was used -} - -func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName { - return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, token.NoPos}, imported, false} -} - -// Imported returns the package that was imported. -// It is distinct from Pkg(), which is the package containing the import statement. -func (obj *PkgName) Imported() *Package { return obj.imported } - -// A Const represents a declared constant. -type Const struct { - object - val exact.Value - visited bool // for initialization cycle detection -} - -func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const { - return &Const{object{nil, pos, pkg, name, typ, 0, token.NoPos}, val, false} -} - -func (obj *Const) Val() exact.Value { return obj.val } - -// A TypeName represents a declared type. -type TypeName struct { - object -} - -func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { - return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}} -} - -// A Variable represents a declared variable (including function parameters and results, and struct fields). -type Var struct { - object - anonymous bool // if set, the variable is an anonymous struct field, and name is the type name - visited bool // for initialization cycle detection - isField bool // var is struct field - used bool // set if the variable was used -} - -func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}} -} - -func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, used: true} // parameters are always 'used' -} - -func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { - return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, anonymous: anonymous, isField: true} -} - -func (obj *Var) Anonymous() bool { return obj.anonymous } - -func (obj *Var) IsField() bool { return obj.isField } - -// A Func represents a declared function, concrete method, or abstract -// (interface) method. Its Type() is always a *Signature. -// An abstract method may belong to many interfaces due to embedding. -type Func struct { - object -} - -func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { - // don't store a nil signature - var typ Type - if sig != nil { - typ = sig - } - return &Func{object{nil, pos, pkg, name, typ, 0, token.NoPos}} -} - -// FullName returns the package- or receiver-type-qualified name of -// function or method obj. -func (obj *Func) FullName() string { - var buf bytes.Buffer - writeFuncName(&buf, obj, nil) - return buf.String() -} - -func (obj *Func) Scope() *Scope { - return obj.typ.(*Signature).scope -} - -// A Label represents a declared label. -type Label struct { - object - used bool // set if the label was used -} - -func NewLabel(pos token.Pos, pkg *Package, name string) *Label { - return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false} -} - -// A Builtin represents a built-in function. -// Builtins don't have a valid type. -type Builtin struct { - object - id builtinId -} - -func newBuiltin(id builtinId) *Builtin { - return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id} -} - -// Nil represents the predeclared value nil. -type Nil struct { - object -} - -func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { - typ := obj.Type() - switch obj := obj.(type) { - case *PkgName: - fmt.Fprintf(buf, "package %s", obj.Name()) - if path := obj.imported.path; path != "" && path != obj.name { - fmt.Fprintf(buf, " (%q)", path) - } - return - - case *Const: - buf.WriteString("const") - - case *TypeName: - buf.WriteString("type") - typ = typ.Underlying() - - case *Var: - if obj.isField { - buf.WriteString("field") - } else { - buf.WriteString("var") - } - - case *Func: - buf.WriteString("func ") - writeFuncName(buf, obj, qf) - if typ != nil { - WriteSignature(buf, typ.(*Signature), qf) - } - return - - case *Label: - buf.WriteString("label") - typ = nil - - case *Builtin: - buf.WriteString("builtin") - typ = nil - - case *Nil: - buf.WriteString("nil") - return - - default: - panic(fmt.Sprintf("writeObject(%T)", obj)) - } - - buf.WriteByte(' ') - - // For package-level objects, qualify the name. - if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj { - writePackage(buf, obj.Pkg(), qf) - } - buf.WriteString(obj.Name()) - if typ != nil { - buf.WriteByte(' ') - WriteType(buf, typ, qf) - } -} - -func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) { - if pkg == nil { - return - } - var s string - if qf != nil { - s = qf(pkg) - } else { - s = pkg.Path() - } - if s != "" { - buf.WriteString(s) - buf.WriteByte('.') - } -} - -// ObjectString returns the string form of obj. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func ObjectString(obj Object, qf Qualifier) string { - var buf bytes.Buffer - writeObject(&buf, obj, qf) - return buf.String() -} - -func (obj *PkgName) String() string { return ObjectString(obj, nil) } -func (obj *Const) String() string { return ObjectString(obj, nil) } -func (obj *TypeName) String() string { return ObjectString(obj, nil) } -func (obj *Var) String() string { return ObjectString(obj, nil) } -func (obj *Func) String() string { return ObjectString(obj, nil) } -func (obj *Label) String() string { return ObjectString(obj, nil) } -func (obj *Builtin) String() string { return ObjectString(obj, nil) } -func (obj *Nil) String() string { return ObjectString(obj, nil) } - -func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) { - if f.typ != nil { - sig := f.typ.(*Signature) - if recv := sig.Recv(); recv != nil { - buf.WriteByte('(') - if _, ok := recv.Type().(*Interface); ok { - // gcimporter creates abstract methods of - // named interfaces using the interface type - // (not the named type) as the receiver. - // Don't print it in full. - buf.WriteString("interface") - } else { - WriteType(buf, recv.Type(), qf) - } - buf.WriteByte(')') - buf.WriteByte('.') - } else if f.pkg != nil { - writePackage(buf, f.pkg, qf) - } - } - buf.WriteString(f.name) -} diff --git a/go/types/objset.go b/go/types/objset.go deleted file mode 100644 index 55eb74addb..0000000000 --- a/go/types/objset.go +++ /dev/null @@ -1,31 +0,0 @@ -// 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. - -// This file implements objsets. -// -// An objset is similar to a Scope but objset elements -// are identified by their unique id, instead of their -// object name. - -package types - -// An objset is a set of objects identified by their unique id. -// The zero value for objset is a ready-to-use empty objset. -type objset map[string]Object // initialized lazily - -// insert attempts to insert an object obj into objset s. -// If s already contains an alternative object alt with -// the same name, insert leaves s unchanged and returns alt. -// Otherwise it inserts obj and returns nil. -func (s *objset) insert(obj Object) Object { - id := obj.Id() - if alt := (*s)[id]; alt != nil { - return alt - } - if *s == nil { - *s = make(map[string]Object) - } - (*s)[id] = obj - return nil -} diff --git a/go/types/operand.go b/go/types/operand.go deleted file mode 100644 index d52b30e161..0000000000 --- a/go/types/operand.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2012 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. - -// This file defines operands and associated operations. - -package types - -import ( - "bytes" - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -// An operandMode specifies the (addressing) mode of an operand. -type operandMode byte - -const ( - invalid operandMode = iota // operand is invalid - novalue // operand represents no value (result of a function call w/o result) - builtin // operand is a built-in function - typexpr // operand is a type - constant // operand is a constant; the operand's typ is a Basic type - variable // operand is an addressable variable - mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) - value // operand is a computed value - commaok // like value, but operand may be used in a comma,ok expression -) - -var operandModeString = [...]string{ - invalid: "invalid operand", - novalue: "no value", - builtin: "built-in", - typexpr: "type", - constant: "constant", - variable: "variable", - mapindex: "map index expression", - value: "value", - commaok: "comma, ok expression", -} - -// An operand represents an intermediate value during type checking. -// Operands have an (addressing) mode, the expression evaluating to -// the operand, the operand's type, a value for constants, and an id -// for built-in functions. -// The zero value of operand is a ready to use invalid operand. -// -type operand struct { - mode operandMode - expr ast.Expr - typ Type - val exact.Value - id builtinId -} - -// pos returns the position of the expression corresponding to x. -// If x is invalid the position is token.NoPos. -// -func (x *operand) pos() token.Pos { - // x.expr may not be set if x is invalid - if x.expr == nil { - return token.NoPos - } - return x.expr.Pos() -} - -// Operand string formats -// (not all "untyped" cases can appear due to the type system, -// but they fall out naturally here) -// -// mode format -// -// invalid ( ) -// novalue ( ) -// builtin ( ) -// typexpr ( ) -// -// constant ( ) -// constant ( of type ) -// constant ( ) -// constant ( of type ) -// -// variable ( ) -// variable ( of type ) -// -// mapindex ( ) -// mapindex ( of type ) -// -// value ( ) -// value ( of type ) -// -// commaok ( ) -// commaok ( of type ) -// -func operandString(x *operand, qf Qualifier) string { - var buf bytes.Buffer - - var expr string - if x.expr != nil { - expr = ExprString(x.expr) - } else { - switch x.mode { - case builtin: - expr = predeclaredFuncs[x.id].name - case typexpr: - expr = TypeString(x.typ, qf) - case constant: - expr = x.val.String() - } - } - - // ( - if expr != "" { - buf.WriteString(expr) - buf.WriteString(" (") - } - - // - hasType := false - switch x.mode { - case invalid, novalue, builtin, typexpr: - // no type - default: - // has type - if isUntyped(x.typ) { - buf.WriteString(x.typ.(*Basic).name) - buf.WriteByte(' ') - break - } - hasType = true - } - - // - buf.WriteString(operandModeString[x.mode]) - - // - if x.mode == constant { - if s := x.val.String(); s != expr { - buf.WriteByte(' ') - buf.WriteString(s) - } - } - - // - if hasType { - if x.typ != Typ[Invalid] { - buf.WriteString(" of type ") - WriteType(&buf, x.typ, qf) - } else { - buf.WriteString(" with invalid type") - } - } - - // ) - if expr != "" { - buf.WriteByte(')') - } - - return buf.String() -} - -func (x *operand) String() string { - return operandString(x, nil) -} - -// setConst sets x to the untyped constant for literal lit. -func (x *operand) setConst(tok token.Token, lit string) { - val := exact.MakeFromLiteral(lit, tok) - if val == nil { - // TODO(gri) Should we make it an unknown constant instead? - x.mode = invalid - return - } - - var kind BasicKind - switch tok { - case token.INT: - kind = UntypedInt - case token.FLOAT: - kind = UntypedFloat - case token.IMAG: - kind = UntypedComplex - case token.CHAR: - kind = UntypedRune - case token.STRING: - kind = UntypedString - } - - x.mode = constant - x.typ = Typ[kind] - x.val = val -} - -// isNil reports whether x is the nil value. -func (x *operand) isNil() bool { - return x.mode == value && x.typ == Typ[UntypedNil] -} - -// TODO(gri) The functions operand.assignableTo, checker.convertUntyped, -// checker.representable, and checker.assignment are -// overlapping in functionality. Need to simplify and clean up. - -// assignableTo reports whether x is assignable to a variable of type T. -func (x *operand) assignableTo(conf *Config, T Type) bool { - if x.mode == invalid || T == Typ[Invalid] { - return true // avoid spurious errors - } - - V := x.typ - - // x's type is identical to T - if Identical(V, T) { - return true - } - - Vu := V.Underlying() - Tu := T.Underlying() - - // T is an interface type and x implements T - // (Do this check first as it might succeed early.) - if Ti, ok := Tu.(*Interface); ok { - if Implements(x.typ, Ti) { - return true - } - } - - // x's type V and T have identical underlying types - // and at least one of V or T is not a named type - if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) { - return true - } - - // 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 == SendRecv { - if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) { - return !isNamed(V) || !isNamed(T) - } - } - - // x is the predeclared identifier nil and T is a pointer, - // function, slice, map, channel, or interface type - if x.isNil() { - switch t := Tu.(type) { - case *Basic: - if t.kind == UnsafePointer { - return true - } - case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface: - return true - } - return false - } - - // x is an untyped constant representable by a value of type T - // TODO(gri) This is borrowing from checker.convertUntyped and - // checker.representable. Need to clean up. - if isUntyped(Vu) { - switch t := Tu.(type) { - case *Basic: - if x.mode == constant { - return representableConst(x.val, conf, t.kind, nil) - } - // 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) - } - case *Interface: - return x.isNil() || t.Empty() - case *Pointer, *Signature, *Slice, *Map, *Chan: - return x.isNil() - } - } - - return false -} - -// isInteger reports whether x is a value of integer type -// or an untyped constant representable as an integer. -func (x *operand) isInteger() bool { - return x.mode == invalid || - isInteger(x.typ) || - isUntyped(x.typ) && x.mode == constant && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt -} diff --git a/go/types/ordering.go b/go/types/ordering.go deleted file mode 100644 index 6bb98f2dc1..0000000000 --- a/go/types/ordering.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2014 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. - -// This file implements resolveOrder. - -package types - -import ( - "go/ast" - "sort" -) - -// resolveOrder computes the order in which package-level objects -// must be type-checked. -// -// Interface types appear first in the list, sorted topologically -// by dependencies on embedded interfaces that are also declared -// in this package, followed by all other objects sorted in source -// order. -// -// TODO(gri) Consider sorting all types by dependencies here, and -// in the process check _and_ report type cycles. This may simplify -// the full type-checking phase. -// -func (check *Checker) resolveOrder() []Object { - var ifaces, others []Object - - // collect interface types with their dependencies, and all other objects - for obj := range check.objMap { - if ityp := check.interfaceFor(obj); ityp != nil { - ifaces = append(ifaces, obj) - // determine dependencies on embedded interfaces - for _, f := range ityp.Methods.List { - if len(f.Names) == 0 { - // Embedded interface: The type must be a (possibly - // qualified) identifier denoting another interface. - // Imported interfaces are already fully resolved, - // so we can ignore qualified identifiers. - if ident, _ := f.Type.(*ast.Ident); ident != nil { - embedded := check.pkg.scope.Lookup(ident.Name) - if check.interfaceFor(embedded) != nil { - check.objMap[obj].addDep(embedded) - } - } - } - } - } else { - others = append(others, obj) - } - } - - // final object order - var order []Object - - // sort interface types topologically by dependencies, - // and in source order if there are no dependencies - sort.Sort(inSourceOrder(ifaces)) - if debug { - for _, obj := range ifaces { - assert(check.objMap[obj].mark == 0) - } - } - for _, obj := range ifaces { - check.appendInPostOrder(&order, obj) - } - - // sort everything else in source order - sort.Sort(inSourceOrder(others)) - - return append(order, others...) -} - -// interfaceFor returns the AST interface denoted by obj, or nil. -func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType { - tname, _ := obj.(*TypeName) - if tname == nil { - return nil // not a type - } - d := check.objMap[obj] - if d == nil { - check.dump("%s: %s should have been declared", obj.Pos(), obj.Name()) - unreachable() - } - if d.typ == nil { - return nil // invalid AST - ignore (will be handled later) - } - ityp, _ := d.typ.(*ast.InterfaceType) - return ityp -} - -func (check *Checker) appendInPostOrder(order *[]Object, obj Object) { - d := check.objMap[obj] - if d.mark != 0 { - // We've already seen this object; either because it's - // already added to order, or because we have a cycle. - // In both cases we stop. Cycle errors are reported - // when type-checking types. - return - } - d.mark = 1 - - for _, obj := range orderedSetObjects(d.deps) { - check.appendInPostOrder(order, obj) - } - - *order = append(*order, obj) -} - -func orderedSetObjects(set map[Object]bool) []Object { - list := make([]Object, len(set)) - i := 0 - for obj := range set { - // we don't care about the map element value - list[i] = obj - i++ - } - sort.Sort(inSourceOrder(list)) - return list -} - -// inSourceOrder implements the sort.Sort interface. -type inSourceOrder []Object - -func (a inSourceOrder) Len() int { return len(a) } -func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() } -func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/go/types/package.go b/go/types/package.go deleted file mode 100644 index 48fe8398fe..0000000000 --- a/go/types/package.go +++ /dev/null @@ -1,65 +0,0 @@ -// 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 ( - "fmt" - "go/token" -) - -// A Package describes a Go package. -type Package struct { - path string - name string - scope *Scope - complete bool - imports []*Package - fake bool // scope lookup errors are silently dropped if package is fake (internal use only) -} - -// NewPackage returns a new Package for the given package path and name; -// the name must not be the blank identifier. -// The package is not complete and contains no explicit imports. -func NewPackage(path, name string) *Package { - if name == "_" { - panic("invalid package name _") - } - scope := NewScope(Universe, token.NoPos, token.NoPos, fmt.Sprintf("package %q", path)) - return &Package{path: path, name: name, scope: scope} -} - -// Path returns the package path. -func (pkg *Package) Path() string { return pkg.path } - -// Name returns the package name. -func (pkg *Package) Name() string { return pkg.name } - -// Scope returns the (complete or incomplete) package scope -// holding the objects declared at package level (TypeNames, -// Consts, Vars, and Funcs). -func (pkg *Package) Scope() *Scope { return pkg.scope } - -// A package is complete if its scope contains (at least) all -// exported objects; otherwise it is incomplete. -func (pkg *Package) Complete() bool { return pkg.complete } - -// MarkComplete marks a package as complete. -func (pkg *Package) MarkComplete() { pkg.complete = true } - -// Imports returns the list of packages directly imported by -// pkg; the list is in source order. Package unsafe is excluded. -// -// If pkg was loaded from export data, Imports includes packages that -// provide package-level objects referenced by pkg. This may be more or -// less than the set of packages directly imported by pkg's source code. -func (pkg *Package) Imports() []*Package { return pkg.imports } - -// SetImports sets the list of explicitly imported packages to list. -// It is the caller's responsibility to make sure list elements are unique. -func (pkg *Package) SetImports(list []*Package) { pkg.imports = list } - -func (pkg *Package) String() string { - return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path) -} diff --git a/go/types/predicates.go b/go/types/predicates.go deleted file mode 100644 index 993c6d290b..0000000000 --- a/go/types/predicates.go +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2012 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. - -// This file implements commonly used type predicates. - -package types - -import "sort" - -func isNamed(typ Type) bool { - if _, ok := typ.(*Basic); ok { - return ok - } - _, ok := typ.(*Named) - return ok -} - -func isBoolean(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsBoolean != 0 -} - -func isInteger(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsInteger != 0 -} - -func isUnsigned(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsUnsigned != 0 -} - -func isFloat(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsFloat != 0 -} - -func isComplex(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsComplex != 0 -} - -func isNumeric(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsNumeric != 0 -} - -func isString(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsString != 0 -} - -func isTyped(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return !ok || t.info&IsUntyped == 0 -} - -func isUntyped(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsUntyped != 0 -} - -func isOrdered(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsOrdered != 0 -} - -func isConstType(typ Type) bool { - t, ok := typ.Underlying().(*Basic) - return ok && t.info&IsConstType != 0 -} - -// IsInterface reports whether typ is an interface type. -func IsInterface(typ Type) bool { - _, ok := typ.Underlying().(*Interface) - return ok -} - -// Comparable reports whether values of type T are comparable. -func Comparable(T Type) bool { - switch t := T.Underlying().(type) { - case *Basic: - // assume invalid types to be comparable - // to avoid follow-up errors - return t.kind != UntypedNil - case *Pointer, *Interface, *Chan: - return true - case *Struct: - for _, f := range t.fields { - if !Comparable(f.typ) { - return false - } - } - return true - case *Array: - return Comparable(t.elem) - } - return false -} - -// hasNil reports whether a type includes the nil value. -func hasNil(typ Type) bool { - switch t := typ.Underlying().(type) { - case *Basic: - return t.kind == UnsafePointer - case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: - return true - } - return false -} - -// Identical reports whether x and y are identical. -func Identical(x, y Type) bool { - return identical(x, y, nil) -} - -// An ifacePair is a node in a stack of interface type pairs compared for identity. -type ifacePair struct { - x, y *Interface - prev *ifacePair -} - -func (p *ifacePair) identical(q *ifacePair) bool { - return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x -} - -func identical(x, y Type, p *ifacePair) bool { - if x == y { - return true - } - - switch x := x.(type) { - case *Basic: - // Basic types are singletons except for the rune and byte - // aliases, thus we cannot solely rely on the x == y check - // above. - if y, ok := y.(*Basic); ok { - 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 && identical(x.elem, y.elem, p) - } - - case *Slice: - // Two slice types are identical if they have identical element types. - if y, ok := y.(*Slice); ok { - return identical(x.elem, y.elem, p) - } - - case *Struct: - // Two struct types are identical if they have the same sequence of fields, - // and if corresponding fields have the same names, and identical types, - // 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 x.NumFields() == y.NumFields() { - for i, f := range x.fields { - g := y.fields[i] - if f.anonymous != g.anonymous || - x.Tag(i) != y.Tag(i) || - !f.sameId(g.pkg, g.name) || - !identical(f.typ, g.typ, p) { - return false - } - } - return true - } - } - - case *Pointer: - // Two pointer types are identical if they have identical base types. - if y, ok := y.(*Pointer); ok { - return identical(x.base, y.base, p) - } - - case *Tuple: - // Two tuples types are identical if they have the same number of elements - // and corresponding elements have identical types. - if y, ok := y.(*Tuple); ok { - if x.Len() == y.Len() { - if x != nil { - for i, v := range x.vars { - w := y.vars[i] - if !identical(v.typ, w.typ, p) { - return false - } - } - } - return true - } - } - - case *Signature: - // Two function types are identical if they have the same number of parameters - // and result values, corresponding parameter and result types are identical, - // 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 x.variadic == y.variadic && - identical(x.params, y.params, p) && - identical(x.results, y.results, p) - } - - case *Interface: - // Two interface types are identical if they have the same set of methods with - // 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 { - a := x.allMethods - b := y.allMethods - if len(a) == len(b) { - // Interface types are the only types where cycles can occur - // that are not "terminated" via named types; and such cycles - // can only be created via method parameter types that are - // anonymous interfaces (directly or indirectly) embedding - // the current interface. Example: - // - // type T interface { - // m() interface{T} - // } - // - // If two such (differently named) interfaces are compared, - // endless recursion occurs if the cycle is not detected. - // - // If x and y were compared before, they must be equal - // (if they were not, the recursion would have stopped); - // search the ifacePair stack for the same pair. - // - // This is a quadratic algorithm, but in practice these stacks - // are extremely short (bounded by the nesting depth of interface - // type declarations that recur via parameter types, an extremely - // rare occurrence). An alternative implementation might use a - // "visited" map, but that is probably less efficient overall. - q := &ifacePair{x, y, p} - for p != nil { - if p.identical(q) { - return true // same pair was compared before - } - p = p.prev - } - if debug { - assert(sort.IsSorted(byUniqueMethodName(a))) - assert(sort.IsSorted(byUniqueMethodName(b))) - } - for i, f := range a { - g := b[i] - if f.Id() != g.Id() || !identical(f.typ, g.typ, q) { - return false - } - } - return true - } - } - - case *Map: - // Two map types are identical if they have identical key and value types. - if y, ok := y.(*Map); ok { - return identical(x.key, y.key, p) && identical(x.elem, y.elem, p) - } - - 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 && identical(x.elem, y.elem, p) - } - - case *Named: - // Two named types are identical if their type names originate - // in the same type declaration. - if y, ok := y.(*Named); ok { - return x.obj == y.obj - } - - default: - unreachable() - } - - return false -} - -// defaultType returns the default "typed" type for an "untyped" type; -// it returns the incoming type for all other types. The default type -// for untyped nil is untyped nil. -// -func defaultType(typ Type) Type { - if t, ok := typ.(*Basic); ok { - switch t.kind { - case UntypedBool: - return Typ[Bool] - case UntypedInt: - return Typ[Int] - case UntypedRune: - return universeRune // use 'rune' name - case UntypedFloat: - return Typ[Float64] - case UntypedComplex: - return Typ[Complex128] - case UntypedString: - return Typ[String] - } - } - return typ -} diff --git a/go/types/resolver.go b/go/types/resolver.go deleted file mode 100644 index 374ffc2800..0000000000 --- a/go/types/resolver.go +++ /dev/null @@ -1,453 +0,0 @@ -// 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 ( - "errors" - "fmt" - "go/ast" - "go/token" - pathLib "path" - "strconv" - "strings" - "unicode" - - "golang.org/x/tools/go/exact" -) - -// A declInfo describes a package-level const, type, var, or func declaration. -type declInfo struct { - file *Scope // scope of file containing this declaration - lhs []*Var // lhs of n:1 variable declarations, or nil - typ ast.Expr // type, or nil - init ast.Expr // init expression, or nil - fdecl *ast.FuncDecl // func declaration, or nil - - deps map[Object]bool // type and init dependencies; lazily allocated - mark int // for dependency analysis -} - -// hasInitializer reports whether the declared object has an initialization -// expression or function body. -func (d *declInfo) hasInitializer() bool { - return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil -} - -// addDep adds obj as a dependency to d. -func (d *declInfo) addDep(obj Object) { - m := d.deps - if m == nil { - m = make(map[Object]bool) - d.deps = m - } - m[obj] = true -} - -// arityMatch checks that the lhs and rhs of a const or var decl -// have the appropriate number of names and init exprs. For const -// decls, init is the value spec providing the init exprs; for -// var decls, init is nil (the init exprs are in s in this case). -func (check *Checker) arityMatch(s, init *ast.ValueSpec) { - l := len(s.Names) - r := len(s.Values) - if init != nil { - r = len(init.Values) - } - - switch { - case init == nil && r == 0: - // var decl w/o init expr - if s.Type == nil { - check.errorf(s.Pos(), "missing type or init expr") - } - case l < r: - if l < len(s.Values) { - // init exprs from s - n := s.Values[l] - check.errorf(n.Pos(), "extra init expr %s", n) - // TODO(gri) avoid declared but not used error here - } else { - // init exprs "inherited" - check.errorf(s.Pos(), "extra init expr at %s", init.Pos()) - // TODO(gri) avoid declared but not used error here - } - case l > r && (init != nil || r != 1): - n := s.Names[r] - check.errorf(n.Pos(), "missing init expr for %s", n) - } -} - -func validatedImportPath(path string) (string, error) { - s, err := strconv.Unquote(path) - if err != nil { - return "", err - } - if s == "" { - return "", fmt.Errorf("empty string") - } - const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" - for _, r := range s { - if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { - return s, fmt.Errorf("invalid character %#U", r) - } - } - return s, nil -} - -// declarePkgObj declares obj in the package scope, records its ident -> obj mapping, -// and updates check.objMap. The object must not be a function or method. -func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) { - assert(ident.Name == obj.Name()) - - // spec: "A package-scope or file-scope identifier with name init - // may only be declared to be a function with this (func()) signature." - if ident.Name == "init" { - check.errorf(ident.Pos(), "cannot declare init - must be func") - return - } - - check.declare(check.pkg.scope, ident, obj, token.NoPos) - check.objMap[obj] = d - obj.setOrder(uint32(len(check.objMap))) -} - -// filename returns a filename suitable for debugging output. -func (check *Checker) filename(fileNo int) string { - file := check.files[fileNo] - if pos := file.Pos(); pos.IsValid() { - return check.fset.File(pos).Name() - } - return fmt.Sprintf("file[%d]", fileNo) -} - -// collectObjects collects all file and package objects and inserts them -// into their respective scopes. It also performs imports and associates -// methods with receiver base type names. -func (check *Checker) collectObjects() { - pkg := check.pkg - - importer := check.conf.Import - if importer == nil { - if DefaultImport != nil { - importer = DefaultImport - } else { - // Panic if we encounter an import. - importer = func(map[string]*Package, string) (*Package, error) { - panic(`no Config.Import or DefaultImport (missing import _ "golang.org/x/tools/go/gcimporter"?)`) - } - } - } - - // pkgImports is the set of packages already imported by any package file seen - // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate - // it (pkg.imports may not be empty if we are checking test files incrementally). - var pkgImports = make(map[*Package]bool) - for _, imp := range pkg.imports { - pkgImports[imp] = true - } - - for fileNo, file := range check.files { - // The package identifier denotes the current package, - // but there is no corresponding package object. - check.recordDef(file.Name, nil) - - // Use the actual source file extent rather than *ast.File extent since the - // latter doesn't include comments which appear at the start or end of the file. - // Be conservative and use the *ast.File extent if we don't have a *token.File. - pos, end := file.Pos(), file.End() - if f := check.fset.File(file.Pos()); f != nil { - pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size()) - } - fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo)) - check.recordScope(file, fileScope) - - for _, decl := range file.Decls { - switch d := decl.(type) { - case *ast.BadDecl: - // ignore - - case *ast.GenDecl: - var last *ast.ValueSpec // last ValueSpec with type or init exprs seen - for iota, spec := range d.Specs { - switch s := spec.(type) { - case *ast.ImportSpec: - // import package - var imp *Package - path, err := validatedImportPath(s.Path.Value) - if err != nil { - check.errorf(s.Path.Pos(), "invalid import path (%s)", err) - continue - } - if path == "C" && check.conf.FakeImportC { - // TODO(gri) shouldn't create a new one each time - imp = NewPackage("C", "C") - imp.fake = true - } else { - var err error - imp, err = importer(check.conf.Packages, path) - if imp == nil && err == nil { - err = errors.New("Config.Import returned nil but no error") - } - if err != nil { - check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) - continue - } - } - - // add package to list of explicit imports - // (this functionality is provided as a convenience - // for clients; it is not needed for type-checking) - if !pkgImports[imp] { - pkgImports[imp] = true - if imp != Unsafe { - pkg.imports = append(pkg.imports, imp) - } - } - - // local name overrides imported package name - name := imp.name - if s.Name != nil { - name = s.Name.Name - if name == "init" { - check.errorf(s.Name.Pos(), "cannot declare init - must be func") - continue - } - } - - obj := NewPkgName(s.Pos(), pkg, name, imp) - if s.Name != nil { - // in a dot-import, the dot represents the package - check.recordDef(s.Name, obj) - } else { - check.recordImplicit(s, obj) - } - - // add import to file scope - if name == "." { - // merge imported scope with file scope - for _, obj := range imp.scope.elems { - // A package scope may contain non-exported objects, - // do not import them! - if obj.Exported() { - // TODO(gri) When we import a package, we create - // a new local package object. We should do the - // same for each dot-imported object. That way - // they can have correct position information. - // (We must not modify their existing position - // information because the same package - found - // via Config.Packages - may be dot-imported in - // another package!) - check.declare(fileScope, nil, obj, token.NoPos) - check.recordImplicit(s, obj) - } - } - // add position to set of dot-import positions for this file - // (this is only needed for "imported but not used" errors) - check.addUnusedDotImport(fileScope, imp, s.Pos()) - } else { - // declare imported package object in file scope - check.declare(fileScope, nil, obj, token.NoPos) - } - - case *ast.ValueSpec: - switch d.Tok { - case token.CONST: - // determine which initialization expressions to use - switch { - case s.Type != nil || len(s.Values) > 0: - last = s - case last == nil: - last = new(ast.ValueSpec) // make sure last exists - } - - // declare all constants - for i, name := range s.Names { - obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) - - var init ast.Expr - if i < len(last.Values) { - init = last.Values[i] - } - - d := &declInfo{file: fileScope, typ: last.Type, init: init} - check.declarePkgObj(name, obj, d) - } - - check.arityMatch(s, last) - - case token.VAR: - lhs := make([]*Var, len(s.Names)) - // If there's exactly one rhs initializer, use - // the same declInfo d1 for all lhs variables - // so that each lhs variable depends on the same - // rhs initializer (n:1 var declaration). - var d1 *declInfo - if len(s.Values) == 1 { - // The lhs elements are only set up after the for loop below, - // but that's ok because declareVar only collects the declInfo - // for a later phase. - d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]} - } - - // declare all variables - for i, name := range s.Names { - obj := NewVar(name.Pos(), pkg, name.Name, nil) - lhs[i] = obj - - d := d1 - if d == nil { - // individual assignments - var init ast.Expr - if i < len(s.Values) { - init = s.Values[i] - } - d = &declInfo{file: fileScope, typ: s.Type, init: init} - } - - check.declarePkgObj(name, obj, d) - } - - check.arityMatch(s, nil) - - default: - check.invalidAST(s.Pos(), "invalid token %s", d.Tok) - } - - case *ast.TypeSpec: - obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type}) - - default: - check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) - } - } - - case *ast.FuncDecl: - name := d.Name.Name - obj := NewFunc(d.Name.Pos(), pkg, name, nil) - if d.Recv == nil { - // regular function - if name == "init" { - // don't declare init functions in the package scope - they are invisible - obj.parent = pkg.scope - check.recordDef(d.Name, obj) - // init functions must have a body - if d.Body == nil { - check.softErrorf(obj.pos, "missing function body") - } - } else { - check.declare(pkg.scope, d.Name, obj, token.NoPos) - } - } else { - // method - check.recordDef(d.Name, obj) - // Associate method with receiver base type name, if possible. - // Ignore methods that have an invalid receiver, or a blank _ - // receiver name. They will be type-checked later, with regular - // functions. - if list := d.Recv.List; len(list) > 0 { - typ := list[0].Type - if ptr, _ := typ.(*ast.StarExpr); ptr != nil { - typ = ptr.X - } - if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" { - check.assocMethod(base.Name, obj) - } - } - } - info := &declInfo{file: fileScope, fdecl: d} - check.objMap[obj] = info - obj.setOrder(uint32(len(check.objMap))) - - default: - check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) - } - } - } - - // verify that objects in package and file scopes have different names - for _, scope := range check.pkg.scope.children /* file scopes */ { - for _, obj := range scope.elems { - if alt := pkg.scope.Lookup(obj.Name()); alt != nil { - if pkg, ok := obj.(*PkgName); ok { - check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported()) - check.reportAltDecl(pkg) - } else { - check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg()) - // TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything - check.reportAltDecl(obj) - } - } - } - } -} - -// packageObjects typechecks all package objects in objList, but not function bodies. -func (check *Checker) packageObjects(objList []Object) { - // add new methods to already type-checked types (from a prior Checker.Files call) - for _, obj := range objList { - if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil { - check.addMethodDecls(obj) - } - } - - // pre-allocate space for type declaration paths so that the underlying array is reused - typePath := make([]*TypeName, 0, 8) - - for _, obj := range objList { - check.objDecl(obj, nil, typePath) - } - - // At this point we may have a non-empty check.methods map; this means that not all - // entries were deleted at the end of typeDecl because the respective receiver base - // types were not found. In that case, an error was reported when declaring those - // methods. We can now safely discard this map. - check.methods = nil -} - -// functionBodies typechecks all function bodies. -func (check *Checker) functionBodies() { - for _, f := range check.funcs { - check.funcBody(f.decl, f.name, f.sig, f.body) - } -} - -// unusedImports checks for unused imports. -func (check *Checker) unusedImports() { - // if function bodies are not checked, packages' uses are likely missing - don't check - if check.conf.IgnoreFuncBodies { - return - } - - // spec: "It is illegal (...) to directly import a package without referring to - // any of its exported identifiers. To import a package solely for its side-effects - // (initialization), use the blank identifier as explicit package name." - - // check use of regular imported packages - for _, scope := range check.pkg.scope.children /* file scopes */ { - for _, obj := range scope.elems { - if obj, ok := obj.(*PkgName); ok { - // Unused "blank imports" are automatically ignored - // since _ identifiers are not entered into scopes. - if !obj.used { - path := obj.imported.path - base := pathLib.Base(path) - if obj.name == base { - check.softErrorf(obj.pos, "%q imported but not used", path) - } else { - check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name) - } - } - } - } - } - - // check use of dot-imported packages - for _, unusedDotImports := range check.unusedDotImports { - for pkg, pos := range unusedDotImports { - check.softErrorf(pos, "%q imported but not used", pkg.path) - } - } -} diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go deleted file mode 100644 index 2ef1f18648..0000000000 --- a/go/types/resolver_test.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2011 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_test - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "sort" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -func TestResolveIdents(t *testing.T) { - skipSpecialPlatforms(t) - - sources := []string{ - ` - package p - import "fmt" - import "math" - const pi = math.Pi - func sin(x float64) float64 { - return math.Sin(x) - } - var Println = fmt.Println - `, - ` - package p - import "fmt" - type errorStringer struct { fmt.Stringer; error } - func f() string { - _ = "foo" - return fmt.Sprintf("%d", g()) - } - func g() (x int) { return } - `, - ` - package p - import . "go/parser" - import "sync" - func h() Mode { return ImportsOnly } - var _, x int = 1, 2 - func init() {} - type T struct{ *sync.Mutex; a, b, c int} - type I interface{ m() } - var _ = T{a: 1, b: 2, c: 3} - func (_ T) m() {} - func (T) _() {} - var i I - var _ = i.m - func _(s []int) { for i, x := range s { _, _ = i, x } } - func _(x interface{}) { - switch x := x.(type) { - case int: - _ = x - } - switch {} // implicit 'true' tag - } - `, - ` - package p - type S struct{} - func (T) _() {} - func (T) _() {} - `, - ` - package p - func _() { - L0: - L1: - goto L0 - for { - goto L1 - } - if true { - goto L2 - } - L2: - } - `, - } - - pkgnames := []string{ - "fmt", - "math", - } - - // parse package files - fset := token.NewFileSet() - var files []*ast.File - for i, src := range sources { - f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors) - if err != nil { - t.Fatal(err) - } - files = append(files, f) - } - - // resolve and type-check package AST - var conf Config - uses := make(map[*ast.Ident]Object) - defs := make(map[*ast.Ident]Object) - _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses}) - if err != nil { - t.Fatal(err) - } - - // check that all packages were imported - for _, name := range pkgnames { - if conf.Packages[name] == nil { - t.Errorf("package %s not imported", name) - } - } - - // check that qualified identifiers are resolved - for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - if s, ok := n.(*ast.SelectorExpr); ok { - if x, ok := s.X.(*ast.Ident); ok { - obj := uses[x] - if obj == nil { - t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) - return false - } - if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil { - t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) - return false - } - return false - } - return false - } - return true - }) - } - - for id, obj := range uses { - if obj == nil { - t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name) - } - } - - // check that each identifier in the source is found in uses or defs or both - var both []string - for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - if x, ok := n.(*ast.Ident); ok { - var objects int - if _, found := uses[x]; found { - objects |= 1 - delete(uses, x) - } - if _, found := defs[x]; found { - objects |= 2 - delete(defs, x) - } - if objects == 0 { - t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name) - } else if objects == 3 { - both = append(both, x.Name) - } - return false - } - return true - }) - } - - // check the expected set of idents that are simultaneously uses and defs - sort.Strings(both) - if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want { - t.Errorf("simultaneous uses/defs = %s, want %s", got, want) - } - - // any left-over identifiers didn't exist in the source - for x := range uses { - t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) - } - for x := range defs { - t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) - } - - // TODO(gri) add tests to check ImplicitObj callbacks -} diff --git a/go/types/return.go b/go/types/return.go deleted file mode 100644 index 6628985214..0000000000 --- a/go/types/return.go +++ /dev/null @@ -1,185 +0,0 @@ -// 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. - -// This file implements isTerminating. - -package types - -import ( - "go/ast" - "go/token" -) - -// isTerminating reports if s is a terminating statement. -// If s is labeled, label is the label name; otherwise s -// is "". -func (check *Checker) isTerminating(s ast.Stmt, label string) bool { - switch s := s.(type) { - default: - unreachable() - - case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt, - *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt, - *ast.RangeStmt: - // no chance - - case *ast.LabeledStmt: - return check.isTerminating(s.Stmt, s.Label.Name) - - case *ast.ExprStmt: - // the predeclared (possibly parenthesized) panic() function is terminating - if call, _ := unparen(s.X).(*ast.CallExpr); call != nil { - if id, _ := call.Fun.(*ast.Ident); id != nil { - if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil { - if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { - return true - } - } - } - } - - case *ast.ReturnStmt: - return true - - case *ast.BranchStmt: - if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH { - return true - } - - case *ast.BlockStmt: - return check.isTerminatingList(s.List, "") - - case *ast.IfStmt: - if s.Else != nil && - check.isTerminating(s.Body, "") && - check.isTerminating(s.Else, "") { - return true - } - - case *ast.SwitchStmt: - return check.isTerminatingSwitch(s.Body, label) - - case *ast.TypeSwitchStmt: - return check.isTerminatingSwitch(s.Body, label) - - case *ast.SelectStmt: - for _, s := range s.Body.List { - cc := s.(*ast.CommClause) - if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { - return false - } - - } - return true - - case *ast.ForStmt: - if s.Cond == nil && !hasBreak(s.Body, label, true) { - return true - } - } - - return false -} - -func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool { - n := len(list) - return n > 0 && check.isTerminating(list[n-1], label) -} - -func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool { - hasDefault := false - for _, s := range body.List { - cc := s.(*ast.CaseClause) - if cc.List == nil { - hasDefault = true - } - if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { - return false - } - } - return hasDefault -} - -// TODO(gri) For nested breakable statements, the current implementation of hasBreak -// will traverse the same subtree repeatedly, once for each label. Replace -// with a single-pass label/break matching phase. - -// hasBreak reports if s is or contains a break statement -// referring to the label-ed statement or implicit-ly the -// closest outer breakable statement. -func hasBreak(s ast.Stmt, label string, implicit bool) bool { - switch s := s.(type) { - default: - unreachable() - - case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt, - *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, - *ast.DeferStmt, *ast.ReturnStmt: - // no chance - - case *ast.LabeledStmt: - return hasBreak(s.Stmt, label, implicit) - - case *ast.BranchStmt: - if s.Tok == token.BREAK { - if s.Label == nil { - return implicit - } - if s.Label.Name == label { - return true - } - } - - case *ast.BlockStmt: - return hasBreakList(s.List, label, implicit) - - case *ast.IfStmt: - if hasBreak(s.Body, label, implicit) || - s.Else != nil && hasBreak(s.Else, label, implicit) { - return true - } - - case *ast.CaseClause: - return hasBreakList(s.Body, label, implicit) - - case *ast.SwitchStmt: - if label != "" && hasBreak(s.Body, label, false) { - return true - } - - case *ast.TypeSwitchStmt: - if label != "" && hasBreak(s.Body, label, false) { - return true - } - - case *ast.CommClause: - return hasBreakList(s.Body, label, implicit) - - case *ast.SelectStmt: - if label != "" && hasBreak(s.Body, label, false) { - return true - } - - case *ast.ForStmt: - if label != "" && hasBreak(s.Body, label, false) { - return true - } - - case *ast.RangeStmt: - if label != "" && hasBreak(s.Body, label, false) { - return true - } - } - - return false -} - -func hasBreakList(list []ast.Stmt, label string, implicit bool) bool { - for _, s := range list { - if hasBreak(s, label, implicit) { - return true - } - } - return false -} diff --git a/go/types/scope.go b/go/types/scope.go deleted file mode 100644 index 3502840225..0000000000 --- a/go/types/scope.go +++ /dev/null @@ -1,190 +0,0 @@ -// 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. - -// This file implements Scopes. - -package types - -import ( - "bytes" - "fmt" - "go/token" - "io" - "sort" - "strings" -) - -// TODO(gri) Provide scopes with a name or other mechanism so that -// objects can use that information for better printing. - -// A Scope maintains a set of objects and links to its containing -// (parent) and contained (children) scopes. Objects may be inserted -// and looked up by name. The zero value for Scope is a ready-to-use -// empty scope. -type Scope struct { - parent *Scope - children []*Scope - elems map[string]Object // lazily allocated - pos, end token.Pos // scope extent; may be invalid - comment string // for debugging only -} - -// NewScope returns a new, empty scope contained in the given parent -// scope, if any. The comment is for debugging only. -func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope { - s := &Scope{parent, nil, nil, pos, end, comment} - // don't add children to Universe scope! - if parent != nil && parent != Universe { - parent.children = append(parent.children, s) - } - return s -} - -// Parent returns the scope's containing (parent) scope. -func (s *Scope) Parent() *Scope { return s.parent } - -// Len() returns the number of scope elements. -func (s *Scope) Len() int { return len(s.elems) } - -// Names returns the scope's element names in sorted order. -func (s *Scope) Names() []string { - names := make([]string, len(s.elems)) - i := 0 - for name := range s.elems { - names[i] = name - i++ - } - sort.Strings(names) - return names -} - -// NumChildren() returns the number of scopes nested in s. -func (s *Scope) NumChildren() int { return len(s.children) } - -// Child returns the i'th child scope for 0 <= i < NumChildren(). -func (s *Scope) Child(i int) *Scope { return s.children[i] } - -// Lookup returns the object in scope s with the given name if such an -// object exists; otherwise the result is nil. -func (s *Scope) Lookup(name string) Object { - return s.elems[name] -} - -// LookupParent follows the parent chain of scopes starting with s until -// it finds a scope where Lookup(name) returns a non-nil object, and then -// returns that scope and object. If a valid position pos is provided, -// only objects that were declared at or before pos are considered. -// If no such scope and object exists, the result is (nil, nil). -// -// Note that obj.Parent() may be different from the returned scope if the -// object was inserted into the scope and already had a parent at that -// time (see Insert, below). This can only happen for dot-imported objects -// whose scope is the scope of the package that exported them. -func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) { - for ; s != nil; s = s.parent { - if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) { - return s, obj - } - } - return nil, nil -} - -// Insert attempts to insert an object obj into scope s. -// If s already contains an alternative object alt with -// the same name, Insert leaves s unchanged and returns alt. -// Otherwise it inserts obj, sets the object's parent scope -// if not already set, and returns nil. -func (s *Scope) Insert(obj Object) Object { - name := obj.Name() - if alt := s.elems[name]; alt != nil { - return alt - } - if s.elems == nil { - s.elems = make(map[string]Object) - } - s.elems[name] = obj - if obj.Parent() == nil { - obj.setParent(s) - } - return nil -} - -// Pos and End describe the scope's source code extent [pos, end). -// The results are guaranteed to be valid only if the type-checked -// AST has complete position information. The extent is undefined -// for Universe and package scopes. -func (s *Scope) Pos() token.Pos { return s.pos } -func (s *Scope) End() token.Pos { return s.end } - -// Contains returns true if pos is within the scope's extent. -// The result is guaranteed to be valid only if the type-checked -// AST has complete position information. -func (s *Scope) Contains(pos token.Pos) bool { - return s.pos <= pos && pos < s.end -} - -// Innermost returns the innermost (child) scope containing -// pos. If pos is not within any scope, the result is nil. -// The result is also nil for the Universe scope. -// The result is guaranteed to be valid only if the type-checked -// AST has complete position information. -func (s *Scope) Innermost(pos token.Pos) *Scope { - // Package scopes do not have extents since they may be - // discontiguous, so iterate over the package's files. - if s.parent == Universe { - for _, s := range s.children { - if inner := s.Innermost(pos); inner != nil { - return inner - } - } - } - - if s.Contains(pos) { - for _, s := range s.children { - if s.Contains(pos) { - return s.Innermost(pos) - } - } - return s - } - return nil -} - -// WriteTo writes a string representation of the scope to w, -// with the scope elements sorted by name. -// The level of indentation is controlled by n >= 0, with -// n == 0 for no indentation. -// If recurse is set, it also writes nested (children) scopes. -func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { - const ind = ". " - indn := strings.Repeat(ind, n) - - fmt.Fprintf(w, "%s%s scope %p {", indn, s.comment, s) - if len(s.elems) == 0 { - fmt.Fprintf(w, "}\n") - return - } - - fmt.Fprintln(w) - indn1 := indn + ind - for _, name := range s.Names() { - fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name]) - } - - if recurse { - for _, s := range s.children { - fmt.Fprintln(w) - s.WriteTo(w, n+1, recurse) - } - } - - fmt.Fprintf(w, "%s}", indn) -} - -// String returns a string representation of the scope, for debugging. -func (s *Scope) String() string { - var buf bytes.Buffer - s.WriteTo(&buf, 0, false) - return buf.String() -} diff --git a/go/types/selection.go b/go/types/selection.go deleted file mode 100644 index 124e0d39f0..0000000000 --- a/go/types/selection.go +++ /dev/null @@ -1,143 +0,0 @@ -// 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. - -// This file implements Selections. - -package types - -import ( - "bytes" - "fmt" -) - -// SelectionKind describes the kind of a selector expression x.f -// (excluding qualified identifiers). -type SelectionKind int - -const ( - FieldVal SelectionKind = iota // x.f is a struct field selector - MethodVal // x.f is a method selector - MethodExpr // x.f is a method expression -) - -// A Selection describes a selector expression x.f. -// For the declarations: -// -// type T struct{ x int; E } -// type E struct{} -// func (e E) m() {} -// var p *T -// -// the following relations exist: -// -// Selector Kind Recv Obj Type Index Indirect -// -// p.x FieldVal T x int {0} true -// p.m MethodVal *T m func (e *T) m() {1, 0} true -// T.m MethodExpr T m func m(_ T) {1, 0} false -// -type Selection struct { - kind SelectionKind - recv Type // type of x - obj Object // object denoted by x.f - index []int // path from x to x.f - indirect bool // set if there was any pointer indirection on the path -} - -// Kind returns the selection kind. -func (s *Selection) Kind() SelectionKind { return s.kind } - -// Recv returns the type of x in x.f. -func (s *Selection) Recv() Type { return s.recv } - -// Obj returns the object denoted by x.f; a *Var for -// a field selection, and a *Func in all other cases. -func (s *Selection) Obj() Object { return s.obj } - -// Type returns the type of x.f, which may be different from the type of f. -// See Selection for more information. -func (s *Selection) Type() Type { - switch s.kind { - case MethodVal: - // The type of x.f is a method with its receiver type set - // to the type of x. - sig := *s.obj.(*Func).typ.(*Signature) - recv := *sig.recv - recv.typ = s.recv - sig.recv = &recv - return &sig - - case MethodExpr: - // The type of x.f is a function (without receiver) - // and an additional first argument with the same type as x. - // TODO(gri) Similar code is already in call.go - factor! - // TODO(gri) Compute this eagerly to avoid allocations. - sig := *s.obj.(*Func).typ.(*Signature) - arg0 := *sig.recv - sig.recv = nil - arg0.typ = s.recv - var params []*Var - if sig.params != nil { - params = sig.params.vars - } - sig.params = NewTuple(append([]*Var{&arg0}, params...)...) - return &sig - } - - // In all other cases, the type of x.f is the type of x. - return s.obj.Type() -} - -// Index describes the path from x to f in x.f. -// The last index entry is the field or method index of the type declaring f; -// either: -// -// 1) the list of declared methods of a named type; or -// 2) the list of methods of an interface type; or -// 3) the list of fields of a struct type. -// -// The earlier index entries are the indices of the embedded fields implicitly -// traversed to get from (the type of) x to f, starting at embedding depth 0. -func (s *Selection) Index() []int { return s.index } - -// Indirect reports whether any pointer indirection was required to get from -// x to f in x.f. -func (s *Selection) Indirect() bool { return s.indirect } - -func (s *Selection) String() string { return SelectionString(s, nil) } - -// SelectionString returns the string form of s. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -// -// Examples: -// "field (T) f int" -// "method (T) f(X) Y" -// "method expr (T) f(X) Y" -// -func SelectionString(s *Selection, qf Qualifier) string { - var k string - switch s.kind { - case FieldVal: - k = "field " - case MethodVal: - k = "method " - case MethodExpr: - k = "method expr " - default: - unreachable() - } - var buf bytes.Buffer - buf.WriteString(k) - buf.WriteByte('(') - WriteType(&buf, s.Recv(), qf) - fmt.Fprintf(&buf, ") %s", s.obj.Name()) - if T := s.Type(); s.kind == FieldVal { - buf.WriteByte(' ') - WriteType(&buf, T, qf) - } else { - WriteSignature(&buf, T.(*Signature), qf) - } - return buf.String() -} diff --git a/go/types/self_test.go b/go/types/self_test.go deleted file mode 100644 index 01d12c71a0..0000000000 --- a/go/types/self_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// 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_test - -import ( - "flag" - "fmt" - "go/ast" - "go/parser" - "go/token" - "path/filepath" - "testing" - "time" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -var benchmark = flag.Bool("b", false, "run benchmarks") - -func TestSelf(t *testing.T) { - fset := token.NewFileSet() - files, err := pkgFiles(fset, ".") - if err != nil { - t.Fatal(err) - } - - _, err = Check("go/types", fset, files) - if err != nil { - // Importing go.tools/go/exact doensn't work in the - // build dashboard environment. Don't report an error - // for now so that the build remains green. - // TODO(gri) fix this - t.Log(err) // replace w/ t.Fatal eventually - return - } -} - -func TestBenchmark(t *testing.T) { - if !*benchmark { - return - } - - // We're not using testing's benchmarking mechanism directly - // because we want custom output. - - for _, p := range []string{"types", "exact", "gcimporter"} { - path := filepath.Join("..", p) - runbench(t, path, false) - runbench(t, path, true) - fmt.Println() - } -} - -func runbench(t *testing.T, path string, ignoreFuncBodies bool) { - fset := token.NewFileSet() - files, err := pkgFiles(fset, path) - if err != nil { - t.Fatal(err) - } - - b := testing.Benchmark(func(b *testing.B) { - for i := 0; i < b.N; i++ { - conf := Config{IgnoreFuncBodies: ignoreFuncBodies} - conf.Check(path, fset, files, nil) - } - }) - - // determine line count - lines := 0 - fset.Iterate(func(f *token.File) bool { - lines += f.LineCount() - return true - }) - - d := time.Duration(b.NsPerOp()) - fmt.Printf( - "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n", - filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies, - ) -} - -func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) { - filenames, err := pkgFilenames(path) // from stdlib_test.go - if err != nil { - return nil, err - } - - var files []*ast.File - for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, 0) - if err != nil { - return nil, err - } - files = append(files, file) - } - - return files, nil -} diff --git a/go/types/sizes.go b/go/types/sizes.go deleted file mode 100644 index 56fb310c29..0000000000 --- a/go/types/sizes.go +++ /dev/null @@ -1,211 +0,0 @@ -// 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. - -// This file implements Sizes. - -package types - -// Sizes defines the sizing functions for package unsafe. -type Sizes interface { - // Alignof returns the alignment of a variable of type T. - // Alignof must implement the alignment guarantees required by the spec. - Alignof(T Type) int64 - - // Offsetsof returns the offsets of the given struct fields, in bytes. - // Offsetsof must implement the offset guarantees required by the spec. - Offsetsof(fields []*Var) []int64 - - // Sizeof returns the size of a variable of type T. - // Sizeof must implement the size guarantees required by the spec. - Sizeof(T Type) int64 -} - -// StdSizes is a convenience type for creating commonly used Sizes. -// It makes the following simplifying assumptions: -// -// - The size of explicitly sized basic types (int16, etc.) is the -// specified size. -// - The size of strings and interfaces is 2*WordSize. -// - The size of slices is 3*WordSize. -// - The size of an array of n elements corresponds to the size of -// a struct of n consecutive fields of the array's element type. -// - The size of a struct is the offset of the last field plus that -// field's size. As with all element types, if the struct is used -// in an array its size must first be aligned to a multiple of the -// struct's alignment. -// - All other types have size WordSize. -// - Arrays and structs are aligned per spec definition; all other -// types are naturally aligned with a maximum alignment MaxAlign. -// -// *StdSizes implements Sizes. -// -type StdSizes struct { - WordSize int64 // word size in bytes - must be >= 4 (32bits) - MaxAlign int64 // maximum alignment in bytes - must be >= 1 -} - -func (s *StdSizes) Alignof(T Type) int64 { - // For arrays and structs, alignment is defined in terms - // of alignment of the elements and fields, respectively. - switch t := T.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 s.Alignof(t.elem) - 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 { - if a := s.Alignof(f.typ); a > max { - max = a - } - } - return max - } - a := s.Sizeof(T) // may be 0 - // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." - if a < 1 { - return 1 - } - if a > s.MaxAlign { - return s.MaxAlign - } - return a -} - -func (s *StdSizes) Offsetsof(fields []*Var) []int64 { - offsets := make([]int64, len(fields)) - var o int64 - for i, f := range fields { - a := s.Alignof(f.typ) - o = align(o, a) - offsets[i] = o - o += s.Sizeof(f.typ) - } - return offsets -} - -var basicSizes = [...]byte{ - Bool: 1, - Int8: 1, - Int16: 2, - Int32: 4, - Int64: 8, - Uint8: 1, - Uint16: 2, - Uint32: 4, - Uint64: 8, - Float32: 4, - Float64: 8, - Complex64: 8, - Complex128: 16, -} - -func (s *StdSizes) Sizeof(T Type) int64 { - switch t := T.Underlying().(type) { - case *Basic: - assert(isTyped(T)) - k := t.kind - if int(k) < len(basicSizes) { - if s := basicSizes[k]; s > 0 { - return int64(s) - } - } - if k == String { - return s.WordSize * 2 - } - case *Array: - n := t.len - if n == 0 { - return 0 - } - a := s.Alignof(t.elem) - z := s.Sizeof(t.elem) - return align(z, a)*(n-1) + z - case *Slice: - return s.WordSize * 3 - case *Struct: - n := t.NumFields() - if n == 0 { - return 0 - } - offsets := t.offsets - if t.offsets == nil { - // compute offsets on demand - offsets = s.Offsetsof(t.fields) - t.offsets = offsets - } - return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) - case *Interface: - return s.WordSize * 2 - } - return s.WordSize // catch-all -} - -// stdSizes is used if Config.Sizes == nil. -var stdSizes = StdSizes{8, 8} - -func (conf *Config) alignof(T Type) int64 { - if s := conf.Sizes; s != nil { - if a := s.Alignof(T); a >= 1 { - return a - } - panic("Config.Sizes.Alignof returned an alignment < 1") - } - return stdSizes.Alignof(T) -} - -func (conf *Config) offsetsof(T *Struct) []int64 { - offsets := T.offsets - if offsets == nil && T.NumFields() > 0 { - // compute offsets on demand - if s := conf.Sizes; s != nil { - offsets = s.Offsetsof(T.fields) - // sanity checks - if len(offsets) != T.NumFields() { - panic("Config.Sizes.Offsetsof returned the wrong number of offsets") - } - for _, o := range offsets { - if o < 0 { - panic("Config.Sizes.Offsetsof returned an offset < 0") - } - } - } else { - offsets = stdSizes.Offsetsof(T.fields) - } - T.offsets = offsets - } - return offsets -} - -// offsetof returns the offset of the field specified via -// the index sequence relative to typ. All embedded fields -// must be structs (rather than pointer to structs). -func (conf *Config) offsetof(typ Type, index []int) int64 { - var o int64 - for _, i := range index { - s := typ.Underlying().(*Struct) - o += conf.offsetsof(s)[i] - typ = s.fields[i].typ - } - return o -} - -func (conf *Config) sizeof(T Type) int64 { - if s := conf.Sizes; s != nil { - if z := s.Sizeof(T); z >= 0 { - return z - } - panic("Config.Sizes.Sizeof returned a size < 0") - } - return stdSizes.Sizeof(T) -} - -// align returns the smallest y >= x such that y % a == 0. -func align(x, a int64) int64 { - y := x + a - 1 - return y - y%a -} diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go deleted file mode 100644 index 7554f91578..0000000000 --- a/go/types/stdlib_test.go +++ /dev/null @@ -1,279 +0,0 @@ -// 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. - -// This file tests types.Check by using it to -// typecheck the standard library and tests. - -package types_test - -import ( - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -var ( - pkgCount int // number of packages processed - start = time.Now() -) - -func TestStdlib(t *testing.T) { - skipSpecialPlatforms(t) - - walkDirs(t, filepath.Join(runtime.GOROOT(), "src")) - if testing.Verbose() { - fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) - } -} - -// firstComment returns the contents of the first comment in -// the given file, assuming there's one within the first KB. -func firstComment(filename string) string { - f, err := os.Open(filename) - if err != nil { - return "" - } - defer f.Close() - - var src [1 << 10]byte // read at most 1KB - n, _ := f.Read(src[:]) - - var s scanner.Scanner - s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments) - for { - _, tok, lit := s.Scan() - switch tok { - case token.COMMENT: - // remove trailing */ of multi-line comment - if lit[1] == '*' { - lit = lit[:len(lit)-2] - } - return strings.TrimSpace(lit[2:]) - case token.EOF: - return "" - } - } -} - -func testTestDir(t *testing.T, path string, ignore ...string) { - files, err := ioutil.ReadDir(path) - if err != nil { - t.Fatal(err) - } - - excluded := make(map[string]bool) - for _, filename := range ignore { - excluded[filename] = true - } - - fset := token.NewFileSet() - for _, f := range files { - // filter directory contents - if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] { - continue - } - - // get per-file instructions - expectErrors := false - filename := filepath.Join(path, f.Name()) - if cmd := firstComment(filename); cmd != "" { - switch cmd { - case "skip", "compiledir": - continue // ignore this file - case "errorcheck": - expectErrors = true - } - } - - // parse and type-check file - file, err := parser.ParseFile(fset, filename, nil, 0) - if err == nil { - _, err = Check(filename, fset, []*ast.File{file}) - } - - if expectErrors { - if err == nil { - t.Errorf("expected errors but found none in %s", filename) - } - } else { - if err != nil { - t.Error(err) - } - } - } -} - -func TestStdTest(t *testing.T) { - skipSpecialPlatforms(t) - - // test/recover4.go is only built for Linux and Darwin. - // TODO(gri) Remove once tests consider +build tags (issue 10370). - if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { - return - } - - testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), - "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore - "sigchld.go", // don't work on Windows; testTestDir should consult build tags - ) -} - -func TestStdFixed(t *testing.T) { - skipSpecialPlatforms(t) - - testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), - "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore - "bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification) - "issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification) - "issue6889.go", // gc-specific test - "issue7746.go", // large constants - consumes too much memory - "issue11326.go", // large constants - "issue11326b.go", // large constants - "issue11362.go", // canonical import path check is implementation-defined behavior - "issue13471.go", // large constants - ) -} - -func TestStdKen(t *testing.T) { - skipSpecialPlatforms(t) - - testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) -} - -// Package paths of excluded packages. -var excluded = map[string]bool{ - "builtin": true, -} - -// typecheck typechecks the given package files. -func typecheck(t *testing.T, path string, filenames []string) { - fset := token.NewFileSet() - - // parse package files - var files []*ast.File - for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) - if err != nil { - // the parser error may be a list of individual errors; report them all - if list, ok := err.(scanner.ErrorList); ok { - for _, err := range list { - t.Error(err) - } - return - } - t.Error(err) - return - } - - if testing.Verbose() { - if len(files) == 0 { - fmt.Println("package", file.Name.Name) - } - fmt.Println("\t", filename) - } - - files = append(files, file) - } - - // gcimporter doesn't support vendored imports. - // TODO(gri): fix. - if strings.HasSuffix(path, "src/cmd/internal/objfile") || - strings.HasSuffix(path, "src/net/http") { - return - } - - // typecheck package files - var conf Config - conf.Error = func(err error) { t.Error(err) } - info := Info{Uses: make(map[*ast.Ident]Object)} - conf.Check(path, fset, files, &info) - pkgCount++ - - // Perform checks of API invariants. - - // All Objects have a package, except predeclared ones. - errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error - for id, obj := range info.Uses { - predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError - if predeclared == (obj.Pkg() != nil) { - posn := fset.Position(id.Pos()) - if predeclared { - t.Errorf("%s: predeclared object with package: %s", posn, obj) - } else { - t.Errorf("%s: user-defined object without package: %s", posn, obj) - } - } - } -} - -// pkgFilenames returns the list of package filenames for the given directory. -func pkgFilenames(dir string) ([]string, error) { - ctxt := build.Default - ctxt.CgoEnabled = false - pkg, err := ctxt.ImportDir(dir, 0) - if err != nil { - if _, nogo := err.(*build.NoGoError); nogo { - return nil, nil // no *.go files, not an error - } - return nil, err - } - if excluded[pkg.ImportPath] { - return nil, nil - } - var filenames []string - for _, name := range pkg.GoFiles { - filenames = append(filenames, filepath.Join(pkg.Dir, name)) - } - for _, name := range pkg.TestGoFiles { - filenames = append(filenames, filepath.Join(pkg.Dir, name)) - } - return filenames, nil -} - -// Note: Could use filepath.Walk instead of walkDirs but that wouldn't -// necessarily be shorter or clearer after adding the code to -// terminate early for -short tests. - -func walkDirs(t *testing.T, dir string) { - // limit run time for short tests - if testing.Short() && time.Since(start) >= 750*time.Millisecond { - return - } - - fis, err := ioutil.ReadDir(dir) - if err != nil { - t.Error(err) - return - } - - // typecheck package in directory - files, err := pkgFilenames(dir) - if err != nil { - t.Error(err) - return - } - if files != nil { - typecheck(t, dir, files) - } - - // traverse subdirectories, but don't walk into testdata - for _, fi := range fis { - if fi.IsDir() && fi.Name() != "testdata" { - walkDirs(t, filepath.Join(dir, fi.Name())) - } - } -} diff --git a/go/types/stmt.go b/go/types/stmt.go deleted file mode 100644 index eeb2c31730..0000000000 --- a/go/types/stmt.go +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright 2012 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. - -// This file implements typechecking of statements. - -package types - -import ( - "fmt" - "go/ast" - "go/token" - - "golang.org/x/tools/go/exact" -) - -func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) { - if trace { - if name == "" { - name = "" - } - fmt.Printf("--- %s: %s {\n", name, sig) - defer fmt.Println("--- ") - } - - // set function scope extent - sig.scope.pos = body.Pos() - sig.scope.end = body.End() - - // save/restore current context and setup function context - // (and use 0 indentation at function start) - defer func(ctxt context, indent int) { - check.context = ctxt - check.indent = indent - }(check.context, check.indent) - check.context = context{ - decl: decl, - scope: sig.scope, - sig: sig, - } - check.indent = 0 - - check.stmtList(0, body.List) - - if check.hasLabel { - check.labels(body) - } - - if sig.results.Len() > 0 && !check.isTerminating(body, "") { - check.error(body.Rbrace, "missing return") - } - - // spec: "Implementation restriction: A compiler may make it illegal to - // declare a variable inside a function body if the variable is never used." - // (One could check each scope after use, but that distributes this check - // over several places because CloseScope is not always called explicitly.) - check.usage(sig.scope) -} - -func (check *Checker) usage(scope *Scope) { - for _, obj := range scope.elems { - if v, _ := obj.(*Var); v != nil && !v.used { - check.softErrorf(v.pos, "%s declared but not used", v.name) - } - } - for _, scope := range scope.children { - check.usage(scope) - } -} - -// stmtContext is a bitset describing which -// control-flow statements are permissible. -type stmtContext uint - -const ( - breakOk stmtContext = 1 << iota - continueOk - fallthroughOk -) - -func (check *Checker) simpleStmt(s ast.Stmt) { - if s != nil { - check.stmt(0, s) - } -} - -func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) { - ok := ctxt&fallthroughOk != 0 - inner := ctxt &^ fallthroughOk - for i, s := range list { - inner := inner - if ok && i+1 == len(list) { - inner |= fallthroughOk - } - check.stmt(inner, s) - } -} - -func (check *Checker) multipleDefaults(list []ast.Stmt) { - var first ast.Stmt - for _, s := range list { - var d ast.Stmt - switch c := s.(type) { - case *ast.CaseClause: - if len(c.List) == 0 { - d = s - } - case *ast.CommClause: - if c.Comm == nil { - d = s - } - default: - check.invalidAST(s.Pos(), "case/communication clause expected") - } - if d != nil { - if first != nil { - check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos()) - } else { - first = d - } - } - } -} - -func (check *Checker) openScope(s ast.Stmt, comment string) { - scope := NewScope(check.scope, s.Pos(), s.End(), comment) - check.recordScope(s, scope) - check.scope = scope -} - -func (check *Checker) closeScope() { - check.scope = check.scope.Parent() -} - -func assignOp(op token.Token) token.Token { - // token_test.go verifies the token ordering this function relies on - if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN { - return op + (token.ADD - token.ADD_ASSIGN) - } - return token.ILLEGAL -} - -func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) { - var x operand - var msg string - switch check.rawExpr(&x, call, nil) { - case conversion: - msg = "requires function call, not conversion" - case expression: - msg = "discards result of" - case statement: - return - default: - unreachable() - } - check.errorf(x.pos(), "%s %s %s", keyword, msg, &x) -} - -func (check *Checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) { - // No duplicate checking for now. See issue 4524. - for _, e := range values { - var y operand - check.expr(&y, e) - if y.mode == invalid { - return - } - // TODO(gri) The convertUntyped call pair below appears in other places. Factor! - // Order matters: By comparing y against x, error positions are at the case values. - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - return - } - check.convertUntyped(&x, y.typ) - if x.mode == invalid { - return - } - check.comparison(&y, &x, token.EQL) - } -} - -func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) { -L: - for _, e := range types { - T = check.typOrNil(e) - if T == Typ[Invalid] { - continue - } - // complain about duplicate types - // TODO(gri) use a type hash to avoid quadratic algorithm - for t, pos := range seen { - if T == nil && t == nil || T != nil && t != nil && Identical(T, t) { - // talk about "case" rather than "type" because of nil case - check.error(e.Pos(), "duplicate case in type switch") - check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented - continue L - } - } - seen[T] = e.Pos() - if T != nil { - check.typeAssertion(e.Pos(), x, xtyp, T) - } - } - return -} - -// stmt typechecks statement s. -func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { - // statements cannot use iota in general - // (constant declarations set it explicitly) - assert(check.iota == nil) - - // statements must end with the same top scope as they started with - if debug { - defer func(scope *Scope) { - // don't check if code is panicking - if p := recover(); p != nil { - panic(p) - } - assert(scope == check.scope) - }(check.scope) - } - - inner := ctxt &^ fallthroughOk - switch s := s.(type) { - case *ast.BadStmt, *ast.EmptyStmt: - // ignore - - case *ast.DeclStmt: - check.declStmt(s.Decl) - - case *ast.LabeledStmt: - check.hasLabel = true - check.stmt(ctxt, s.Stmt) - - case *ast.ExprStmt: - // spec: "With the exception of specific built-in functions, - // function and method calls and receive operations can appear - // in statement context. Such statements may be parenthesized." - var x operand - kind := check.rawExpr(&x, s.X, nil) - var msg string - switch x.mode { - default: - if kind == statement { - return - } - msg = "is not used" - case builtin: - msg = "must be called" - case typexpr: - msg = "is not an expression" - } - check.errorf(x.pos(), "%s %s", &x, msg) - - case *ast.SendStmt: - var ch, x operand - check.expr(&ch, s.Chan) - check.expr(&x, s.Value) - if ch.mode == invalid || x.mode == invalid { - return - } - if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) { - if x.mode != invalid { - check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch) - } - } - - case *ast.IncDecStmt: - var op token.Token - switch s.Tok { - case token.INC: - op = token.ADD - case token.DEC: - op = token.SUB - default: - check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok) - return - } - var x operand - Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position - check.binary(&x, nil, s.X, Y, op) - if x.mode == invalid { - return - } - check.assignVar(s.X, &x) - - case *ast.AssignStmt: - switch s.Tok { - case token.ASSIGN, token.DEFINE: - if len(s.Lhs) == 0 { - check.invalidAST(s.Pos(), "missing lhs in assignment") - return - } - if s.Tok == token.DEFINE { - check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs) - } else { - // regular assignment - check.assignVars(s.Lhs, s.Rhs) - } - - default: - // assignment operations - if len(s.Lhs) != 1 || len(s.Rhs) != 1 { - check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok) - return - } - op := assignOp(s.Tok) - if op == token.ILLEGAL { - check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok) - return - } - var x operand - check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op) - if x.mode == invalid { - return - } - check.assignVar(s.Lhs[0], &x) - } - - case *ast.GoStmt: - check.suspendedCall("go", s.Call) - - case *ast.DeferStmt: - check.suspendedCall("defer", s.Call) - - case *ast.ReturnStmt: - res := check.sig.results - if res.Len() > 0 { - // function returns results - // (if one, say the first, result parameter is named, all of them are named) - if len(s.Results) == 0 && res.vars[0].name != "" { - // spec: "Implementation restriction: A compiler may disallow an empty expression - // list in a "return" statement if a different entity (constant, type, or variable) - // with the same name as a result parameter is in scope at the place of the return." - for _, obj := range res.vars { - if _, alt := check.scope.LookupParent(obj.name, check.pos); alt != nil && alt != obj { - check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name) - check.errorf(alt.Pos(), "\tinner declaration of %s", obj) - // ok to continue - } - } - } else { - // return has results or result parameters are unnamed - check.initVars(res.vars, s.Results, s.Return) - } - } else if len(s.Results) > 0 { - check.error(s.Results[0].Pos(), "no result values expected") - check.use(s.Results...) - } - - case *ast.BranchStmt: - if s.Label != nil { - check.hasLabel = true - return // checked in 2nd pass (check.labels) - } - switch s.Tok { - case token.BREAK: - if ctxt&breakOk == 0 { - check.error(s.Pos(), "break not in for, switch, or select statement") - } - case token.CONTINUE: - if ctxt&continueOk == 0 { - check.error(s.Pos(), "continue not in for statement") - } - case token.FALLTHROUGH: - if ctxt&fallthroughOk == 0 { - check.error(s.Pos(), "fallthrough statement out of place") - } - default: - check.invalidAST(s.Pos(), "branch statement: %s", s.Tok) - } - - case *ast.BlockStmt: - check.openScope(s, "block") - defer check.closeScope() - - check.stmtList(inner, s.List) - - case *ast.IfStmt: - check.openScope(s, "if") - defer check.closeScope() - - check.simpleStmt(s.Init) - var x operand - check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { - check.error(s.Cond.Pos(), "non-boolean condition in if statement") - } - check.stmt(inner, s.Body) - if s.Else != nil { - check.stmt(inner, s.Else) - } - - case *ast.SwitchStmt: - inner |= breakOk - check.openScope(s, "switch") - defer check.closeScope() - - check.simpleStmt(s.Init) - var x operand - if s.Tag != nil { - check.expr(&x, s.Tag) - } else { - // spec: "A missing switch expression is - // equivalent to the boolean value true." - x.mode = constant - x.typ = Typ[Bool] - x.val = exact.MakeBool(true) - x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"} - } - - check.multipleDefaults(s.Body.List) - - for i, c := range s.Body.List { - clause, _ := c.(*ast.CaseClause) - if clause == nil { - check.invalidAST(c.Pos(), "incorrect expression switch case") - continue - } - if x.mode != invalid { - check.caseValues(x, clause.List) - } - check.openScope(clause, "case") - inner := inner - if i+1 < len(s.Body.List) { - inner |= fallthroughOk - } - check.stmtList(inner, clause.Body) - check.closeScope() - } - - case *ast.TypeSwitchStmt: - inner |= breakOk - check.openScope(s, "type switch") - defer check.closeScope() - - check.simpleStmt(s.Init) - - // A type switch guard must be of the form: - // - // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . - // - // The parser is checking syntactic correctness; - // remaining syntactic errors are considered AST errors here. - // TODO(gri) better factoring of error handling (invalid ASTs) - // - var lhs *ast.Ident // lhs identifier or nil - var rhs ast.Expr - switch guard := s.Assign.(type) { - case *ast.ExprStmt: - rhs = guard.X - case *ast.AssignStmt: - if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 { - check.invalidAST(s.Pos(), "incorrect form of type switch guard") - return - } - - lhs, _ = guard.Lhs[0].(*ast.Ident) - if lhs == nil { - check.invalidAST(s.Pos(), "incorrect form of type switch guard") - return - } - - if lhs.Name == "_" { - // _ := x.(type) is an invalid short variable declaration - check.softErrorf(lhs.Pos(), "no new variable on left side of :=") - lhs = nil // avoid declared but not used error below - } else { - check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause - } - - rhs = guard.Rhs[0] - - default: - check.invalidAST(s.Pos(), "incorrect form of type switch guard") - return - } - - // rhs must be of the form: expr.(type) and expr must be an interface - expr, _ := rhs.(*ast.TypeAssertExpr) - if expr == nil || expr.Type != nil { - check.invalidAST(s.Pos(), "incorrect form of type switch guard") - return - } - var x operand - check.expr(&x, expr.X) - if x.mode == invalid { - return - } - xtyp, _ := x.typ.Underlying().(*Interface) - if xtyp == nil { - check.errorf(x.pos(), "%s is not an interface", &x) - return - } - - check.multipleDefaults(s.Body.List) - - var lhsVars []*Var // list of implicitly declared lhs variables - seen := make(map[Type]token.Pos) // map of seen types to positions - for _, s := range s.Body.List { - clause, _ := s.(*ast.CaseClause) - if clause == nil { - check.invalidAST(s.Pos(), "incorrect type switch case") - continue - } - // Check each type in this type switch case. - T := check.caseTypes(&x, xtyp, clause.List, seen) - check.openScope(clause, "case") - // If lhs exists, declare a corresponding variable in the case-local scope. - if lhs != nil { - // spec: "The TypeSwitchGuard may include a short variable declaration. - // When that form is used, the variable is declared at the beginning of - // the implicit block in each clause. In clauses with a case listing - // exactly one type, the variable has that type; otherwise, the variable - // has the type of the expression in the TypeSwitchGuard." - if len(clause.List) != 1 || T == nil { - T = x.typ - } - obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T) - scopePos := clause.End() - if len(clause.Body) > 0 { - scopePos = clause.Body[0].Pos() - } - check.declare(check.scope, nil, obj, scopePos) - check.recordImplicit(clause, obj) - // For the "declared but not used" error, all lhs variables act as - // one; i.e., if any one of them is 'used', all of them are 'used'. - // Collect them for later analysis. - lhsVars = append(lhsVars, obj) - } - check.stmtList(inner, clause.Body) - check.closeScope() - } - - // If lhs exists, we must have at least one lhs variable that was used. - if lhs != nil { - var used bool - for _, v := range lhsVars { - if v.used { - used = true - } - v.used = true // avoid usage error when checking entire function - } - if !used { - check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name) - } - } - - case *ast.SelectStmt: - inner |= breakOk - - check.multipleDefaults(s.Body.List) - - for _, s := range s.Body.List { - clause, _ := s.(*ast.CommClause) - if clause == nil { - continue // error reported before - } - - // clause.Comm must be a SendStmt, RecvStmt, or default case - valid := false - var rhs ast.Expr // rhs of RecvStmt, or nil - switch s := clause.Comm.(type) { - case nil, *ast.SendStmt: - valid = true - case *ast.AssignStmt: - if len(s.Rhs) == 1 { - rhs = s.Rhs[0] - } - case *ast.ExprStmt: - rhs = s.X - } - - // if present, rhs must be a receive operation - if rhs != nil { - if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW { - valid = true - } - } - - if !valid { - check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)") - continue - } - - check.openScope(s, "case") - if clause.Comm != nil { - check.stmt(inner, clause.Comm) - } - check.stmtList(inner, clause.Body) - check.closeScope() - } - - case *ast.ForStmt: - inner |= breakOk | continueOk - check.openScope(s, "for") - defer check.closeScope() - - check.simpleStmt(s.Init) - if s.Cond != nil { - var x operand - check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { - check.error(s.Cond.Pos(), "non-boolean condition in for statement") - } - } - check.simpleStmt(s.Post) - // spec: "The init statement may be a short variable - // declaration, but the post statement must not." - if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE { - check.softErrorf(s.Pos(), "cannot declare in post statement") - check.use(s.Lhs...) // avoid follow-up errors - } - check.stmt(inner, s.Body) - - case *ast.RangeStmt: - inner |= breakOk | continueOk - check.openScope(s, "for") - defer check.closeScope() - - // check expression to iterate over - var x operand - check.expr(&x, s.X) - - // determine key/value types - var key, val Type - if x.mode != invalid { - switch typ := x.typ.Underlying().(type) { - case *Basic: - if isString(typ) { - key = Typ[Int] - val = universeRune // use 'rune' name - } - case *Array: - key = Typ[Int] - val = typ.elem - case *Slice: - key = Typ[Int] - val = typ.elem - case *Pointer: - if typ, _ := typ.base.Underlying().(*Array); typ != nil { - key = Typ[Int] - val = typ.elem - } - case *Map: - key = typ.key - val = typ.elem - case *Chan: - key = typ.elem - val = Typ[Invalid] - if typ.dir == SendOnly { - check.errorf(x.pos(), "cannot range over send-only channel %s", &x) - // ok to continue - } - if s.Value != nil { - check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x) - // ok to continue - } - } - } - - if key == nil { - check.errorf(x.pos(), "cannot range over %s", &x) - // ok to continue - } - - // check assignment to/declaration of iteration variables - // (irregular assignment, cannot easily map to existing assignment checks) - - // lhs expressions and initialization value (rhs) types - lhs := [2]ast.Expr{s.Key, s.Value} - rhs := [2]Type{key, val} // key, val may be nil - - if s.Tok == token.DEFINE { - // short variable declaration; variable scope starts after the range clause - // (the for loop opens a new scope, so variables on the lhs never redeclare - // previously declared variables) - var vars []*Var - for i, lhs := range lhs { - if lhs == nil { - continue - } - - // determine lhs variable - var obj *Var - if ident, _ := lhs.(*ast.Ident); ident != nil { - // declare new variable - name := ident.Name - obj = NewVar(ident.Pos(), check.pkg, name, nil) - check.recordDef(ident, obj) - // _ variables don't count as new variables - if name != "_" { - vars = append(vars, obj) - } - } else { - check.errorf(lhs.Pos(), "cannot declare %s", lhs) - obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable - } - - // initialize lhs variable - if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.initVar(obj, &x, false) - } else { - obj.typ = Typ[Invalid] - obj.used = true // don't complain about unused variable - } - } - - // declare variables - if len(vars) > 0 { - for _, obj := range vars { - // spec: "The scope of a constant or variable identifier declared inside - // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl - // for short variable declarations) and ends at the end of the innermost - // containing block." - scopePos := s.End() - check.declare(check.scope, nil /* recordDef already called */, obj, scopePos) - } - } else { - check.error(s.TokPos, "no new variables on left side of :=") - } - } else { - // ordinary assignment - for i, lhs := range lhs { - if lhs == nil { - continue - } - if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.assignVar(lhs, &x) - } - } - } - - check.stmt(inner, s.Body) - - default: - check.error(s.Pos(), "invalid statement") - } -} diff --git a/go/types/testdata/blank.src b/go/types/testdata/blank.src deleted file mode 100644 index 6a2507f482..0000000000 --- a/go/types/testdata/blank.src +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2014 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 _ /* ERROR invalid package name */ diff --git a/go/types/testdata/builtins.src b/go/types/testdata/builtins.src deleted file mode 100644 index 9eb551dc10..0000000000 --- a/go/types/testdata/builtins.src +++ /dev/null @@ -1,881 +0,0 @@ -// Copyright 2012 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. - -// builtin calls - -package builtins - -import "unsafe" - -func f0() {} - -func append1() { - var b byte - var x int - var s []byte - _ = append() // ERROR not enough arguments - _ = append("foo" /* ERROR not a slice */ ) - _ = append(nil /* ERROR not a slice */ , s) - _ = append(x /* ERROR not a slice */ , s) - _ = append(s) - append /* ERROR not used */ (s) - - _ = append(s, b) - _ = append(s, x /* ERROR cannot pass argument x */ ) - _ = append(s, s /* ERROR cannot pass argument s */ ) - _ = append(s... /* ERROR can only use ... with matching parameter */ ) - _ = append(s, b, s... /* ERROR can only use ... with matching parameter */ ) - _ = append(s, 1, 2, 3) - _ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6) - _ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ ) - _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) - - type S []byte - type T string - var t T - _ = append(s, "foo" /* ERROR cannot convert */ ) - _ = append(s, "foo"...) - _ = append(S(s), "foo" /* ERROR cannot convert */ ) - _ = append(S(s), "foo"...) - _ = append(s, t /* ERROR cannot pass argument t */ ) - _ = append(s, t...) - _ = append(s, T("foo")...) - _ = append(S(s), t /* ERROR cannot pass argument t */ ) - _ = append(S(s), t...) - _ = append(S(s), T("foo")...) - _ = append([]string{}, t /* ERROR cannot pass argument t */ , "foo") - _ = append([]T{}, t, "foo") -} - -// from the spec -func append2() { - s0 := []int{0, 0} - s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2} - s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7} - s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} - s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} - - var t []interface{} - t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} - - var b []byte - b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' } - - _ = s4 -} - -func append3() { - f1 := func() (s []int) { return } - f2 := func() (s []int, x int) { return } - f3 := func() (s []int, x, y int) { return } - f5 := func() (s []interface{}, x int, y float32, z string, b bool) { return } - ff := func() (int, float32) { return 0, 0 } - _ = append(f0 /* ERROR used as value */ ()) - _ = append(f1()) - _ = append(f2()) - _ = append(f3()) - _ = append(f5()) - _ = append(ff /* ERROR not a slice */ ()) // TODO(gri) better error message -} - -func cap1() { - var a [10]bool - var p *[20]int - var c chan string - _ = cap() // ERROR not enough arguments - _ = cap(1, 2) // ERROR too many arguments - _ = cap(42 /* ERROR invalid */) - const _3 = cap(a) - assert(_3 == 10) - const _4 = cap(p) - assert(_4 == 20) - _ = cap(c) - cap /* ERROR not used */ (c) - - // issue 4744 - type T struct{ a [10]int } - const _ = cap(((*T)(nil)).a) - - var s [][]byte - _ = cap(s) - _ = cap(s... /* ERROR invalid use of \.\.\. */ ) -} - -func cap2() { - f1a := func() (a [10]int) { return } - f1s := func() (s []int) { return } - f2 := func() (s []int, x int) { return } - _ = cap(f0 /* ERROR used as value */ ()) - _ = cap(f1a()) - _ = cap(f1s()) - _ = cap(f2()) // ERROR too many arguments -} - -// test cases for issue 7387 -func cap3() { - var f = func() int { return 0 } - var x = f() - const ( - _ = cap([4]int{}) - _ = cap([4]int{x}) - _ = cap /* ERROR not constant */ ([4]int{f()}) - _ = cap /* ERROR not constant */ ([4]int{cap([]int{})}) - _ = cap([4]int{cap([4]int{})}) - ) - var y float64 - var z complex128 - const ( - _ = cap([4]float64{}) - _ = cap([4]float64{y}) - _ = cap([4]float64{real(2i)}) - _ = cap /* ERROR not constant */ ([4]float64{real(z)}) - ) - var ch chan [10]int - const ( - _ = cap /* ERROR not constant */ (<-ch) - _ = cap /* ERROR not constant */ ([4]int{(<-ch)[0]}) - ) -} - -func close1() { - var c chan int - var r <-chan int - close() // ERROR not enough arguments - close(1, 2) // ERROR too many arguments - close(42 /* ERROR not a channel */) - close(r /* ERROR receive-only channel */) - close(c) - _ = close /* ERROR used as value */ (c) - - var s []chan int - close(s... /* ERROR invalid use of \.\.\. */ ) -} - -func close2() { - f1 := func() (ch chan int) { return } - f2 := func() (ch chan int, x int) { return } - close(f0 /* ERROR used as value */ ()) - close(f1()) - close(f2()) // ERROR too many arguments -} - -func complex1() { - var i32 int32 - var f32 float32 - var f64 float64 - var c64 complex64 - var c128 complex128 - _ = complex() // ERROR not enough arguments - _ = complex(1) // ERROR not enough arguments - _ = complex(true /* ERROR invalid argument */ , 0) - _ = complex(i32 /* ERROR invalid argument */ , 0) - _ = complex("foo" /* ERROR invalid argument */ , 0) - _ = complex(c64 /* ERROR invalid argument */ , 0) - _ = complex(0, true /* ERROR invalid argument */ ) - _ = complex(0, i32 /* ERROR invalid argument */ ) - _ = complex(0, "foo" /* ERROR invalid argument */ ) - _ = complex(0, c64 /* ERROR invalid argument */ ) - _ = complex(f32, f32) - _ = complex(f32, 1) - _ = complex(f32, 1.0) - _ = complex(f32, 'a') - _ = complex(f64, f64) - _ = complex(f64, 1) - _ = complex(f64, 1.0) - _ = complex(f64, 'a') - _ = complex(f32 /* ERROR mismatched types */ , f64) - _ = complex(f64 /* ERROR mismatched types */ , f32) - _ = complex(1, 1) - _ = complex(1, 1.1) - _ = complex(1, 'a') - complex /* ERROR not used */ (1, 2) - - var _ complex64 = complex(f32, f32) - var _ complex64 = complex /* ERROR cannot initialize */ (f64, f64) - - var _ complex128 = complex /* ERROR cannot initialize */ (f32, f32) - var _ complex128 = complex(f64, f64) - - // untyped constants - const _ int = complex(1, 0) - const _ float32 = complex(1, 0) - const _ complex64 = complex(1, 0) - const _ complex128 = complex(1, 0) - - const _ int = complex /* ERROR int */ (1.1, 0) - const _ float32 = complex /* ERROR float32 */ (1, 2) - - // untyped values - var s uint - _ = complex(1 /* ERROR integer */ <>8&1 + mi>>16&1 + mi>>32&1) - logSizeofUint = uint(mu>>8&1 + mu>>16&1 + mu>>32&1) - logSizeofUintptr = uint(mp>>8&1 + mp>>16&1 + mp>>32&1) -) - -const ( - minInt8 = -1<<(8< 0) - _ = assert(smallestFloat64 > 0) -) - -const ( - maxFloat32 = 1<<127 * (1<<24 - 1) / (1.0<<23) - maxFloat64 = 1<<1023 * (1<<53 - 1) / (1.0<<52) -) - -const ( - _ int8 = minInt8 /* ERROR "overflows" */ - 1 - _ int8 = minInt8 - _ int8 = maxInt8 - _ int8 = maxInt8 /* ERROR "overflows" */ + 1 - _ int8 = smallestFloat64 /* ERROR "truncated" */ - - _ = int8(minInt8 /* ERROR "cannot convert" */ - 1) - _ = int8(minInt8) - _ = int8(maxInt8) - _ = int8(maxInt8 /* ERROR "cannot convert" */ + 1) - _ = int8(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ int16 = minInt16 /* ERROR "overflows" */ - 1 - _ int16 = minInt16 - _ int16 = maxInt16 - _ int16 = maxInt16 /* ERROR "overflows" */ + 1 - _ int16 = smallestFloat64 /* ERROR "truncated" */ - - _ = int16(minInt16 /* ERROR "cannot convert" */ - 1) - _ = int16(minInt16) - _ = int16(maxInt16) - _ = int16(maxInt16 /* ERROR "cannot convert" */ + 1) - _ = int16(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ int32 = minInt32 /* ERROR "overflows" */ - 1 - _ int32 = minInt32 - _ int32 = maxInt32 - _ int32 = maxInt32 /* ERROR "overflows" */ + 1 - _ int32 = smallestFloat64 /* ERROR "truncated" */ - - _ = int32(minInt32 /* ERROR "cannot convert" */ - 1) - _ = int32(minInt32) - _ = int32(maxInt32) - _ = int32(maxInt32 /* ERROR "cannot convert" */ + 1) - _ = int32(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ int64 = minInt64 /* ERROR "overflows" */ - 1 - _ int64 = minInt64 - _ int64 = maxInt64 - _ int64 = maxInt64 /* ERROR "overflows" */ + 1 - _ int64 = smallestFloat64 /* ERROR "truncated" */ - - _ = int64(minInt64 /* ERROR "cannot convert" */ - 1) - _ = int64(minInt64) - _ = int64(maxInt64) - _ = int64(maxInt64 /* ERROR "cannot convert" */ + 1) - _ = int64(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ int = minInt /* ERROR "overflows" */ - 1 - _ int = minInt - _ int = maxInt - _ int = maxInt /* ERROR "overflows" */ + 1 - _ int = smallestFloat64 /* ERROR "truncated" */ - - _ = int(minInt /* ERROR "cannot convert" */ - 1) - _ = int(minInt) - _ = int(maxInt) - _ = int(maxInt /* ERROR "cannot convert" */ + 1) - _ = int(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uint8 = 0 /* ERROR "overflows" */ - 1 - _ uint8 = 0 - _ uint8 = maxUint8 - _ uint8 = maxUint8 /* ERROR "overflows" */ + 1 - _ uint8 = smallestFloat64 /* ERROR "truncated" */ - - _ = uint8(0 /* ERROR "cannot convert" */ - 1) - _ = uint8(0) - _ = uint8(maxUint8) - _ = uint8(maxUint8 /* ERROR "cannot convert" */ + 1) - _ = uint8(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uint16 = 0 /* ERROR "overflows" */ - 1 - _ uint16 = 0 - _ uint16 = maxUint16 - _ uint16 = maxUint16 /* ERROR "overflows" */ + 1 - _ uint16 = smallestFloat64 /* ERROR "truncated" */ - - _ = uint16(0 /* ERROR "cannot convert" */ - 1) - _ = uint16(0) - _ = uint16(maxUint16) - _ = uint16(maxUint16 /* ERROR "cannot convert" */ + 1) - _ = uint16(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uint32 = 0 /* ERROR "overflows" */ - 1 - _ uint32 = 0 - _ uint32 = maxUint32 - _ uint32 = maxUint32 /* ERROR "overflows" */ + 1 - _ uint32 = smallestFloat64 /* ERROR "truncated" */ - - _ = uint32(0 /* ERROR "cannot convert" */ - 1) - _ = uint32(0) - _ = uint32(maxUint32) - _ = uint32(maxUint32 /* ERROR "cannot convert" */ + 1) - _ = uint32(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uint64 = 0 /* ERROR "overflows" */ - 1 - _ uint64 = 0 - _ uint64 = maxUint64 - _ uint64 = maxUint64 /* ERROR "overflows" */ + 1 - _ uint64 = smallestFloat64 /* ERROR "truncated" */ - - _ = uint64(0 /* ERROR "cannot convert" */ - 1) - _ = uint64(0) - _ = uint64(maxUint64) - _ = uint64(maxUint64 /* ERROR "cannot convert" */ + 1) - _ = uint64(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uint = 0 /* ERROR "overflows" */ - 1 - _ uint = 0 - _ uint = maxUint - _ uint = maxUint /* ERROR "overflows" */ + 1 - _ uint = smallestFloat64 /* ERROR "truncated" */ - - _ = uint(0 /* ERROR "cannot convert" */ - 1) - _ = uint(0) - _ = uint(maxUint) - _ = uint(maxUint /* ERROR "cannot convert" */ + 1) - _ = uint(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ uintptr = 0 /* ERROR "overflows" */ - 1 - _ uintptr = 0 - _ uintptr = maxUintptr - _ uintptr = maxUintptr /* ERROR "overflows" */ + 1 - _ uintptr = smallestFloat64 /* ERROR "truncated" */ - - _ = uintptr(0 /* ERROR "cannot convert" */ - 1) - _ = uintptr(0) - _ = uintptr(maxUintptr) - _ = uintptr(maxUintptr /* ERROR "cannot convert" */ + 1) - _ = uintptr(smallestFloat64 /* ERROR "cannot convert" */) -) - -const ( - _ float32 = minInt64 - _ float64 = minInt64 - _ complex64 = minInt64 - _ complex128 = minInt64 - - _ = float32(minInt64) - _ = float64(minInt64) - _ = complex64(minInt64) - _ = complex128(minInt64) -) - -const ( - _ float32 = maxUint64 - _ float64 = maxUint64 - _ complex64 = maxUint64 - _ complex128 = maxUint64 - - _ = float32(maxUint64) - _ = float64(maxUint64) - _ = complex64(maxUint64) - _ = complex128(maxUint64) -) - -// TODO(gri) find smaller deltas below - -const delta32 = maxFloat32/(1 << 23) - -const ( - _ float32 = - /* ERROR "overflow" */ (maxFloat32 + delta32) - _ float32 = -maxFloat32 - _ float32 = maxFloat32 - _ float32 = maxFloat32 /* ERROR "overflow" */ + delta32 - - _ = float32(- /* ERROR "cannot convert" */ (maxFloat32 + delta32)) - _ = float32(-maxFloat32) - _ = float32(maxFloat32) - _ = float32(maxFloat32 /* ERROR "cannot convert" */ + delta32) - - _ = assert(float32(smallestFloat32) == smallestFloat32) - _ = assert(float32(smallestFloat32/2) == 0) - _ = assert(float32(smallestFloat64) == 0) - _ = assert(float32(smallestFloat64/2) == 0) -) - -const delta64 = maxFloat64/(1 << 52) - -const ( - _ float64 = - /* ERROR "overflow" */ (maxFloat64 + delta64) - _ float64 = -maxFloat64 - _ float64 = maxFloat64 - _ float64 = maxFloat64 /* ERROR "overflow" */ + delta64 - - _ = float64(- /* ERROR "cannot convert" */ (maxFloat64 + delta64)) - _ = float64(-maxFloat64) - _ = float64(maxFloat64) - _ = float64(maxFloat64 /* ERROR "cannot convert" */ + delta64) - - _ = assert(float64(smallestFloat32) == smallestFloat32) - _ = assert(float64(smallestFloat32/2) == smallestFloat32/2) - _ = assert(float64(smallestFloat64) == smallestFloat64) - _ = assert(float64(smallestFloat64/2) == 0) -) - -const ( - _ complex64 = - /* ERROR "overflow" */ (maxFloat32 + delta32) - _ complex64 = -maxFloat32 - _ complex64 = maxFloat32 - _ complex64 = maxFloat32 /* ERROR "overflow" */ + delta32 - - _ = complex64(- /* ERROR "cannot convert" */ (maxFloat32 + delta32)) - _ = complex64(-maxFloat32) - _ = complex64(maxFloat32) - _ = complex64(maxFloat32 /* ERROR "cannot convert" */ + delta32) -) - -const ( - _ complex128 = - /* ERROR "overflow" */ (maxFloat64 + delta64) - _ complex128 = -maxFloat64 - _ complex128 = maxFloat64 - _ complex128 = maxFloat64 /* ERROR "overflow" */ + delta64 - - _ = complex128(- /* ERROR "cannot convert" */ (maxFloat64 + delta64)) - _ = complex128(-maxFloat64) - _ = complex128(maxFloat64) - _ = complex128(maxFloat64 /* ERROR "cannot convert" */ + delta64) -) - -// Initialization of typed constant and conversion are the same: -const ( - f32 = 1 + smallestFloat32 - x32 float32 = f32 - y32 = float32(f32) - _ = assert(x32 - y32 == 0) -) - -const ( - f64 = 1 + smallestFloat64 - x64 float64 = f64 - y64 = float64(f64) - _ = assert(x64 - y64 == 0) -) diff --git a/go/types/testdata/constdecl.src b/go/types/testdata/constdecl.src deleted file mode 100644 index 6de9b13d6e..0000000000 --- a/go/types/testdata/constdecl.src +++ /dev/null @@ -1,97 +0,0 @@ -// 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 constdecl - -import "math" - -var v int - -// Const decls must be initialized by constants. -const _ = v /* ERROR "not constant" */ -const _ = math /* ERROR "not constant" */ .Sin(0) -const _ = int /* ERROR "not an expression" */ - -func _() { - const _ = v /* ERROR "not constant" */ - const _ = math /* ERROR "not constant" */ .Sin(0) - const _ = int /* ERROR "not an expression" */ -} - -// Identifier and expression arity must match. -// The first error message is produced by the parser. -// In a real-world scenario, the type-checker would not be run -// in this case and the 2nd error message would not appear. -const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ -const _ = 1, 2 /* ERROR "extra init expr 2" */ - -const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int -const _ int = 1, 2 /* ERROR "extra init expr 2" */ - -const ( - _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ - _ = 1, 2 /* ERROR "extra init expr 2" */ - - _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int - _ int = 1, 2 /* ERROR "extra init expr 2" */ -) - -const ( - _ = 1 - _ - _, _ /* ERROR "missing init expr for _" */ - _ -) - -const ( - _, _ = 1, 2 - _, _ - _ /* ERROR "extra init expr at" */ - _, _ - _, _, _ /* ERROR "missing init expr for _" */ - _, _ -) - -func _() { - const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ - const _ = 1, 2 /* ERROR "extra init expr 2" */ - - const _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int - const _ int = 1, 2 /* ERROR "extra init expr 2" */ - - const ( - _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ - _ = 1, 2 /* ERROR "extra init expr 2" */ - - _ /* ERROR "missing constant value" */ /* ERROR "missing init expr for _" */ int - _ int = 1, 2 /* ERROR "extra init expr 2" */ - ) - - const ( - _ = 1 - _ - _, _ /* ERROR "missing init expr for _" */ - _ - ) - - const ( - _, _ = 1, 2 - _, _ - _ /* ERROR "extra init expr at" */ - _, _ - _, _, _ /* ERROR "missing init expr for _" */ - _, _ - ) -} - -// Test case for constant with invalid initialization. -// Caused panic because the constant value was not set up (gri - 7/8/2014). -func _() { - const ( - x string = missing /* ERROR "undeclared name" */ - y = x + "" - ) -} - -// TODO(gri) move extra tests from testdata/const0.src into here diff --git a/go/types/testdata/conversions.src b/go/types/testdata/conversions.src deleted file mode 100644 index e1336c0456..0000000000 --- a/go/types/testdata/conversions.src +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2012 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. - -// conversions - -package conversions - -import "unsafe" - -// argument count -var ( - _ = int() /* ERROR "missing argument" */ - _ = int(1, 2 /* ERROR "too many arguments" */ ) -) - -// numeric constant conversions are in const1.src. - -func string_conversions() { - const A = string(65) - assert(A == "A") - const E = string(-1) - assert(E == "\uFFFD") - assert(E == string(1234567890)) - - type myint int - assert(A == string(myint(65))) - - type mystring string - const _ mystring = mystring("foo") - - const _ = string(true /* ERROR "cannot convert" */ ) - const _ = string(1.2 /* ERROR "cannot convert" */ ) - const _ = string(nil /* ERROR "cannot convert" */ ) - - // issues 11357, 11353: argument must be of integer type - _ = string(0.0 /* ERROR "cannot convert" */ ) - _ = string(0i /* ERROR "cannot convert" */ ) - _ = string(1 /* ERROR "cannot convert" */ + 2i) -} - -func interface_conversions() { - type E interface{} - - type I1 interface{ - m1() - } - - type I2 interface{ - m1() - m2(x int) - } - - type I3 interface{ - m1() - m2() int - } - - var e E - var i1 I1 - var i2 I2 - var i3 I3 - - _ = E(0) - _ = E(nil) - _ = E(e) - _ = E(i1) - _ = E(i2) - - _ = I1(0 /* ERROR "cannot convert" */ ) - _ = I1(nil) - _ = I1(i1) - _ = I1(e /* ERROR "cannot convert" */ ) - _ = I1(i2) - - _ = I2(nil) - _ = I2(i1 /* ERROR "cannot convert" */ ) - _ = I2(i2) - _ = I2(i3 /* ERROR "cannot convert" */ ) - - _ = I3(nil) - _ = I3(i1 /* ERROR "cannot convert" */ ) - _ = I3(i2 /* ERROR "cannot convert" */ ) - _ = I3(i3) - - // TODO(gri) add more tests, improve error message -} - -func issue6326() { - type T unsafe.Pointer - var x T - _ = uintptr(x) // see issue 6326 -} diff --git a/go/types/testdata/cycles.src b/go/types/testdata/cycles.src deleted file mode 100644 index 621d83c945..0000000000 --- a/go/types/testdata/cycles.src +++ /dev/null @@ -1,143 +0,0 @@ -// 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 cycles - -type ( - T0 int - T1 /* ERROR cycle */ T1 - T2 *T2 - - T3 /* ERROR cycle */ T4 - T4 T5 - T5 T3 - - T6 T7 - T7 *T8 - T8 T6 - - // arrays - A0 /* ERROR cycle */ [10]A0 - A1 [10]*A1 - - A2 /* ERROR cycle */ [10]A3 - A3 [10]A4 - A4 A2 - - A5 [10]A6 - A6 *A5 - - // slices - L0 []L0 - - // structs - S0 /* ERROR cycle */ struct{ _ S0 } - S1 /* ERROR cycle */ struct{ S1 } - S2 struct{ _ *S2 } - S3 struct{ *S3 } - - S4 /* ERROR cycle */ struct{ S5 } - S5 struct{ S6 } - S6 S4 - - // pointers - P0 *P0 - - // functions - F0 func(F0) - F1 func() F1 - F2 func(F2) F2 - - // interfaces - I0 /* ERROR cycle */ interface{ I0 } - - I1 interface{ I2 } - I2 interface{ I3 } - I3 /* ERROR cycle */ interface{ I1 } - - I4 interface{ f(I4) } - - // testcase for issue 5090 - I5 interface{ f(I6) } - I6 interface{ I5 } - - // maps - M0 map[M0 /* ERROR invalid map key */ ]M0 - - // channels - C0 chan C0 -) - -func _() { - type ( - t1 /* ERROR cycle */ t1 - t2 *t2 - - t3 t4 /* ERROR undeclared */ - t4 t5 /* ERROR undeclared */ - t5 t3 - - // arrays - a0 /* ERROR cycle */ [10]a0 - a1 [10]*a1 - - // slices - l0 []l0 - - // structs - s0 /* ERROR cycle */ struct{ _ s0 } - s1 /* ERROR cycle */ struct{ s1 } - s2 struct{ _ *s2 } - s3 struct{ *s3 } - - // pointers - p0 *p0 - - // functions - f0 func(f0) - f1 func() f1 - f2 func(f2) f2 - - // interfaces - i0 /* ERROR cycle */ interface{ i0 } - - // maps - m0 map[m0 /* ERROR invalid map key */ ]m0 - - // channels - c0 chan c0 - ) -} - -// test cases for issue 6667 - -type A [10]map[A /* ERROR invalid map key */ ]bool - -type S struct { - m map[S /* ERROR invalid map key */ ]bool -} - -// test cases for issue 7236 -// (cycle detection must not be dependent on starting point of resolution) - -type ( - P1 *T9 - T9 /* ERROR cycle */ T9 - - T10 /* ERROR cycle */ T10 - P2 *T10 -) - -func (T11) m() {} - -type T11 /* ERROR cycle */ struct{ T11 } - -type T12 /* ERROR cycle */ struct{ T12 } - -func (*T12) m() {} - -type ( - P3 *T13 - T13 /* ERROR cycle */ T13 -) \ No newline at end of file diff --git a/go/types/testdata/cycles1.src b/go/types/testdata/cycles1.src deleted file mode 100644 index ae2b38ebec..0000000000 --- a/go/types/testdata/cycles1.src +++ /dev/null @@ -1,77 +0,0 @@ -// 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 p - -type ( - A interface { - a() interface { - ABC1 - } - } - B interface { - b() interface { - ABC2 - } - } - C interface { - c() interface { - ABC3 - } - } - - AB interface { - A - B - } - BC interface { - B - C - } - - ABC1 interface { - A - B - C - } - ABC2 interface { - AB - C - } - ABC3 interface { - A - BC - } -) - -var ( - x1 ABC1 - x2 ABC2 - x3 ABC3 -) - -func _() { - // all types have the same method set - x1 = x2 - x2 = x1 - - x1 = x3 - x3 = x1 - - x2 = x3 - x3 = x2 - - // all methods return the same type again - x1 = x1.a() - x1 = x1.b() - x1 = x1.c() - - x2 = x2.a() - x2 = x2.b() - x2 = x2.c() - - x3 = x3.a() - x3 = x3.b() - x3 = x3.c() -} diff --git a/go/types/testdata/cycles2.src b/go/types/testdata/cycles2.src deleted file mode 100644 index 345ab56ea6..0000000000 --- a/go/types/testdata/cycles2.src +++ /dev/null @@ -1,118 +0,0 @@ -// 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 p - -import "unsafe" - -// Test case for issue 5090 - -type t interface { - f(u) -} - -type u interface { - t -} - -func _() { - var t t - var u u - - t.f(t) - t.f(u) - - u.f(t) - u.f(u) -} - - -// Test case for issue 6589. - -type A interface { - a() interface { - AB - } -} - -type B interface { - a() interface { - AB - } -} - -type AB interface { - a() interface { - A - B /* ERROR a redeclared */ - } - b() interface { - A - B /* ERROR a redeclared */ - } -} - -var x AB -var y interface { - A - B /* ERROR a redeclared */ -} -var _ = x /* ERROR cannot compare */ == y - - -// Test case for issue 6638. - -type T interface { - m() [T /* ERROR no value */ (nil).m()[0]]int -} - -// Variations of this test case. - -type T1 interface { - m() [x1 /* ERROR no value */ .m()[0]]int -} - -var x1 T1 - -type T2 interface { - m() [len(x2 /* ERROR no value */ .m())]int -} - -var x2 T2 - -type T3 interface { - m() [unsafe.Sizeof(x3.m)]int -} - -var x3 T3 - -// The test case below should also report an error for -// the cast inside the T4 interface (like it does for the -// variable initialization). The reason why it does not is -// that inside T4, the method x4.m depends on T4 which is not -// fully set up yet. The x4.m method happens to have an empty -// signature which is why the cast is permitted. -// TODO(gri) Consider marking methods as incomplete and provide -// a better error message in that case. - -type T4 interface { - m() [unsafe.Sizeof(cast4(x4.m))]int -} - -var x4 T4 -var _ = cast4(x4 /* ERROR cannot convert */.m) - -type cast4 func() - -// This test is symmetric to the T4 case: Here the cast is -// "correct", but it doesn't work inside the T5 interface. - -type T5 interface { - m() [unsafe.Sizeof(cast5(x5 /* ERROR cannot convert */ .m))]int -} - -var x5 T5 -var _ = cast5(x5.m) - -type cast5 func() [0]int diff --git a/go/types/testdata/cycles3.src b/go/types/testdata/cycles3.src deleted file mode 100644 index 3da4fb5761..0000000000 --- a/go/types/testdata/cycles3.src +++ /dev/null @@ -1,60 +0,0 @@ -// 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 p - -import "unsafe" - -var ( - _ A = A(nil).a().b().c().d().e().f() - _ A = A(nil).b().c().d().e().f() - _ A = A(nil).c().d().e().f() - _ A = A(nil).d().e().f() - _ A = A(nil).e().f() - _ A = A(nil).f() - _ A = A(nil) -) - -type ( - A interface { - a() B - B - } - - B interface { - b() C - C - } - - C interface { - c() D - D - } - - D interface { - d() E - E - } - - E interface { - e() F - F - } - - F interface { - f() A - } -) - -type ( - U interface { - V - } - - V interface { - v() [unsafe.Sizeof(u)]int - } -) - -var u U diff --git a/go/types/testdata/cycles4.src b/go/types/testdata/cycles4.src deleted file mode 100644 index 445babca68..0000000000 --- a/go/types/testdata/cycles4.src +++ /dev/null @@ -1,110 +0,0 @@ -// 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 p - -// Check that all methods of T are collected before -// determining the result type of m (which embeds -// all methods of T). - -type T interface { - m() interface {T} - E -} - -var _ = T.m(nil).m().e() - -type E interface { - e() int -} - -// Check that unresolved forward chains are followed -// (see also comment in resolver.go, checker.typeDecl). - -var _ = C.m(nil).m().e() - -type A B - -type B interface { - m() interface{C} - E -} - -type C A - -// Check that interface type comparison for identity -// does not recur endlessly. - -type T1 interface { - m() interface{T1} -} - -type T2 interface { - m() interface{T2} -} - -func _(x T1, y T2) { - // Checking for assignability of interfaces must check - // if all methods of x are present in y, and that they - // have identical signatures. The signatures recur via - // the result type, which is an interface that embeds - // a single method m that refers to the very interface - // that contains it. This requires cycle detection in - // identity checks for interface types. - x = y -} - -type T3 interface { - m() interface{T4} -} - -type T4 interface { - m() interface{T3} -} - -func _(x T1, y T3) { - x = y -} - -// Check that interfaces are type-checked in order of -// (embedded interface) dependencies (was issue 7158). - -var x1 T5 = T7(nil) - -type T5 interface { - T6 -} - -type T6 interface { - m() T7 -} -type T7 interface { - T5 -} - -// Actual test case from issue 7158. - -func wrapNode() Node { - return wrapElement() -} - -func wrapElement() Element { - return nil -} - -type EventTarget interface { - AddEventListener(Event) -} - -type Node interface { - EventTarget -} - -type Element interface { - Node -} - -type Event interface { - Target() Element -} diff --git a/go/types/testdata/decls0.src b/go/types/testdata/decls0.src deleted file mode 100644 index 21baafe279..0000000000 --- a/go/types/testdata/decls0.src +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2011 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. - -// type declarations - -package decls0 - -import "unsafe" - -const pi = 3.1415 - -type ( - N undeclared /* ERROR "undeclared" */ - B bool - I int32 - A [10]P - T struct { - x, y P - } - P *T - R (*R) - F func(A) I - Y interface { - f(A) I - } - S [](((P))) - M map[I]F - C chan<- I - - // blank types must be typechecked - _ pi /* ERROR "not a type" */ - _ struct{} - _ struct{ pi /* ERROR "not a type" */ } -) - - -// declarations of init -const _, init /* ERROR "cannot declare init" */ , _ = 0, 1, 2 -type init /* ERROR "cannot declare init" */ struct{} -var _, init /* ERROR "cannot declare init" */ int - -func init() {} -func init /* ERROR "missing function body" */ () - -func _() { const init = 0 } -func _() { type init int } -func _() { var init int; _ = init } - -// invalid array types -type ( - iA0 [... /* ERROR "invalid use of '...'" */ ]byte - iA1 [1 /* ERROR "invalid array length" */ <<100]int - iA2 [- /* ERROR "invalid array length" */ 1]complex128 - iA3 ["foo" /* ERROR "must be integer" */ ]string - iA4 [float64 /* ERROR "must be integer" */ (0)]int -) - - -type ( - p1 pi /* ERROR "no field or method foo" */ .foo - p2 unsafe.Pointer -) - - -type ( - Pi pi /* ERROR "not a type" */ - - a /* ERROR "illegal cycle" */ a - a /* ERROR "redeclared" */ int - - // where the cycle error appears depends on the - // order in which declarations are processed - // (which depends on the order in which a map - // is iterated through) - b /* ERROR "illegal cycle" */ c - c d - d e - e b - - t *t - - U V - V *W - W U - - P1 *S2 - P2 P1 - - S0 struct { - } - S1 struct { - a, b, c int - u, v, a /* ERROR "redeclared" */ float32 - } - S2 struct { - S0 // anonymous field - S0 /* ERROR "redeclared" */ int - } - S3 struct { - x S2 - } - S4/* ERROR "illegal cycle" */ struct { - S4 - } - S5 /* ERROR "illegal cycle" */ struct { - S6 - } - S6 struct { - field S7 - } - S7 struct { - S5 - } - - L1 []L1 - L2 []int - - A1 [10.0]int - A2 /* ERROR "illegal cycle" */ [10]A2 - A3 /* ERROR "illegal cycle" */ [10]struct { - x A4 - } - A4 [10]A3 - - F1 func() - F2 func(x, y, z float32) - F3 func(x, y, x /* ERROR "redeclared" */ float32) - F4 func() (x, y, x /* ERROR "redeclared" */ float32) - F5 func(x int) (x /* ERROR "redeclared" */ float32) - F6 func(x ...int) - - I1 interface{} - I2 interface { - m1() - } - I3 interface { - m1() - m1 /* ERROR "redeclared" */ () - } - I4 interface { - m1(x, y, x /* ERROR "redeclared" */ float32) - m2() (x, y, x /* ERROR "redeclared" */ float32) - m3(x int) (x /* ERROR "redeclared" */ float32) - } - I5 interface { - m1(I5) - } - I6 interface { - S0 /* ERROR "not an interface" */ - } - I7 interface { - I1 - I1 - } - I8 /* ERROR "illegal cycle" */ interface { - I8 - } - I9 interface { - I10 - } - I10 interface { - I11 - } - I11 /* ERROR "illegal cycle" */ interface { - I9 - } - - C1 chan int - C2 <-chan int - C3 chan<- C3 - C4 chan C5 - C5 chan C6 - C6 chan C4 - - M1 map[Last]string - M2 map[string]M2 - - Last int -) - -// cycles in function/method declarations -// (test cases for issue 5217 and variants) -func f1(x f1 /* ERROR "not a type" */ ) {} -func f2(x *f2 /* ERROR "not a type" */ ) {} -func f3() (x f3 /* ERROR "not a type" */ ) { return } -func f4() (x *f4 /* ERROR "not a type" */ ) { return } - -func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {} -func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {} -func (S0) m3() (x S0 /* ERROR "field or method" */ .m3) { return } -func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { return } - -// interfaces may not have any blank methods -type BlankI interface { - _ /* ERROR "invalid method name" */ () - _ /* ERROR "invalid method name" */ (float32) int - m() -} - -// non-interface types may have multiple blank methods -type BlankT struct{} - -func (BlankT) _() {} -func (BlankT) _(int) {} -func (BlankT) _() int { return 0 } -func (BlankT) _(int) int { return 0} diff --git a/go/types/testdata/decls1.src b/go/types/testdata/decls1.src deleted file mode 100644 index 7855e461e2..0000000000 --- a/go/types/testdata/decls1.src +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2012 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. - -// variable declarations - -package decls1 - -import ( - "math" -) - -// Global variables without initialization -var ( - a, b bool - c byte - d uint8 - r rune - i int - j, k, l int - x, y float32 - xx, yy float64 - u, v complex64 - uu, vv complex128 - s, t string - array []byte - iface interface{} - - blank _ /* ERROR "cannot use _" */ -) - -// Global variables with initialization -var ( - s1 = i + j - s2 = i /* ERROR "mismatched types" */ + x - s3 = c + d - s4 = s + t - s5 = s /* ERROR "invalid operation" */ / t - s6 = array[t1] - s7 = array[x /* ERROR "integer" */] - s8 = &a - s10 = &42 /* ERROR "cannot take address" */ - s11 = &v - s12 = -(u + *t11) / *&v - s13 = a /* ERROR "shifted operand" */ << d - s14 = i << j /* ERROR "must be unsigned" */ - s18 = math.Pi * 10.0 - s19 = s1 /* ERROR "cannot call" */ () - s20 = f0 /* ERROR "no value" */ () - s21 = f6(1, s1, i) - s22 = f6(1, s1, uu /* ERROR "cannot pass argument" */ ) - - t1 int = i + j - t2 int = i /* ERROR "mismatched types" */ + x - t3 int = c /* ERROR "cannot initialize" */ + d - t4 string = s + t - t5 string = s /* ERROR "invalid operation" */ / t - t6 byte = array[t1] - t7 byte = array[x /* ERROR "must be integer" */] - t8 *int = & /* ERROR "cannot initialize" */ a - t10 *int = &42 /* ERROR "cannot take address" */ - t11 *complex64 = &v - t12 complex64 = -(u + *t11) / *&v - t13 int = a /* ERROR "shifted operand" */ << d - t14 int = i << j /* ERROR "must be unsigned" */ - t15 math /* ERROR "not in selector" */ - t16 math /* ERROR "not declared" */ .xxx - t17 math /* ERROR "not a type" */ .Pi - t18 float64 = math.Pi * 10.0 - t19 int = t1 /* ERROR "cannot call" */ () - t20 int = f0 /* ERROR "no value" */ () - t21 int = a /* ERROR "cannot initialize" */ -) - -// Various more complex expressions -var ( - u1 = x /* ERROR "not an interface" */ .(int) - u2 = iface.([]int) - u3 = iface.(a /* ERROR "not a type" */ ) - u4, ok = iface.(int) - u5, ok2, ok3 = iface /* ERROR "assignment count mismatch" */ .(int) -) - -// Constant expression initializations -var ( - v1 = 1 /* ERROR "cannot convert" */ + "foo" - v2 = c + 255 - v3 = c + 256 /* ERROR "overflows" */ - v4 = r + 2147483647 - v5 = r + 2147483648 /* ERROR "overflows" */ - v6 = 42 - v7 = v6 + 9223372036854775807 - v8 = v6 + 9223372036854775808 /* ERROR "overflows" */ - v9 = i + 1 << 10 - v10 byte = 1024 /* ERROR "overflows" */ - v11 = xx/yy*yy - xx - v12 = true && false - v13 = nil /* ERROR "use of untyped nil" */ -) - -// Multiple assignment expressions -var ( - m1a, m1b = 1, 2 - m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2 - m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */ -) - -func _() { - var ( - m1a, m1b = 1, 2 - m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2 - m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */ - ) - - _, _ = m1a, m1b - _, _, _ = m2a, m2b, m2c - _, _ = m3a, m3b -} - -// Declaration of parameters and results -func f0() {} -func f1(a /* ERROR "not a type" */) {} -func f2(a, b, c d /* ERROR "not a type" */) {} - -func f3() int { return 0 } -func f4() a /* ERROR "not a type" */ { return 0 } -func f5() (a, b, c d /* ERROR "not a type" */) { return } - -func f6(a, b, c int) complex128 { return 0 } - -// Declaration of receivers -type T struct{} - -func (T) m0() {} -func (*T) m1() {} -func (x T) m2() {} -func (x *T) m3() {} - -// Initialization functions -func init() {} -func /* ERROR "no arguments and no return values" */ init(int) {} -func /* ERROR "no arguments and no return values" */ init() int { return 0 } -func /* ERROR "no arguments and no return values" */ init(int) int { return 0 } -func (T) init(int) int { return 0 } diff --git a/go/types/testdata/decls2a.src b/go/types/testdata/decls2a.src deleted file mode 100644 index bdbecd9dbb..0000000000 --- a/go/types/testdata/decls2a.src +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2012 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. - -// method declarations - -package decls2 - -import "time" -import "unsafe" - -// T1 declared before its methods. -type T1 struct{ - f int -} - -func (T1) m() {} -func (T1) m /* ERROR "already declared" */ () {} -func (x *T1) f /* ERROR "field and method" */ () {} - -// Conflict between embedded field and method name, -// with the embedded field being a basic type. -type T1b struct { - int -} - -func (T1b) int /* ERROR "field and method" */ () {} - -type T1c struct { - time.Time -} - -func (T1c) Time /* ERROR "field and method" */ () int { return 0 } - -// Disabled for now: LookupFieldOrMethod will find Pointer even though -// it's double-declared (it would cost extra in the common case to verify -// this). But the MethodSet computation will not find it due to the name -// collision caused by the double-declaration, leading to an internal -// inconsistency while we are verifying one computation against the other. -// var _ = T1c{}.Pointer - -// T2's method declared before the type. -func (*T2) f /* ERROR "field and method" */ () {} - -type T2 struct { - f int -} - -// Methods declared without a declared type. -func (undeclared /* ERROR "undeclared" */) m() {} -func (x *undeclared /* ERROR "undeclared" */) m() {} - -func (pi /* ERROR "not a type" */) m1() {} -func (x pi /* ERROR "not a type" */) m2() {} -func (x *pi /* ERROR "not a type" */ ) m3() {} - -// Blank types. -type _ struct { m int } -type _ struct { m int } - -func (_ /* ERROR "cannot use _" */) m() {} -func m(_ /* ERROR "cannot use _" */) {} - -// Methods with receiver base type declared in another file. -func (T3) m1() {} -func (*T3) m2() {} -func (x T3) m3() {} -func (x *T3) f /* ERROR "field and method" */ () {} - -// Methods of non-struct type. -type T4 func() - -func (self T4) m() func() { return self } - -// Methods associated with an interface. -type T5 interface { - m() int -} - -func (T5 /* ERROR "invalid receiver" */ ) m1() {} -func (T5 /* ERROR "invalid receiver" */ ) m2() {} - -// Methods associated with a named pointer type. -type ptr *int -func (ptr /* ERROR "invalid receiver" */ ) _() {} -func (* /* ERROR "invalid receiver" */ ptr) _() {} - -// Methods with zero or multiple receivers. -func ( /* ERROR "missing receiver" */ ) _() {} -func (T3, * /* ERROR "exactly one receiver" */ T3) _() {} -func (T3, T3, T3 /* ERROR "exactly one receiver" */ ) _() {} -func (a, b /* ERROR "exactly one receiver" */ T3) _() {} -func (a, b, c /* ERROR "exactly one receiver" */ T3) _() {} - -// Methods associated with non-local or unnamed types. -func (int /* ERROR "invalid receiver" */ ) m() {} -func ([ /* ERROR "invalid receiver" */ ]int) m() {} -func (time /* ERROR "invalid receiver" */ .Time) m() {} -func (* /* ERROR "invalid receiver" */ time.Time) m() {} -func (x /* ERROR "invalid receiver" */ interface{}) m() {} - -// Unsafe.Pointer is treated like a pointer when used as receiver type. -type UP unsafe.Pointer -func (UP /* ERROR "invalid" */ ) m1() {} -func (* /* ERROR "invalid" */ UP) m2() {} - -// Double declarations across package files -const c_double = 0 -type t_double int -var v_double int -func f_double() {} diff --git a/go/types/testdata/decls2b.src b/go/types/testdata/decls2b.src deleted file mode 100644 index e7bc394762..0000000000 --- a/go/types/testdata/decls2b.src +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2012 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. - -// method declarations - -package decls2 - -import "io" - -const pi = 3.1415 - -func (T1) m /* ERROR "already declared" */ () {} -func (T2) m(io.Writer) {} - -type T3 struct { - f *T3 -} - -type T6 struct { - x int -} - -func (t *T6) m1() int { - return t.x -} - -func f() { - var t *T6 - t.m1() -} - -// Double declarations across package files -const c_double /* ERROR "redeclared" */ = 0 -type t_double /* ERROR "redeclared" */ int -var v_double /* ERROR "redeclared" */ int -func f_double /* ERROR "redeclared" */ () {} - -// Blank methods need to be type-checked. -// Verify by checking that errors are reported. -func (T /* ERROR "undeclared" */ ) _() {} -func (T1) _(undeclared /* ERROR "undeclared" */ ) {} -func (T1) _() int { return "foo" /* ERROR "cannot convert" */ } - -// Methods with undeclared receiver type can still be checked. -// Verify by checking that errors are reported. -func (Foo /* ERROR "undeclared" */ ) m() {} -func (Foo /* ERROR "undeclared" */ ) m(undeclared /* ERROR "undeclared" */ ) {} -func (Foo /* ERROR "undeclared" */ ) m() int { return "foo" /* ERROR "cannot convert" */ } - -func (Foo /* ERROR "undeclared" */ ) _() {} -func (Foo /* ERROR "undeclared" */ ) _(undeclared /* ERROR "undeclared" */ ) {} -func (Foo /* ERROR "undeclared" */ ) _() int { return "foo" /* ERROR "cannot convert" */ } - -// Receiver declarations are regular parameter lists; -// receiver types may use parentheses, and the list -// may have a trailing comma. -type T7 struct {} - -func (T7) m1() {} -func ((T7)) m2() {} -func ((*T7)) m3() {} -func (x *(T7),) m4() {} -func (x (*(T7)),) m5() {} -func (x ((*((T7)))),) m6() {} diff --git a/go/types/testdata/decls3.src b/go/types/testdata/decls3.src deleted file mode 100644 index 80d2bc8ff8..0000000000 --- a/go/types/testdata/decls3.src +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2012 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. - -// embedded types - -package decls3 - -import "unsafe" -import "fmt" - -// fields with the same name at the same level cancel each other out - -func _() { - type ( - T1 struct { X int } - T2 struct { X int } - T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X - ) - - var t T3 - _ = t /* ERROR "ambiguous selector" */ .X -} - -func _() { - type ( - T1 struct { X int } - T2 struct { T1 } - T3 struct { T1 } - T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X - ) - - var t T4 - _ = t /* ERROR "ambiguous selector" */ .X -} - -func issue4355() { - type ( - T1 struct {X int} - T2 struct {T1} - T3 struct {T2} - T4 struct {T2} - T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X - ) - - var t T5 - _ = t /* ERROR "ambiguous selector" */ .X -} - -func _() { - type State int - type A struct{ State } - type B struct{ fmt.State } - type T struct{ A; B } - - var t T - _ = t /* ERROR "ambiguous selector" */ .State -} - -// Embedded fields can be predeclared types. - -func _() { - type T0 struct{ - int - float32 - f int - } - var x T0 - _ = x.int - _ = x.float32 - _ = x.f - - type T1 struct{ - T0 - } - var y T1 - _ = y.int - _ = y.float32 - _ = y.f -} - -// Restrictions on embedded field types. - -func _() { - type I1 interface{} - type I2 interface{} - type P1 *int - type P2 *int - type UP unsafe.Pointer - - type T1 struct { - I1 - * /* ERROR "cannot be a pointer to an interface" */ I2 - * /* ERROR "cannot be a pointer to an interface" */ error - P1 /* ERROR "cannot be a pointer" */ - * /* ERROR "cannot be a pointer" */ P2 - } - - // unsafe.Pointers are treated like regular pointers when embedded - type T2 struct { - unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer - */* ERROR "cannot be unsafe.Pointer" */ unsafe.Pointer - UP /* ERROR "cannot be unsafe.Pointer" */ - * /* ERROR "cannot be unsafe.Pointer" */ UP - } -} - -// Named types that are pointers. - -type S struct{ x int } -func (*S) m() {} -type P *S - -func _() { - var s *S - _ = s.x - _ = s.m - - var p P - _ = p.x - _ = p /* ERROR "no field or method" */ .m - _ = P /* ERROR "no field or method" */ .m -} - -// Borrowed from the FieldByName test cases in reflect/all_test.go. - -type D1 struct { - d int -} -type D2 struct { - d int -} - -type S0 struct { - A, B, C int - D1 - D2 -} - -type S1 struct { - B int - S0 -} - -type S2 struct { - A int - *S1 -} - -type S1x struct { - S1 -} - -type S1y struct { - S1 -} - -type S3 struct { - S1x - S2 - D, E int - *S1y -} - -type S4 struct { - *S4 - A int -} - -// The X in S6 and S7 annihilate, but they also block the X in S8.S9. -type S5 struct { - S6 - S7 - S8 -} - -type S6 struct { - X int -} - -type S7 S6 - -type S8 struct { - S9 -} - -type S9 struct { - X int - Y int -} - -// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. -type S10 struct { - S11 - S12 - S13 -} - -type S11 struct { - S6 -} - -type S12 struct { - S6 -} - -type S13 struct { - S8 -} - -func _() { - _ = struct /* ERROR "no field or method" */ {}{}.Foo - _ = S0{}.A - _ = S0 /* ERROR "no field or method" */ {}.D - _ = S1{}.A - _ = S1{}.B - _ = S1{}.S0 - _ = S1{}.C - _ = S2{}.A - _ = S2{}.S1 - _ = S2{}.B - _ = S2{}.C - _ = S2 /* ERROR "no field or method" */ {}.D - _ = S3 /* ERROR "ambiguous selector" */ {}.S1 - _ = S3{}.A - _ = S3 /* ERROR "ambiguous selector" */ {}.B - _ = S3{}.D - _ = S3{}.E - _ = S4{}.A - _ = S4 /* ERROR "no field or method" */ {}.B - _ = S5 /* ERROR "ambiguous selector" */ {}.X - _ = S5{}.Y - _ = S10 /* ERROR "ambiguous selector" */ {}.X - _ = S10{}.Y -} - -// Borrowed from the FieldByName benchmark in reflect/all_test.go. - -type R0 struct { - *R1 - *R2 - *R3 - *R4 -} - -type R1 struct { - *R5 - *R6 - *R7 - *R8 -} - -type R2 R1 -type R3 R1 -type R4 R1 - -type R5 struct { - *R9 - *R10 - *R11 - *R12 -} - -type R6 R5 -type R7 R5 -type R8 R5 - -type R9 struct { - *R13 - *R14 - *R15 - *R16 -} - -type R10 R9 -type R11 R9 -type R12 R9 - -type R13 struct { - *R17 - *R18 - *R19 - *R20 -} - -type R14 R13 -type R15 R13 -type R16 R13 - -type R17 struct { - *R21 - *R22 - *R23 - *R24 -} - -type R18 R17 -type R19 R17 -type R20 R17 - -type R21 struct { - X int -} - -type R22 R21 -type R23 R21 -type R24 R21 - -var _ = R0 /* ERROR "ambiguous selector" */ {}.X \ No newline at end of file diff --git a/go/types/testdata/errors.src b/go/types/testdata/errors.src deleted file mode 100644 index 45bd45a13a..0000000000 --- a/go/types/testdata/errors.src +++ /dev/null @@ -1,55 +0,0 @@ -// 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 errors - -// Testing precise operand formatting in error messages -// (matching messages are regular expressions, hence the \'s). -func f(x int, m map[string]int) { - // no values - _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m) - - // built-ins - _ = println /* ERROR "println \(built-in\) must be called" */ - - // types - _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */ - - // constants - const c1 = 991 - const c2 float32 = 0.5 - 0 /* ERROR "0 \(untyped int constant\) is not used" */ - c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */ - c2 /* ERROR "c2 \(constant 1/2 of type float32\) is not used" */ - c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2 - - // variables - x /* ERROR "x \(variable of type int\) is not used" */ - - // values - x /* ERROR "x != x \(untyped bool value\) is not used" */ != x - x /* ERROR "x \+ x \(value of type int\) is not used" */ + x - - // value, ok's - const s = "foo" - m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s] -} - -// Valid ERROR comments can have a variety of forms. -func _() { - 0 /* ERROR "0 .* is not used" */ - 0 /* ERROR 0 .* is not used */ - 0 // ERROR "0 .* is not used" - 0 // ERROR 0 .* is not used -} - -// Don't report spurious errors as a consequence of earlier errors. -// Add more tests as needed. -func _() { - if err := foo /* ERROR undeclared */ (); err != nil /* no error here */ {} -} - -// Use unqualified names for package-local objects. -type T struct{} -var _ int = T /* ERROR value of type T */ {} // use T in error message rather then errors.T diff --git a/go/types/testdata/expr0.src b/go/types/testdata/expr0.src deleted file mode 100644 index 3120c6f078..0000000000 --- a/go/types/testdata/expr0.src +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2012 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. - -// unary expressions - -package expr0 - -type mybool bool - -var ( - // bool - b0 = true - b1 bool = b0 - b2 = !true - b3 = !b1 - b4 bool = !true - b5 bool = !b4 - b6 = +b0 /* ERROR "not defined" */ - b7 = -b0 /* ERROR "not defined" */ - b8 = ^b0 /* ERROR "not defined" */ - b9 = *b0 /* ERROR "cannot indirect" */ - b10 = &true /* ERROR "cannot take address" */ - b11 = &b0 - b12 = <-b0 /* ERROR "cannot receive" */ - b13 = & & /* ERROR "cannot take address" */ b0 - - // byte - _ = byte(0) - _ = byte(- /* ERROR "cannot convert" */ 1) - _ = - /* ERROR "-byte\(1\) \(constant -1 of type byte\) overflows byte" */ byte(1) // test for issue 11367 - _ = byte /* ERROR "overflows byte" */ (0) - byte(1) - - // int - i0 = 1 - i1 int = i0 - i2 = +1 - i3 = +i0 - i4 int = +1 - i5 int = +i4 - i6 = -1 - i7 = -i0 - i8 int = -1 - i9 int = -i4 - i10 = !i0 /* ERROR "not defined" */ - i11 = ^1 - i12 = ^i0 - i13 int = ^1 - i14 int = ^i4 - i15 = *i0 /* ERROR "cannot indirect" */ - i16 = &i0 - i17 = *i16 - i18 = <-i16 /* ERROR "cannot receive" */ - - // uint - u0 = uint(1) - u1 uint = u0 - u2 = +1 - u3 = +u0 - u4 uint = +1 - u5 uint = +u4 - u6 = -1 - u7 = -u0 - u8 uint = - /* ERROR "overflows" */ 1 - u9 uint = -u4 - u10 = !u0 /* ERROR "not defined" */ - u11 = ^1 - u12 = ^i0 - u13 uint = ^ /* ERROR "overflows" */ 1 - u14 uint = ^u4 - u15 = *u0 /* ERROR "cannot indirect" */ - u16 = &u0 - u17 = *u16 - u18 = <-u16 /* ERROR "cannot receive" */ - u19 = ^uint(0) - - // float64 - f0 = float64(1) - f1 float64 = f0 - f2 = +1 - f3 = +f0 - f4 float64 = +1 - f5 float64 = +f4 - f6 = -1 - f7 = -f0 - f8 float64 = -1 - f9 float64 = -f4 - f10 = !f0 /* ERROR "not defined" */ - f11 = ^1 - f12 = ^i0 - f13 float64 = ^1 - f14 float64 = ^f4 /* ERROR "not defined" */ - f15 = *f0 /* ERROR "cannot indirect" */ - f16 = &f0 - f17 = *u16 - f18 = <-u16 /* ERROR "cannot receive" */ - - // complex128 - c0 = complex128(1) - c1 complex128 = c0 - c2 = +1 - c3 = +c0 - c4 complex128 = +1 - c5 complex128 = +c4 - c6 = -1 - c7 = -c0 - c8 complex128 = -1 - c9 complex128 = -c4 - c10 = !c0 /* ERROR "not defined" */ - c11 = ^1 - c12 = ^i0 - c13 complex128 = ^1 - c14 complex128 = ^c4 /* ERROR "not defined" */ - c15 = *c0 /* ERROR "cannot indirect" */ - c16 = &c0 - c17 = *u16 - c18 = <-u16 /* ERROR "cannot receive" */ - - // string - s0 = "foo" - s1 = +"foo" /* ERROR "not defined" */ - s2 = -s0 /* ERROR "not defined" */ - s3 = !s0 /* ERROR "not defined" */ - s4 = ^s0 /* ERROR "not defined" */ - s5 = *s4 - s6 = &s4 - s7 = *s6 - s8 = <-s7 - - // channel - ch chan int - rc <-chan float64 - sc chan <- string - ch0 = +ch /* ERROR "not defined" */ - ch1 = -ch /* ERROR "not defined" */ - ch2 = !ch /* ERROR "not defined" */ - ch3 = ^ch /* ERROR "not defined" */ - ch4 = *ch /* ERROR "cannot indirect" */ - ch5 = &ch - ch6 = *ch5 - ch7 = <-ch - ch8 = <-rc - ch9 = <-sc /* ERROR "cannot receive" */ - ch10, ok = <-ch - // ok is of type bool - ch11, myok = <-ch - _ mybool = myok /* ERROR "cannot initialize" */ -) - -// address of composite literals -type T struct{x, y int} - -func f() T { return T{} } - -var ( - _ = &T{1, 2} - _ = &[...]int{} - _ = &[]int{} - _ = &[]int{} - _ = &map[string]T{} - _ = &(T{1, 2}) - _ = &((((T{1, 2})))) - _ = &f /* ERROR "cannot take address" */ () -) - -// recursive pointer types -type P *P - -var ( - p1 P = new(P) - p2 P = *p1 - p3 P = &p2 -) - diff --git a/go/types/testdata/expr1.src b/go/types/testdata/expr1.src deleted file mode 100644 index 8ef0aed6d2..0000000000 --- a/go/types/testdata/expr1.src +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2012 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. - -// binary expressions - -package expr1 diff --git a/go/types/testdata/expr2.src b/go/types/testdata/expr2.src deleted file mode 100644 index 31dc5f021c..0000000000 --- a/go/types/testdata/expr2.src +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2012 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. - -// comparisons - -package expr2 - -func _bool() { - const t = true == true - const f = true == false - _ = t /* ERROR "cannot compare" */ < f - _ = 0 /* ERROR "cannot convert" */ == t - var b bool - var x, y float32 - b = x < y - _ = b - _ = struct{b bool}{x < y} -} - -// corner cases -var ( - v0 = nil /* ERROR "cannot compare" */ == nil -) - -func arrays() { - // basics - var a, b [10]int - _ = a == b - _ = a != b - _ = a /* ERROR < not defined */ < b - _ = a == nil /* ERROR cannot convert */ - - type C [10]int - var c C - _ = a == c - - type D [10]int - var d D - _ = c /* ERROR mismatched types */ == d - - var e [10]func() int - _ = e /* ERROR == not defined */ == e -} - -func structs() { - // basics - var s, t struct { - x int - a [10]float32 - _ bool - } - _ = s == t - _ = s != t - _ = s /* ERROR < not defined */ < t - _ = s == nil /* ERROR cannot convert */ - - type S struct { - x int - a [10]float32 - _ bool - } - type T struct { - x int - a [10]float32 - _ bool - } - var ss S - var tt T - _ = s == ss - _ = ss /* ERROR mismatched types */ == tt - - var u struct { - x int - a [10]map[string]int - } - _ = u /* ERROR cannot compare */ == u -} - -func pointers() { - // nil - _ = nil /* ERROR == not defined */ == nil - _ = nil /* ERROR != not defined */ != nil - _ = nil /* ERROR < not defined */ < nil - _ = nil /* ERROR <= not defined */ <= nil - _ = nil /* ERROR > not defined */ > nil - _ = nil /* ERROR >= not defined */ >= nil - - // basics - var p, q *int - _ = p == q - _ = p != q - - _ = p == nil - _ = p != nil - _ = nil == q - _ = nil != q - - _ = p /* ERROR < not defined */ < q - _ = p /* ERROR <= not defined */ <= q - _ = p /* ERROR > not defined */ > q - _ = p /* ERROR >= not defined */ >= q - - // various element types - type ( - S1 struct{} - S2 struct{} - P1 *S1 - P2 *S2 - ) - var ( - ps1 *S1 - ps2 *S2 - p1 P1 - p2 P2 - ) - _ = ps1 == ps1 - _ = ps1 /* ERROR mismatched types */ == ps2 - _ = ps2 /* ERROR mismatched types */ == ps1 - - _ = p1 == p1 - _ = p1 /* ERROR mismatched types */ == p2 - - _ = p1 == ps1 -} - -func channels() { - // basics - var c, d chan int - _ = c == d - _ = c != d - _ = c == nil - _ = c /* ERROR < not defined */ < d - - // various element types (named types) - type ( - C1 chan int - C1r <-chan int - C1s chan<- int - C2 chan float32 - ) - var ( - c1 C1 - c1r C1r - c1s C1s - c1a chan int - c2 C2 - ) - _ = c1 == c1 - _ = c1 /* ERROR mismatched types */ == c1r - _ = c1 /* ERROR mismatched types */ == c1s - _ = c1r /* ERROR mismatched types */ == c1s - _ = c1 == c1a - _ = c1a == c1 - _ = c1 /* ERROR mismatched types */ == c2 - _ = c1a /* ERROR mismatched types */ == c2 - - // various element types (unnamed types) - var ( - d1 chan int - d1r <-chan int - d1s chan<- int - d1a chan<- int - d2 chan float32 - ) - _ = d1 == d1 - _ = d1 == d1r - _ = d1 == d1s - _ = d1r /* ERROR mismatched types */ == d1s - _ = d1 == d1a - _ = d1a == d1 - _ = d1 /* ERROR mismatched types */ == d2 - _ = d1a /* ERROR mismatched types */ == d2 -} - -// for interfaces test -type S1 struct{} -type S11 struct{} -type S2 struct{} -func (*S1) m() int -func (*S11) m() int -func (*S11) n() -func (*S2) m() float32 - -func interfaces() { - // basics - var i, j interface{ m() int } - _ = i == j - _ = i != j - _ = i == nil - _ = i /* ERROR < not defined */ < j - - // various interfaces - var ii interface { m() int; n() } - var k interface { m() float32 } - _ = i == ii - _ = i /* ERROR mismatched types */ == k - - // interfaces vs values - var s1 S1 - var s11 S11 - var s2 S2 - - _ = i == 0 /* ERROR cannot convert */ - _ = i /* ERROR mismatched types */ == s1 - _ = i == &s1 - _ = i == &s11 - - _ = i /* ERROR mismatched types */ == s2 - _ = i /* ERROR mismatched types */ == &s2 -} - -func slices() { - // basics - var s []int - _ = s == nil - _ = s != nil - _ = s /* ERROR < not defined */ < nil - - // slices are not otherwise comparable - _ = s /* ERROR == not defined */ == s - _ = s /* ERROR < not defined */ < s -} - -func maps() { - // basics - var m map[string]int - _ = m == nil - _ = m != nil - _ = m /* ERROR < not defined */ < nil - - // maps are not otherwise comparable - _ = m /* ERROR == not defined */ == m - _ = m /* ERROR < not defined */ < m -} - -func funcs() { - // basics - var f func(int) float32 - _ = f == nil - _ = f != nil - _ = f /* ERROR < not defined */ < nil - - // funcs are not otherwise comparable - _ = f /* ERROR == not defined */ == f - _ = f /* ERROR < not defined */ < f -} diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src deleted file mode 100644 index 57720954bd..0000000000 --- a/go/types/testdata/expr3.src +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2012 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 expr3 - -import "time" - -func indexes() { - _ = 1 /* ERROR "cannot index" */ [0] - _ = indexes /* ERROR "cannot index" */ [0] - _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2] - - var a [10]int - _ = a[true /* ERROR "cannot convert" */ ] - _ = a["foo" /* ERROR "cannot convert" */ ] - _ = a[1.1 /* ERROR "truncated" */ ] - _ = a[1.0] - _ = a[- /* ERROR "negative" */ 1] - _ = a[- /* ERROR "negative" */ 1 :] - _ = a[: - /* ERROR "negative" */ 1] - _ = a[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ] - _ = a[0: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ] - _ = a[0: /* ERROR "2nd index required" */ :10] - _ = a[:10:10] - - var a0 int - a0 = a[0] - _ = a0 - var a1 int32 - a1 = a /* ERROR "cannot assign" */ [1] - _ = a1 - - _ = a[9] - _ = a[10 /* ERROR "index .* out of bounds" */ ] - _ = a[1 /* ERROR "overflows" */ <<100] - _ = a[10:] - _ = a[:10] - _ = a[10:10] - _ = a[11 /* ERROR "index .* out of bounds" */ :] - _ = a[: 11 /* ERROR "index .* out of bounds" */ ] - _ = a[: 1 /* ERROR "overflows" */ <<100] - _ = a[:10:10] - _ = a[:11 /* ERROR "index .* out of bounds" */ :10] - _ = a[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = a[10:0:10] /* ERROR "invalid slice indices" */ - _ = a[0:10:0] /* ERROR "invalid slice indices" */ - _ = a[10:0:0] /* ERROR "invalid slice indices" */ - _ = &a /* ERROR "cannot take address" */ [:10] - - pa := &a - _ = pa[9] - _ = pa[10 /* ERROR "index .* out of bounds" */ ] - _ = pa[1 /* ERROR "overflows" */ <<100] - _ = pa[10:] - _ = pa[:10] - _ = pa[10:10] - _ = pa[11 /* ERROR "index .* out of bounds" */ :] - _ = pa[: 11 /* ERROR "index .* out of bounds" */ ] - _ = pa[: 1 /* ERROR "overflows" */ <<100] - _ = pa[:10:10] - _ = pa[:11 /* ERROR "index .* out of bounds" */ :10] - _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = pa[10:0:10] /* ERROR "invalid slice indices" */ - _ = pa[0:10:0] /* ERROR "invalid slice indices" */ - _ = pa[10:0:0] /* ERROR "invalid slice indices" */ - _ = &pa /* ERROR "cannot take address" */ [:10] - - var b [0]int - _ = b[0 /* ERROR "index .* out of bounds" */ ] - _ = b[:] - _ = b[0:] - _ = b[:0] - _ = b[0:0] - _ = b[0:0:0] - _ = b[1 /* ERROR "index .* out of bounds" */ :0:0] - - var s []int - _ = s[- /* ERROR "negative" */ 1] - _ = s[- /* ERROR "negative" */ 1 :] - _ = s[: - /* ERROR "negative" */ 1] - _ = s[0] - _ = s[1:2] - _ = s[2:1] /* ERROR "invalid slice indices" */ - _ = s[2:] - _ = s[: 1 /* ERROR "overflows" */ <<100] - _ = s[1 /* ERROR "overflows" */ <<100 :] - _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] - _ = s[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ] - _ = s[:10:10] - _ = s[10:0:10] /* ERROR "invalid slice indices" */ - _ = s[0:10:0] /* ERROR "invalid slice indices" */ - _ = s[10:0:0] /* ERROR "invalid slice indices" */ - _ = &s /* ERROR "cannot take address" */ [:10] - - var m map[string]int - _ = m[0 /* ERROR "cannot convert" */ ] - _ = m /* ERROR "cannot slice" */ ["foo" : "bar"] - _ = m["foo"] - // ok is of type bool - type mybool bool - var ok mybool - _, ok = m["bar"] - _ = ok - - var t string - _ = t[- /* ERROR "negative" */ 1] - _ = t[- /* ERROR "negative" */ 1 :] - _ = t[: - /* ERROR "negative" */ 1] - _ = t /* ERROR "3-index slice of string" */ [1:2:3] - _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3] - var t0 byte - t0 = t[0] - _ = t0 - var t1 rune - t1 = t /* ERROR "cannot assign" */ [2] - _ = t1 - _ = ("foo" + "bar")[5] - _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ] - - const c = "foo" - _ = c[- /* ERROR "negative" */ 1] - _ = c[- /* ERROR "negative" */ 1 :] - _ = c[: - /* ERROR "negative" */ 1] - var c0 byte - c0 = c[0] - _ = c0 - var c2 float32 - c2 = c /* ERROR "cannot assign" */ [2] - _ = c[3 /* ERROR "index .* out of bounds" */ ] - _ = ""[0 /* ERROR "index .* out of bounds" */ ] - _ = c2 - - _ = s[1<<30] // no compile-time error here - - // issue 4913 - type mystring string - var ss string - var ms mystring - var i, j int - ss = "foo"[1:2] - ss = "foo"[i:j] - ms = "foo" /* ERROR "cannot assign" */ [1:2] - ms = "foo" /* ERROR "cannot assign" */ [i:j] - _, _ = ss, ms -} - -type T struct { - x int - y func() -} - -func (*T) m() {} - -func method_expressions() { - _ = T /* ERROR "no field or method" */ .a - _ = T /* ERROR "has no method" */ .x - _ = T /* ERROR "not in method set" */ .m - _ = (*T).m - - var f func(*T) = T /* ERROR "not in method set" */ .m - var g func(*T) = (*T).m - _, _ = f, g - - _ = T /* ERROR "has no method" */ .y - _ = ( /* ERROR "has no method" */ *T).y -} - -func struct_literals() { - type T0 struct { - a, b, c int - } - - type T1 struct { - T0 - a, b int - u float64 - s string - } - - // keyed elements - _ = T1{} - _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ } - _ = T1{aa /* ERROR "unknown field" */ : 0} - _ = T1{1 /* ERROR "invalid field name" */ : 0} - _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10} - _ = T1{a: "foo" /* ERROR "cannot convert" */ } - _ = T1{c /* ERROR "unknown field" */ : 0} - _ = T1{T0: { /* ERROR "missing type" */ }} // struct literal element type may not be elided - _ = T1{T0: T0{}} - _ = T1{T0 /* ERROR "invalid field name" */ .a: 0} - - // unkeyed elements - _ = T0{1, 2, 3} - _ = T0{1, b /* ERROR "mixture" */ : 2, 3} - _ = T0{1, 2} /* ERROR "too few values" */ - _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } - _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "truncated" */} - - // invalid type - type P *struct{ - x int - } - _ = P /* ERROR "invalid composite literal type" */ {} - - // unexported fields - _ = time.Time{} - _ = time.Time{sec /* ERROR "unknown field" */ : 0} - _ = time.Time{ - 0 /* ERROR implicit assignment to unexported field sec in time.Time literal */, - 0 /* ERROR implicit assignment */ , - nil /* ERROR implicit assignment */ , - } -} - -func array_literals() { - type A0 [0]int - _ = A0{} - _ = A0{0 /* ERROR "index .* out of bounds" */} - _ = A0{0 /* ERROR "index .* out of bounds" */ : 0} - - type A1 [10]int - _ = A1{} - _ = A1{0, 1, 2} - _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ } - _ = A1{- /* ERROR "negative" */ 1: 0} - _ = A1{8: 8, 9} - _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ } - _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - _ = A1{5: 5, 6, 7, 3: 3, 4} - _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } - _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10} - _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } - _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} - _ = A1{2.0} - _ = A1{2.1 /* ERROR "truncated" */ } - _ = A1{"foo" /* ERROR "cannot convert" */ } - - // indices must be integer constants - i := 1 - const f = 2.1 - const s = "foo" - _ = A1{i /* ERROR "index i must be integer constant" */ : 0} - _ = A1{f /* ERROR "truncated" */ : 0} - _ = A1{s /* ERROR "cannot convert" */ : 0} - - a0 := [...]int{} - assert(len(a0) == 0) - - a1 := [...]int{0, 1, 2} - assert(len(a1) == 3) - var a13 [3]int - var a14 [4]int - a13 = a1 - a14 = a1 /* ERROR "cannot assign" */ - _, _ = a13, a14 - - a2 := [...]int{- /* ERROR "negative" */ 1: 0} - _ = a2 - - a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - assert(len(a3) == 5) // somewhat arbitrary - - a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14} - assert(len(a4) == 1024) - - // composite literal element types may be elided - type T []int - _ = [10]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - a6 := [...]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - assert(len(a6) == 8) - - // recursively so - _ = [10][10]T{{}, [10]T{{}}, {{1, 2, 3}}} - - // from the spec - type Point struct { x, y float32 } - _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}} - _ = [...]Point{{1.5, -3.5}, {0, 0}} - _ = [][]int{[]int{1, 2, 3}, []int{4, 5}} - _ = [][]int{{1, 2, 3}, {4, 5}} - _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}} - _ = [...]*Point{{1.5, -3.5}, {0, 0}} -} - -func slice_literals() { - type S0 []int - _ = S0{} - _ = S0{0, 1, 2} - _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - _ = S0{- /* ERROR "negative" */ 1: 0} - _ = S0{8: 8, 9} - _ = S0{8: 8, 9, 10} - _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - _ = S0{5: 5, 6, 7, 3: 3, 4} - _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } - _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10} - _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } - _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} - _ = S0{2.0} - _ = S0{2.1 /* ERROR "truncated" */ } - _ = S0{"foo" /* ERROR "cannot convert" */ } - - // indices must be resolved correctly - const index1 = 1 - _ = S0{index1: 1} - _ = S0{index2: 2} - _ = S0{index3 /* ERROR "undeclared name" */ : 3} - - // indices must be integer constants - i := 1 - const f = 2.1 - const s = "foo" - _ = S0{i /* ERROR "index i must be integer constant" */ : 0} - _ = S0{f /* ERROR "truncated" */ : 0} - _ = S0{s /* ERROR "cannot convert" */ : 0} - - // composite literal element types may be elided - type T []int - _ = []T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - _ = [][]int{{1, 2, 3}, {4, 5}} - - // recursively so - _ = [][]T{{}, []T{{}}, {{1, 2, 3}}} -} - -const index2 int = 2 - -type N int -func (N) f() {} - -func map_literals() { - type M0 map[string]int - type M1 map[bool]int - type M2 map[*int]int - - _ = M0{} - _ = M0{1 /* ERROR "missing key" */ } - _ = M0{1 /* ERROR "cannot convert" */ : 2} - _ = M0{"foo": "bar" /* ERROR "cannot convert" */ } - _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } - - _ = map[interface{}]int{2: 1, 2 /* ERROR "duplicate key" */ : 1} - _ = map[interface{}]int{int(2): 1, int16(2): 1} - _ = map[interface{}]int{int16(2): 1, int16 /* ERROR "duplicate key" */ (2): 1} - - type S string - - _ = map[interface{}]int{"a": 1, "a" /* ERROR "duplicate key" */ : 1} - _ = map[interface{}]int{"a": 1, S("a"): 1} - _ = map[interface{}]int{S("a"): 1, S /* ERROR "duplicate key" */ ("a"): 1} - - type I interface { - f() - } - - _ = map[I]int{N(0): 1, N(2): 1} - _ = map[I]int{N(2): 1, N /* ERROR "duplicate key" */ (2): 1} - - // map keys must be resolved correctly - key1 := "foo" - _ = M0{key1: 1} - _ = M0{key2: 2} - _ = M0{key3 /* ERROR "undeclared name" */ : 2} - - var value int - _ = M1{true: 1, false: 0} - _ = M2{nil: 0, &value: 1} - - // composite literal element types may be elided - type T [2]int - _ = map[int]T{0: T{3, 4}, 1: {5, 6}} - - // recursively so - _ = map[int][]T{0: {}, 1: {{}, T{1, 2}}} - - // composite literal key types may be elided - _ = map[T]int{T{3, 4}: 0, {5, 6}: 1} - - // recursively so - _ = map[[2]T]int{{}: 0, {{}}: 1, [2]T{{}}: 2, {T{1, 2}}: 3} - - // composite literal element and key types may be elided - _ = map[T]T{{}: {}, {1, 2}: T{3, 4}, T{4, 5}: {}} - _ = map[T]M0{{} : {}, T{1, 2}: M0{"foo": 0}, {1, 3}: {"foo": 1}} - - // recursively so - _ = map[[2]T][]T{{}: {}, {{}}: {{}, T{1, 2}}, [2]T{{}}: nil, {T{1, 2}}: {{}, {}}} - - // from the spec - type Point struct { x, y float32 } - _ = map[string]Point{"orig": {0, 0}} - _ = map[*Point]string{{0, 0}: "orig"} -} - -var key2 string = "bar" - -type I interface { - m() -} - -type I2 interface { - m(int) -} - -type T1 struct{} -type T2 struct{} - -func (T2) m(int) {} - -type mybool bool - -func type_asserts() { - var x int - _ = x /* ERROR "not an interface" */ .(int) - - var e interface{} - var ok bool - x, ok = e.(int) - _ = ok - - // ok value is of type bool - var myok mybool - _, myok = e.(int) - _ = myok - - var t I - _ = t /* ERROR "use of .* outside type switch" */ .(type) - _ = t /* ERROR "missing method m" */ .(T) - _ = t.(*T) - _ = t /* ERROR "missing method m" */ .(T1) - _ = t /* ERROR "wrong type for method m" */ .(T2) - _ = t /* STRICT "wrong type for method m" */ .(I2) // only an error in strict mode (issue 8561) - - // e doesn't statically have an m, but may have one dynamically. - _ = e.(I2) -} - -func f0() {} -func f1(x int) {} -func f2(u float32, s string) {} -func fs(s []byte) {} -func fv(x ...int) {} -func fi(x ... interface{}) {} -func (T) fm(x ...int) - -func g0() {} -func g1() int { return 0} -func g2() (u float32, s string) { return } -func gs() []byte { return nil } - -func _calls() { - var x int - var y float32 - var s []int - - f0() - _ = f0 /* ERROR "used as value" */ () - f0(g0 /* ERROR "too many arguments" */ ) - - f1(0) - f1(x) - f1(10.0) - f1() /* ERROR "too few arguments" */ - f1(x, y /* ERROR "too many arguments" */ ) - f1(s /* ERROR "cannot pass" */ ) - f1(x ... /* ERROR "cannot use ..." */ ) - f1(g0 /* ERROR "used as value" */ ()) - f1(g1()) - // f1(g2()) // TODO(gri) missing position in error message - - f2() /* ERROR "too few arguments" */ - f2(3.14) /* ERROR "too few arguments" */ - f2(3.14, "foo") - f2(x /* ERROR "cannot pass" */ , "foo") - f2(g0 /* ERROR "used as value" */ ()) - f2(g1 /* ERROR "cannot pass" */ ()) /* ERROR "too few arguments" */ - f2(g2()) - - fs() /* ERROR "too few arguments" */ - fs(g0 /* ERROR "used as value" */ ()) - fs(g1 /* ERROR "cannot pass" */ ()) - fs(g2 /* ERROR "cannot pass" */ /* ERROR "too many arguments" */ ()) - fs(gs()) - - fv() - fv(1, 2.0, x) - fv(s /* ERROR "cannot pass" */ ) - fv(s...) - fv(x /* ERROR "cannot use" */ ...) - fv(1, s... /* ERROR "can only use ... with matching parameter" */ ) - fv(gs /* ERROR "cannot pass" */ ()) - fv(gs /* ERROR "cannot pass" */ ()...) - - var t T - t.fm() - t.fm(1, 2.0, x) - t.fm(s /* ERROR "cannot pass" */ ) - t.fm(g1()) - t.fm(1, s... /* ERROR "can only use ... with matching parameter" */ ) - t.fm(gs /* ERROR "cannot pass" */ ()) - t.fm(gs /* ERROR "cannot pass" */ ()...) - - T.fm(t, ) - T.fm(t, 1, 2.0, x) - T.fm(t, s /* ERROR "cannot pass" */ ) - T.fm(t, g1()) - T.fm(t, 1, s... /* ERROR "can only use ... with matching parameter" */ ) - T.fm(t, gs /* ERROR "cannot pass" */ ()) - T.fm(t, gs /* ERROR "cannot pass" */ ()...) - - var i interface{ fm(x ...int) } = t - i.fm() - i.fm(1, 2.0, x) - i.fm(s /* ERROR "cannot pass" */ ) - i.fm(g1()) - i.fm(1, s... /* ERROR "can only use ... with matching parameter" */ ) - i.fm(gs /* ERROR "cannot pass" */ ()) - i.fm(gs /* ERROR "cannot pass" */ ()...) - - fi() - fi(1, 2.0, x, 3.14, "foo") - fi(g2()) - fi(0, g2) - fi(0, g2 /* ERROR "2-valued expression" */ ()) -} - -func issue6344() { - type T []interface{} - var x T - fi(x...) // ... applies also to named slices -} diff --git a/go/types/testdata/gotos.src b/go/types/testdata/gotos.src deleted file mode 100644 index 0c7ee44056..0000000000 --- a/go/types/testdata/gotos.src +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2011 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. - -// This file is a modified copy of $GOROOT/test/goto.go. - -package gotos - -var ( - i, n int - x []int - c chan int - m map[int]int - s string -) - -// goto after declaration okay -func _() { - x := 1 - goto L -L: - _ = x -} - -// goto before declaration okay -func _() { - goto L -L: - x := 1 - _ = x -} - -// goto across declaration not okay -func _() { - goto L /* ERROR "goto L jumps over variable declaration at line 36" */ - x := 1 - _ = x -L: -} - -// goto across declaration in inner scope okay -func _() { - goto L - { - x := 1 - _ = x - } -L: -} - -// goto across declaration after inner scope not okay -func _() { - goto L /* ERROR "goto L jumps over variable declaration at line 58" */ - { - x := 1 - _ = x - } - x := 1 - _ = x -L: -} - -// goto across declaration in reverse okay -func _() { -L: - x := 1 - _ = x - goto L -} - -func _() { -L: L1: - x := 1 - _ = x - goto L - goto L1 -} - -// error shows first offending variable -func _() { - goto L /* ERROR "goto L jumps over variable declaration at line 84" */ - x := 1 - _ = x - y := 1 - _ = y -L: -} - -// goto not okay even if code path is dead -func _() { - goto L /* ERROR "goto L jumps over variable declaration" */ - x := 1 - _ = x - y := 1 - _ = y - return -L: -} - -// goto into outer block okay -func _() { - { - goto L - } -L: -} - -func _() { - { - goto L - goto L1 - } -L: L1: -} - -// goto backward into outer block okay -func _() { -L: - { - goto L - } -} - -func _() { -L: L1: - { - goto L - goto L1 - } -} - -// goto into inner block not okay -func _() { - goto L /* ERROR "goto L jumps into block" */ - { - L: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - goto L1 /* ERROR "goto L1 jumps into block" */ - { - L: L1: - } -} - -// goto backward into inner block still not okay -func _() { - { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - { - L: L1: - } - goto L /* ERROR "goto L jumps into block" */ - goto L1 /* ERROR "goto L1 jumps into block" */ -} - -// error shows first (outermost) offending block -func _() { - goto L /* ERROR "goto L jumps into block" */ - { - { - { - L: - } - } - } -} - -// error prefers block diagnostic over declaration diagnostic -func _() { - goto L /* ERROR "goto L jumps into block" */ - x := 1 - _ = x - { - L: - } -} - -// many kinds of blocks, all invalid to jump into or among, -// but valid to jump out of - -// if - -func _() { -L: - if true { - goto L - } -} - -func _() { -L: - if true { - goto L - } else { - } -} - -func _() { -L: - if false { - } else { - goto L - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - if true { - L: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - if true { - L: - } else { - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - if true { - } else { - L: - } -} - -func _() { - if false { - L: - } else { - goto L /* ERROR "goto L jumps into block" */ - } -} - -func _() { - if true { - goto L /* ERROR "goto L jumps into block" */ - } else { - L: - } -} - -func _() { - if true { - goto L /* ERROR "goto L jumps into block" */ - } else if false { - L: - } -} - -func _() { - if true { - goto L /* ERROR "goto L jumps into block" */ - } else if false { - L: - } else { - } -} - -func _() { - if true { - goto L /* ERROR "goto L jumps into block" */ - } else if false { - } else { - L: - } -} - -func _() { - if true { - goto L /* ERROR "goto L jumps into block" */ - } else { - L: - } -} - -func _() { - if true { - L: - } else { - goto L /* ERROR "goto L jumps into block" */ - } -} - -// for - -func _() { - for { - goto L - } -L: -} - -func _() { - for { - goto L - L: - } -} - -func _() { - for { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for { - goto L - L1: - } -L: - goto L1 /* ERROR "goto L1 jumps into block" */ -} - -func _() { - for i < n { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for i = 0; i < n; i++ { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for i = range x { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for i = range c { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for i = range m { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -func _() { - for i = range s { - L: - } - goto L /* ERROR "goto L jumps into block" */ -} - -// switch - -func _() { -L: - switch i { - case 0: - goto L - } -} - -func _() { -L: - switch i { - case 0: - - default: - goto L - } -} - -func _() { - switch i { - case 0: - - default: - L: - goto L - } -} - -func _() { - switch i { - case 0: - - default: - goto L - L: - } -} - -func _() { - switch i { - case 0: - goto L - L: - ; - default: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - switch i { - case 0: - L: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - switch i { - case 0: - L: - ; - default: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - switch i { - case 0: - default: - L: - } -} - -func _() { - switch i { - default: - goto L /* ERROR "goto L jumps into block" */ - case 0: - L: - } -} - -func _() { - switch i { - case 0: - L: - ; - default: - goto L /* ERROR "goto L jumps into block" */ - } -} - -// select -// different from switch. the statement has no implicit block around it. - -func _() { -L: - select { - case <-c: - goto L - } -} - -func _() { -L: - select { - case c <- 1: - - default: - goto L - } -} - -func _() { - select { - case <-c: - - default: - L: - goto L - } -} - -func _() { - select { - case c <- 1: - - default: - goto L - L: - } -} - -func _() { - select { - case <-c: - goto L - L: - ; - default: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - select { - case c <- 1: - L: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - select { - case c <- 1: - L: - ; - default: - } -} - -func _() { - goto L /* ERROR "goto L jumps into block" */ - select { - case <-c: - default: - L: - } -} - -func _() { - select { - default: - goto L /* ERROR "goto L jumps into block" */ - case <-c: - L: - } -} - -func _() { - select { - case <-c: - L: - ; - default: - goto L /* ERROR "goto L jumps into block" */ - } -} diff --git a/go/types/testdata/importdecl0a.src b/go/types/testdata/importdecl0a.src deleted file mode 100644 index 463dcd083d..0000000000 --- a/go/types/testdata/importdecl0a.src +++ /dev/null @@ -1,53 +0,0 @@ -// 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 importdecl0 - -import () - -import ( - // we can have multiple blank imports (was bug) - _ "math" - _ "net/rpc" - init /* ERROR "cannot declare init" */ "fmt" - // reflect defines a type "flag" which shows up in the gc export data - "reflect" - . /* ERROR "imported but not used" */ "reflect" -) - -import "math" /* ERROR "imported but not used" */ -import m /* ERROR "imported but not used as m" */ "math" -import _ "math" - -import ( - "math/big" /* ERROR "imported but not used" */ - b /* ERROR "imported but not used" */ "math/big" - _ "math/big" -) - -import "fmt" -import f1 "fmt" -import f2 "fmt" - -// reflect.flag must not be visible in this package -type flag int -type _ reflect /* ERROR "not exported" */ .flag - -// imported package name may conflict with local objects -type reflect /* ERROR "reflect already declared" */ int - -// dot-imported exported objects may conflict with local objects -type Value /* ERROR "Value already declared through dot-import of package reflect" */ struct{} - -var _ = fmt.Println // use "fmt" - -func _() { - f1.Println() // use "fmt" -} - -func _() { - _ = func() { - f2.Println() // use "fmt" - } -} diff --git a/go/types/testdata/importdecl0b.src b/go/types/testdata/importdecl0b.src deleted file mode 100644 index 6844e70982..0000000000 --- a/go/types/testdata/importdecl0b.src +++ /dev/null @@ -1,33 +0,0 @@ -// 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 importdecl0 - -import "math" -import m "math" - -import . "testing" // declares T in file scope -import . /* ERROR "imported but not used" */ "unsafe" -import . "fmt" // declares Println in file scope - -import ( - // TODO(gri) At the moment, 2 errors are reported because both go/parser - // and the type checker report it. Eventually, this test should not be - // done by the parser anymore. - "" /* ERROR invalid import path */ /* ERROR invalid import path */ - "a!b" /* ERROR invalid import path */ /* ERROR invalid import path */ - "abc\xffdef" /* ERROR invalid import path */ /* ERROR invalid import path */ -) - -// using "math" in this file doesn't affect its use in other files -const Pi0 = math.Pi -const Pi1 = m.Pi - -type _ T // use "testing" - -func _() func() interface{} { - return func() interface{} { - return Println // use "fmt" - } -} diff --git a/go/types/testdata/importdecl1a.src b/go/types/testdata/importdecl1a.src deleted file mode 100644 index 8301820dda..0000000000 --- a/go/types/testdata/importdecl1a.src +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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. - -// Test case for issue 8969. - -package importdecl1 - -import . "unsafe" - -var _ Pointer // use dot-imported package unsafe diff --git a/go/types/testdata/importdecl1b.src b/go/types/testdata/importdecl1b.src deleted file mode 100644 index f24bb9ade9..0000000000 --- a/go/types/testdata/importdecl1b.src +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2014 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 importdecl1 - -import . /* ERROR "imported but not used" */ "unsafe" diff --git a/go/types/testdata/init0.src b/go/types/testdata/init0.src deleted file mode 100644 index ef0349c70f..0000000000 --- a/go/types/testdata/init0.src +++ /dev/null @@ -1,106 +0,0 @@ -// 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. - -// initialization cycles - -package init0 - -// initialization cycles (we don't know the types) -const ( - s0 /* ERROR initialization cycle */ = s0 - - x0 /* ERROR initialization cycle */ = y0 - y0 = x0 - - a0 = b0 - b0 /* ERROR initialization cycle */ = c0 - c0 = d0 - d0 = b0 -) - -var ( - s1 /* ERROR initialization cycle */ = s1 - - x1 /* ERROR initialization cycle */ = y1 - y1 = x1 - - a1 = b1 - b1 /* ERROR initialization cycle */ = c1 - c1 = d1 - d1 = b1 -) - -// initialization cycles (we know the types) -const ( - s2 /* ERROR initialization cycle */ int = s2 - - x2 /* ERROR initialization cycle */ int = y2 - y2 = x2 - - a2 = b2 - b2 /* ERROR initialization cycle */ int = c2 - c2 = d2 - d2 = b2 -) - -var ( - s3 /* ERROR initialization cycle */ int = s3 - - x3 /* ERROR initialization cycle */ int = y3 - y3 = x3 - - a3 = b3 - b3 /* ERROR initialization cycle */ int = c3 - c3 = d3 - d3 = b3 -) - -// cycles via struct fields - -type S1 struct { - f int -} -const cx3 S1 /* ERROR invalid constant type */ = S1{cx3.f} -var vx3 /* ERROR initialization cycle */ S1 = S1{vx3.f} - -// cycles via functions - -var x4 = x5 -var x5 /* ERROR initialization cycle */ = f1() -func f1() int { return x5*10 } - -var x6, x7 /* ERROR initialization cycle */ = f2() -var x8 = x7 -func f2() (int, int) { return f3() + f3(), 0 } -func f3() int { return x8 } - -// cycles via closures - -var x9 /* ERROR initialization cycle */ = func() int { return x9 }() - -var x10 /* ERROR initialization cycle */ = f4() - -func f4() int { - _ = func() { - _ = x10 - } - return 0 -} - -// cycles via method expressions - -type T1 struct{} - -func (T1) m() bool { _ = x11; return false } - -var x11 /* ERROR initialization cycle */ = T1.m(T1{}) - -// cycles via method values - -type T2 struct{} - -func (T2) m() bool { _ = x12; return false } - -var t1 T2 -var x12 /* ERROR initialization cycle */ = t1.m diff --git a/go/types/testdata/init1.src b/go/types/testdata/init1.src deleted file mode 100644 index 39ca31466b..0000000000 --- a/go/types/testdata/init1.src +++ /dev/null @@ -1,97 +0,0 @@ -// 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. - -// initialization cycles - -package init1 - -// issue 6683 (marked as WorkingAsIntended) - -type T0 struct{} - -func (T0) m() int { return y0 } - -var x0 = T0{} - -var y0 /* ERROR initialization cycle */ = x0.m() - -type T1 struct{} - -func (T1) m() int { return y1 } - -var x1 interface { - m() int -} = T1{} - -var y1 = x1.m() // no cycle reported, x1 is of interface type - -// issue 6703 (modified) - -var x2 /* ERROR initialization cycle */ = T2.m - -var y2 = x2 - -type T2 struct{} - -func (T2) m() int { - _ = y2 - return 0 -} - -var x3 /* ERROR initialization cycle */ = T3.m(T3{}) // <<<< added (T3{}) - -var y3 = x3 - -type T3 struct{} - -func (T3) m() int { - _ = y3 - return 0 -} - -var x4 /* ERROR initialization cycle */ = T4{}.m // <<<< added {} - -var y4 = x4 - -type T4 struct{} - -func (T4) m() int { - _ = y4 - return 0 -} - -var x5 /* ERROR initialization cycle */ = T5{}.m() // <<<< added () - -var y5 = x5 - -type T5 struct{} - -func (T5) m() int { - _ = y5 - return 0 -} - -// issue 4847 -// simplified test case - -var x6 = f6 -var y6 /* ERROR initialization cycle */ = f6 -func f6() { _ = y6 } - -// full test case - -type ( - E int - S int -) - -type matcher func(s *S) E - -func matchList(s *S) E { return matcher(matchAnyFn)(s) } - -var foo = matcher(matchList) - -var matchAny /* ERROR initialization cycle */ = matcher(matchList) - -func matchAnyFn(s *S) (err E) { return matchAny(s) } \ No newline at end of file diff --git a/go/types/testdata/init2.src b/go/types/testdata/init2.src deleted file mode 100644 index 614db6c949..0000000000 --- a/go/types/testdata/init2.src +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2014 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. - -// initialization cycles - -package init2 - -// cycles through functions - -func f1() int { _ = x1; return 0 } -var x1 /* ERROR initialization cycle */ = f1 - -func f2() int { _ = x2; return 0 } -var x2 /* ERROR initialization cycle */ = f2() - -// cycles through method expressions - -type T3 int -func (T3) m() int { _ = x3; return 0 } -var x3 /* ERROR initialization cycle */ = T3.m - -type T4 int -func (T4) m() int { _ = x4; return 0 } -var x4 /* ERROR initialization cycle */ = T4.m(0) - -type T3p int -func (*T3p) m() int { _ = x3p; return 0 } -var x3p /* ERROR initialization cycle */ = (*T3p).m - -type T4p int -func (*T4p) m() int { _ = x4p; return 0 } -var x4p /* ERROR initialization cycle */ = (*T4p).m(nil) - -// cycles through method expressions of embedded methods - -type T5 struct { E5 } -type E5 int -func (E5) m() int { _ = x5; return 0 } -var x5 /* ERROR initialization cycle */ = T5.m - -type T6 struct { E6 } -type E6 int -func (E6) m() int { _ = x6; return 0 } -var x6 /* ERROR initialization cycle */ = T6.m(T6{0}) - -type T5p struct { E5p } -type E5p int -func (*E5p) m() int { _ = x5p; return 0 } -var x5p /* ERROR initialization cycle */ = (*T5p).m - -type T6p struct { E6p } -type E6p int -func (*E6p) m() int { _ = x6p; return 0 } -var x6p /* ERROR initialization cycle */ = (*T6p).m(nil) - -// cycles through method values - -type T7 int -func (T7) m() int { _ = x7; return 0 } -var x7 /* ERROR initialization cycle */ = T7(0).m - -type T8 int -func (T8) m() int { _ = x8; return 0 } -var x8 /* ERROR initialization cycle */ = T8(0).m() - -type T7p int -func (*T7p) m() int { _ = x7p; return 0 } -var x7p /* ERROR initialization cycle */ = new(T7p).m - -type T8p int -func (*T8p) m() int { _ = x8p; return 0 } -var x8p /* ERROR initialization cycle */ = new(T8p).m() - -type T7v int -func (T7v) m() int { _ = x7v; return 0 } -var x7var T7v -var x7v /* ERROR initialization cycle */ = x7var.m - -type T8v int -func (T8v) m() int { _ = x8v; return 0 } -var x8var T8v -var x8v /* ERROR initialization cycle */ = x8var.m() - -type T7pv int -func (*T7pv) m() int { _ = x7pv; return 0 } -var x7pvar *T7pv -var x7pv /* ERROR initialization cycle */ = x7pvar.m - -type T8pv int -func (*T8pv) m() int { _ = x8pv; return 0 } -var x8pvar *T8pv -var x8pv /* ERROR initialization cycle */ = x8pvar.m() - -// cycles through method values of embedded methods - -type T9 struct { E9 } -type E9 int -func (E9) m() int { _ = x9; return 0 } -var x9 /* ERROR initialization cycle */ = T9{0}.m - -type T10 struct { E10 } -type E10 int -func (E10) m() int { _ = x10; return 0 } -var x10 /* ERROR initialization cycle */ = T10{0}.m() - -type T9p struct { E9p } -type E9p int -func (*E9p) m() int { _ = x9p; return 0 } -var x9p /* ERROR initialization cycle */ = new(T9p).m - -type T10p struct { E10p } -type E10p int -func (*E10p) m() int { _ = x10p; return 0 } -var x10p /* ERROR initialization cycle */ = new(T10p).m() - -type T9v struct { E9v } -type E9v int -func (E9v) m() int { _ = x9v; return 0 } -var x9var T9v -var x9v /* ERROR initialization cycle */ = x9var.m - -type T10v struct { E10v } -type E10v int -func (E10v) m() int { _ = x10v; return 0 } -var x10var T10v -var x10v /* ERROR initialization cycle */ = x10var.m() - -type T9pv struct { E9pv } -type E9pv int -func (*E9pv) m() int { _ = x9pv; return 0 } -var x9pvar *T9pv -var x9pv /* ERROR initialization cycle */ = x9pvar.m - -type T10pv struct { E10pv } -type E10pv int -func (*E10pv) m() int { _ = x10pv; return 0 } -var x10pvar *T10pv -var x10pv /* ERROR initialization cycle */ = x10pvar.m() diff --git a/go/types/testdata/issues.src b/go/types/testdata/issues.src deleted file mode 100644 index 595a6342b7..0000000000 --- a/go/types/testdata/issues.src +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2014 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 issues - -import "fmt" - -func issue7035() { - type T struct{ X int } - _ = func() { - fmt.Println() // must refer to imported fmt rather than the fmt below - } - fmt := new(T) - _ = fmt.X -} - -func issue8066() { - const ( - // TODO(gri) Enable test below for releases 1.4 and higher - // _ = float32(340282356779733661637539395458142568447) - _ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ ) - ) -} - -// Check that a missing identifier doesn't lead to a spurious error cascade. -func issue8799a() { - x, ok := missing /* ERROR undeclared */ () - _ = !ok - _ = x -} - -func issue8799b(x int, ok bool) { - x, ok = missing /* ERROR undeclared */ () - _ = !ok - _ = x -} - -func issue9182() { - type Point C /* ERROR undeclared */ .Point - // no error for composite literal based on unknown type - _ = Point{x: 1, y: 2} -} - -func f0() (a []int) { return } -func f1() (a []int, b int) { return } -func f2() (a, b []int) { return } - -func append_([]int, ...int) {} - -func issue9473(a []int, b ...int) { - // variadic builtin function - _ = append(f0()) - _ = append(f0(), f0()...) - _ = append(f1()) - _ = append(f2 /* ERROR cannot pass argument */ ()) - _ = append(f2()... /* ERROR cannot use ... */ ) - _ = append(f0(), f1 /* ERROR 2-valued expression */ ()) - _ = append(f0(), f2 /* ERROR 2-valued expression */ ()) - _ = append(f0(), f1()... /* ERROR cannot use ... */ ) - _ = append(f0(), f2()... /* ERROR cannot use ... */ ) - - // variadic user-defined function - append_(f0()) - append_(f0(), f0()...) - append_(f1()) - append_(f2 /* ERROR cannot pass argument */ ()) - append_(f2()... /* ERROR cannot use ... */ ) - append_(f0(), f1 /* ERROR 2-valued expression */ ()) - append_(f0(), f2 /* ERROR 2-valued expression */ ()) - append_(f0(), f1()... /* ERROR cannot use */ ) - append_(f0(), f2()... /* ERROR cannot use */ ) -} - -// Check that embedding a non-interface type in an interface results in a good error message. -func issue10979() { - type _ interface { - int /* ERROR int is not an interface */ - } - type T struct{} - type _ interface { - T /* ERROR T is not an interface */ - } - type _ interface { - nosuchtype /* ERROR undeclared name: nosuchtype */ - } - type _ interface { - fmt /* ERROR Nosuchtype not declared by package fmt */ .Nosuchtype - } - type _ interface { - nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype - } - type I interface { - I /* ERROR I\.m \(value of type func\(I\)\) is not a type */ .m - m() - } -} diff --git a/go/types/testdata/labels.src b/go/types/testdata/labels.src deleted file mode 100644 index 102ffc7c17..0000000000 --- a/go/types/testdata/labels.src +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2011 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. - -// This file is a modified concatenation of the files -// $GOROOT/test/label.go and $GOROOT/test/label1.go. - -package labels - -var x int - -func f0() { -L1 /* ERROR "label L1 declared but not used" */ : - for { - } -L2 /* ERROR "label L2 declared but not used" */ : - select { - } -L3 /* ERROR "label L3 declared but not used" */ : - switch { - } -L4 /* ERROR "label L4 declared but not used" */ : - if true { - } -L5 /* ERROR "label L5 declared but not used" */ : - f0() -L6: - f0() -L6 /* ERROR "label L6 already declared" */ : - f0() - if x == 20 { - goto L6 - } - -L7: - for { - break L7 - break L8 /* ERROR "invalid break label L8" */ - } - -// A label must be directly associated with a switch, select, or -// for statement; it cannot be the label of a labeled statement. - -L7a /* ERROR "declared but not used" */ : L7b: - for { - break L7a /* ERROR "invalid break label L7a" */ - continue L7a /* ERROR "invalid continue label L7a" */ - continue L7b - } - -L8: - for { - if x == 21 { - continue L8 - continue L7 /* ERROR "invalid continue label L7" */ - } - } - -L9: - switch { - case true: - break L9 - defalt /* ERROR "label defalt declared but not used" */ : - } - -L10: - select { - default: - break L10 - break L9 /* ERROR "invalid break label L9" */ - } - - goto L10a -L10a: L10b: - select { - default: - break L10a /* ERROR "invalid break label L10a" */ - break L10b - continue L10b /* ERROR "invalid continue label L10b" */ - } -} - -func f1() { -L1: - for { - if x == 0 { - break L1 - } - if x == 1 { - continue L1 - } - goto L1 - } - -L2: - select { - default: - if x == 0 { - break L2 - } - if x == 1 { - continue L2 /* ERROR "invalid continue label L2" */ - } - goto L2 - } - -L3: - switch { - case x > 10: - if x == 11 { - break L3 - } - if x == 12 { - continue L3 /* ERROR "invalid continue label L3" */ - } - goto L3 - } - -L4: - if true { - if x == 13 { - break L4 /* ERROR "invalid break label L4" */ - } - if x == 14 { - continue L4 /* ERROR "invalid continue label L4" */ - } - if x == 15 { - goto L4 - } - } - -L5: - f1() - if x == 16 { - break L5 /* ERROR "invalid break label L5" */ - } - if x == 17 { - continue L5 /* ERROR "invalid continue label L5" */ - } - if x == 18 { - goto L5 - } - - for { - if x == 19 { - break L1 /* ERROR "invalid break label L1" */ - } - if x == 20 { - continue L1 /* ERROR "invalid continue label L1" */ - } - if x == 21 { - goto L1 - } - } -} - -// Additional tests not in the original files. - -func f2() { -L1 /* ERROR "label L1 declared but not used" */ : - if x == 0 { - for { - continue L1 /* ERROR "invalid continue label L1" */ - } - } -} - -func f3() { -L1: -L2: -L3: - for { - break L1 /* ERROR "invalid break label L1" */ - break L2 /* ERROR "invalid break label L2" */ - break L3 - continue L1 /* ERROR "invalid continue label L1" */ - continue L2 /* ERROR "invalid continue label L2" */ - continue L3 - goto L1 - goto L2 - goto L3 - } -} - -// Blank labels are never declared. - -func f4() { -_: -_: // multiple blank labels are ok - goto _ /* ERROR "label _ not declared" */ -} - -func f5() { -_: - for { - break _ /* ERROR "invalid break label _" */ - continue _ /* ERROR "invalid continue label _" */ - } -} - -func f6() { -_: - switch { - default: - break _ /* ERROR "invalid break label _" */ - } -} diff --git a/go/types/testdata/methodsets.src b/go/types/testdata/methodsets.src deleted file mode 100644 index 89211468ea..0000000000 --- a/go/types/testdata/methodsets.src +++ /dev/null @@ -1,214 +0,0 @@ -// 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 methodsets - -type T0 struct {} - -func (T0) v0() {} -func (*T0) p0() {} - -type T1 struct {} // like T0 with different method names - -func (T1) v1() {} -func (*T1) p1() {} - -type T2 interface { - v2() - p2() -} - -type T3 struct { - T0 - *T1 - T2 -} - -// Method expressions -func _() { - var ( - _ func(T0) = T0.v0 - _ = T0 /* ERROR "not in method set" */ .p0 - - _ func (*T0) = (*T0).v0 - _ func (*T0) = (*T0).p0 - - // T1 is like T0 - - _ func(T2) = T2.v2 - _ func(T2) = T2.p2 - - _ func(T3) = T3.v0 - _ func(T3) = T3 /* ERROR "not in method set" */ .p0 - _ func(T3) = T3.v1 - _ func(T3) = T3.p1 - _ func(T3) = T3.v2 - _ func(T3) = T3.p2 - - _ func(*T3) = (*T3).v0 - _ func(*T3) = (*T3).p0 - _ func(*T3) = (*T3).v1 - _ func(*T3) = (*T3).p1 - _ func(*T3) = (*T3).v2 - _ func(*T3) = (*T3).p2 - ) -} - -// Method values with addressable receivers -func _() { - var ( - v0 T0 - _ func() = v0.v0 - _ func() = v0.p0 - ) - - var ( - p0 *T0 - _ func() = p0.v0 - _ func() = p0.p0 - ) - - // T1 is like T0 - - var ( - v2 T2 - _ func() = v2.v2 - _ func() = v2.p2 - ) - - var ( - v4 T3 - _ func() = v4.v0 - _ func() = v4.p0 - _ func() = v4.v1 - _ func() = v4.p1 - _ func() = v4.v2 - _ func() = v4.p2 - ) - - var ( - p4 *T3 - _ func() = p4.v0 - _ func() = p4.p0 - _ func() = p4.v1 - _ func() = p4.p1 - _ func() = p4.v2 - _ func() = p4.p2 - ) -} - -// Method calls with addressable receivers -func _() { - var v0 T0 - v0.v0() - v0.p0() - - var p0 *T0 - p0.v0() - p0.p0() - - // T1 is like T0 - - var v2 T2 - v2.v2() - v2.p2() - - var v4 T3 - v4.v0() - v4.p0() - v4.v1() - v4.p1() - v4.v2() - v4.p2() - - var p4 *T3 - p4.v0() - p4.p0() - p4.v1() - p4.p1() - p4.v2() - p4.p2() -} - -// Method values with value receivers -func _() { - var ( - _ func() = T0{}.v0 - _ func() = T0 /* ERROR "not in method set" */ {}.p0 - - _ func() = (&T0{}).v0 - _ func() = (&T0{}).p0 - - // T1 is like T0 - - // no values for T2 - - _ func() = T3{}.v0 - _ func() = T3 /* ERROR "not in method set" */ {}.p0 - _ func() = T3{}.v1 - _ func() = T3{}.p1 - _ func() = T3{}.v2 - _ func() = T3{}.p2 - - _ func() = (&T3{}).v0 - _ func() = (&T3{}).p0 - _ func() = (&T3{}).v1 - _ func() = (&T3{}).p1 - _ func() = (&T3{}).v2 - _ func() = (&T3{}).p2 - ) -} - -// Method calls with value receivers -func _() { - T0{}.v0() - T0 /* ERROR "not in method set" */ {}.p0() - - (&T0{}).v0() - (&T0{}).p0() - - // T1 is like T0 - - // no values for T2 - - T3{}.v0() - T3 /* ERROR "not in method set" */ {}.p0() - T3{}.v1() - T3{}.p1() - T3{}.v2() - T3{}.p2() - - (&T3{}).v0() - (&T3{}).p0() - (&T3{}).v1() - (&T3{}).p1() - (&T3{}).v2() - (&T3{}).p2() -} - -// *T has no methods if T is an interface type -func issue5918() { - var ( - err error - _ = err.Error() - _ func() string = err.Error - _ func(error) string = error.Error - - perr = &err - _ = perr /* ERROR "no field or method" */ .Error() - _ func() string = perr /* ERROR "no field or method" */ .Error - _ func(*error) string = ( /* ERROR "no field or method" */ *error).Error - ) - - type T *interface{ m() int } - var ( - x T - _ = (*x).m() - _ = (*x).m - - _ = x /* ERROR "no field or method" */ .m() - _ = x /* ERROR "no field or method" */ .m - _ = T /* ERROR "no field or method" */ .m - ) -} diff --git a/go/types/testdata/shifts.src b/go/types/testdata/shifts.src deleted file mode 100644 index fa4de9e899..0000000000 --- a/go/types/testdata/shifts.src +++ /dev/null @@ -1,341 +0,0 @@ -// 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 shifts - -func shifts0() { - // basic constant shifts - const ( - s = 10 - _ = 0<<0 - _ = 1<> s) - _, _, _ = u, v, x -} - -func shifts4() { - // shifts in comparisons w/ untyped operands - var s uint - - _ = 1<> 1.1 /* ERROR "must be unsigned integer" */ // example from issue 11325 - _ = 0 >> 1.1 /* ERROR "must be unsigned integer" */ - _ = 0 << 1.1 /* ERROR "must be unsigned integer" */ - _ = 0 >> 1. - _ = 1 >> 1.1 /* ERROR "must be unsigned integer" */ - _ = 1 >> 1. - _ = 1. >> 1 - _ = 1. >> 1. - _ = 1.1 /* ERROR "must be integer" */ >> 1 -} - -func issue11594() { - var _ = complex64 /* ERROR "must be integer" */ (1) << 2 // example from issue 11594 - _ = float32 /* ERROR "must be integer" */ (0) << 1 - _ = float64 /* ERROR "must be integer" */ (0) >> 2 - _ = complex64 /* ERROR "must be integer" */ (0) << 3 - _ = complex64 /* ERROR "must be integer" */ (0) >> 4 -} diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src deleted file mode 100644 index fd1ddba2ec..0000000000 --- a/go/types/testdata/stmt0.src +++ /dev/null @@ -1,833 +0,0 @@ -// Copyright 2012 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. - -// statements - -package stmt0 - -func assignments0() (int, int) { - var a, b, c int - var ch chan int - f0 := func() {} - f1 := func() int { return 1 } - f2 := func() (int, int) { return 1, 2 } - f3 := func() (int, int, int) { return 1, 2, 3 } - - a, b, c = 1, 2, 3 - a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2 - a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2, 3, 4 - _, _, _ = a, b, c - - a = f0 /* ERROR "used as value" */ () - a = f1() - a = f2 /* ERROR "assignment count mismatch" */ () - a, b = f2() - a, b, c = f2 /* ERROR "assignment count mismatch" */ () - a, b, c = f3() - a, b = f3 /* ERROR "assignment count mismatch" */ () - - a, b, c = <- /* ERROR "assignment count mismatch" */ ch - - return /* ERROR "wrong number of return values" */ - return /* ERROR "wrong number of return values" */ 1 - return 1, 2 - return /* ERROR "wrong number of return values" */ 1, 2, 3 -} - -func assignments1() { - b, i, f, c, s := false, 1, 1.0, 1i, "foo" - b = i /* ERROR "cannot assign" */ - i = f /* ERROR "cannot assign" */ - f = c /* ERROR "cannot assign" */ - c = s /* ERROR "cannot assign" */ - s = b /* ERROR "cannot assign" */ - - v0, v1, v2 := 1 /* ERROR "mismatch" */ , 2, 3, 4 - _, _, _ = v0, v1, v2 - - b = true - - i += 1 - i += "foo" /* ERROR "cannot convert.*int" */ - - f -= 1 - f /= 0 - f = float32(0)/0 /* ERROR "division by zero" */ - f -= "foo" /* ERROR "cannot convert.*float64" */ - - c *= 1 - c /= 0 - - s += "bar" - s += 1 /* ERROR "cannot convert.*string" */ - - var u64 uint64 - u64 += 1< len(fields) { - panic("more tags than fields") - } - return &Struct{fields: fields, tags: tags} -} - -// NumFields returns the number of fields in the struct (including blank and anonymous fields). -func (s *Struct) NumFields() int { return len(s.fields) } - -// Field returns the i'th field for 0 <= i < NumFields(). -func (s *Struct) Field(i int) *Var { return s.fields[i] } - -// Tag returns the i'th field tag for 0 <= i < NumFields(). -func (s *Struct) Tag(i int) string { - if i < len(s.tags) { - return s.tags[i] - } - return "" -} - -// A Pointer represents a pointer type. -type Pointer struct { - base Type // element type -} - -// NewPointer returns a new pointer type for the given element (base) type. -func NewPointer(elem Type) *Pointer { return &Pointer{base: 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 -} - -// 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] } - -// A Signature represents a (non-builtin) function or method type. -type Signature struct { - // We need to keep the scope in Signature (rather than passing it around - // and store it in the Func Object) because when type-checking a function - // literal we call the general type checker which returns a general Type. - // We then unpack the *Signature and use the scope for the literal body. - scope *Scope // function scope, present for package-local signatures - 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 - variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) -} - -// NewSignature returns a new function type for the given receiver, parameters, -// and results, either of which may be nil. If variadic is set, the function -// is variadic, it must have at least one parameter, and the last parameter -// must be of unnamed slice type. -func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { - if variadic { - n := params.Len() - if n == 0 { - panic("types.NewSignature: variadic function must have at least one parameter") - } - if _, ok := params.At(n - 1).typ.(*Slice); !ok { - panic("types.NewSignature: variadic parameter must be of unnamed slice type") - } - } - return &Signature{nil, recv, params, results, variadic} -} - -// Recv returns the receiver of signature s (if a method), or nil if a -// function. -// -// For an abstract method, Recv returns the enclosing interface either -// as a *Named or an *Interface. Due to embedding, an interface may -// contain methods whose receiver type is a different interface. -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 } - -// Variadic reports whether the signature s is variadic. -func (s *Signature) Variadic() bool { return s.variadic } - -// An Interface represents an interface type. -type Interface struct { - methods []*Func // ordered list of explicitly declared methods - embeddeds []*Named // ordered list of explicitly embedded types - - allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) -} - -// NewInterface returns a new interface for the given methods and embedded types. -func NewInterface(methods []*Func, embeddeds []*Named) *Interface { - typ := new(Interface) - - var mset objset - for _, m := range methods { - if mset.insert(m) != nil { - panic("multiple methods with the same name") - } - // set receiver - // TODO(gri) Ideally, we should use a named type here instead of - // typ, for less verbose printing of interface method signatures. - m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ) - } - sort.Sort(byUniqueMethodName(methods)) - - if embeddeds == nil { - sort.Sort(byUniqueTypeName(embeddeds)) - } - - typ.methods = methods - typ.embeddeds = embeddeds - return typ -} - -// NumExplicitMethods returns the number of explicitly declared methods of interface t. -func (t *Interface) NumExplicitMethods() int { return len(t.methods) } - -// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). -// The methods are ordered by their unique Id. -func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } - -// NumEmbeddeds returns the number of embedded types in interface t. -func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } - -// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). -// The types are ordered by the corresponding TypeName's unique Id. -func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] } - -// NumMethods returns the total number of methods of interface t. -func (t *Interface) NumMethods() int { return len(t.allMethods) } - -// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). -// The methods are ordered by their unique Id. -func (t *Interface) Method(i int) *Func { return t.allMethods[i] } - -// Empty returns true if t is the empty interface. -func (t *Interface) Empty() bool { return len(t.allMethods) == 0 } - -// Complete computes the interface's method set. It must be called by users of -// NewInterface after the interface's embedded types are fully defined and -// before using the interface type in any way other than to form other types. -// Complete returns the receiver. -func (t *Interface) Complete() *Interface { - if t.allMethods != nil { - return t - } - - var allMethods []*Func - if t.embeddeds == nil { - if t.methods == nil { - allMethods = make([]*Func, 0, 1) - } else { - allMethods = t.methods - } - } else { - allMethods = append(allMethods, t.methods...) - for _, et := range t.embeddeds { - it := et.Underlying().(*Interface) - it.Complete() - for _, tm := range it.allMethods { - // Make a copy of the method and adjust its receiver type. - newm := *tm - newmtyp := *tm.typ.(*Signature) - newm.typ = &newmtyp - newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t) - allMethods = append(allMethods, &newm) - } - } - sort.Sort(byUniqueMethodName(allMethods)) - } - t.allMethods = allMethods - - return t -} - -// A Map represents a map type. -type Map struct { - key, elem Type -} - -// 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.elem } - -// A Chan represents a channel type. -type Chan struct { - dir ChanDir - elem Type -} - -// A ChanDir value indicates a channel direction. -type ChanDir int - -// The direction of a channel is indicated by one of the following constants. -const ( - SendRecv ChanDir = iota - SendOnly - RecvOnly -) - -// NewChan returns a new channel type for the given direction and element type. -func NewChan(dir ChanDir, elem Type) *Chan { - return &Chan{dir, elem} -} - -// Dir returns the direction of channel c. -func (c *Chan) Dir() ChanDir { return c.dir } - -// Elem returns the element type of channel c. -func (c *Chan) Elem() Type { return c.elem } - -// A Named represents a named type. -type Named struct { - obj *TypeName // corresponding declared object - underlying Type // possibly a *Named during setup; never a *Named once set up completely - methods []*Func // methods declared for this type (not the method set of this type) -} - -// NewNamed returns a new named type for the given type name, underlying type, and associated methods. -// The underlying type must not be a *Named. -func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { - if _, ok := underlying.(*Named); ok { - panic("types.NewNamed: underlying type must not be *Named") - } - typ := &Named{obj: obj, underlying: underlying, methods: methods} - if obj.typ == nil { - obj.typ = typ - } - return typ -} - -// Obj returns the type name for the named type t. -func (t *Named) Obj() *TypeName { return t.obj } - -// NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.methods) } - -// 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[i] } - -// SetUnderlying sets the underlying type and marks t as complete. -// TODO(gri) determine if there's a better solution rather than providing this function -func (t *Named) SetUnderlying(underlying Type) { - if underlying == nil { - panic("types.Named.SetUnderlying: underlying type must not be nil") - } - if _, ok := underlying.(*Named); ok { - panic("types.Named.SetUnderlying: underlying type must not be *Named") - } - t.underlying = underlying -} - -// AddMethod adds method m unless it is already in the method list. -// TODO(gri) find a better solution instead of providing this function -func (t *Named) AddMethod(m *Func) { - if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { - t.methods = append(t.methods, m) - } -} - -// 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 *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) String() string { return TypeString(t, nil) } -func (t *Array) String() string { return TypeString(t, nil) } -func (t *Slice) String() string { return TypeString(t, nil) } -func (t *Struct) String() string { return TypeString(t, nil) } -func (t *Pointer) String() string { return TypeString(t, nil) } -func (t *Tuple) String() string { return TypeString(t, nil) } -func (t *Signature) String() string { return TypeString(t, nil) } -func (t *Interface) String() string { return TypeString(t, nil) } -func (t *Map) String() string { return TypeString(t, nil) } -func (t *Chan) String() string { return TypeString(t, nil) } -func (t *Named) String() string { return TypeString(t, nil) } diff --git a/go/types/typestring.go b/go/types/typestring.go deleted file mode 100644 index abee8abb56..0000000000 --- a/go/types/typestring.go +++ /dev/null @@ -1,292 +0,0 @@ -// 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. - -// This file implements printing of types. - -package types - -import ( - "bytes" - "fmt" -) - -// A Qualifier controls how named package-level objects are printed in -// calls to TypeString, ObjectString, and SelectionString. -// -// These three formatting routines call the Qualifier for each -// package-level object O, and if the Qualifier returns a non-empty -// string p, the object is printed in the form p.O. -// If it returns an empty string, only the object name O is printed. -// -// Using a nil Qualifier is equivalent to using (*Package).Path: the -// object is qualified by the import path, e.g., "encoding/json.Marshal". -// -type Qualifier func(*Package) string - -// RelativeTo(pkg) returns a Qualifier that fully qualifies members of -// all packages other than pkg. -func RelativeTo(pkg *Package) Qualifier { - if pkg == nil { - return nil - } - return func(other *Package) string { - if pkg == other { - return "" // same package; unqualified - } - return other.Path() - } -} - -// If GcCompatibilityMode is set, printing of types is modified -// to match the representation of some types in the gc compiler: -// -// - byte and rune lose their alias name and simply stand for -// uint8 and int32 respectively -// - embedded interfaces get flattened (the embedding info is lost, -// and certain recursive interface types cannot be printed anymore) -// -// This makes it easier to compare packages computed with the type- -// checker vs packages imported from gc export data. -// -// Caution: This flag affects all uses of WriteType, globally. -// It is only provided for testing in conjunction with -// gc-generated data. It may be removed at any time. -var GcCompatibilityMode bool - -// TypeString returns the string representation of typ. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func TypeString(typ Type, qf Qualifier) string { - var buf bytes.Buffer - WriteType(&buf, typ, qf) - return buf.String() -} - -// WriteType writes the string representation of typ to buf. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { - writeType(buf, typ, qf, make([]Type, 8)) -} - -func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { - // Theoretically, this is a quadratic lookup algorithm, but in - // practice deeply nested composite types with unnamed component - // types are uncommon. This code is likely more efficient than - // using a map. - for _, t := range visited { - if t == typ { - fmt.Fprintf(buf, "○%T", typ) // cycle to typ - return - } - } - visited = append(visited, typ) - - switch t := typ.(type) { - case nil: - buf.WriteString("") - - case *Basic: - if t.kind == UnsafePointer { - buf.WriteString("unsafe.") - } - if GcCompatibilityMode { - // forget the alias names - switch t.kind { - case Byte: - t = Typ[Uint8] - case Rune: - t = Typ[Int32] - } - } - buf.WriteString(t.name) - - case *Array: - fmt.Fprintf(buf, "[%d]", t.len) - writeType(buf, t.elem, qf, visited) - - case *Slice: - buf.WriteString("[]") - writeType(buf, t.elem, qf, visited) - - case *Struct: - buf.WriteString("struct{") - for i, f := range t.fields { - if i > 0 { - buf.WriteString("; ") - } - if !f.anonymous { - buf.WriteString(f.name) - buf.WriteByte(' ') - } - writeType(buf, f.typ, qf, visited) - if tag := t.Tag(i); tag != "" { - fmt.Fprintf(buf, " %q", tag) - } - } - buf.WriteByte('}') - - case *Pointer: - buf.WriteByte('*') - writeType(buf, t.base, qf, visited) - - case *Tuple: - writeTuple(buf, t, false, qf, visited) - - case *Signature: - buf.WriteString("func") - writeSignature(buf, t, qf, visited) - - case *Interface: - // We write the source-level methods and embedded types rather - // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have anonymous - // interface types that (directly or indirectly) embed the - // current interface. For instance, consider the result type - // of m: - // - // type T interface{ - // m() interface{ T } - // } - // - buf.WriteString("interface{") - if GcCompatibilityMode { - // print flattened interface - // (useful to compare against gc-generated interfaces) - for i, m := range t.allMethods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - } - } else { - // print explicit interface methods and embedded types - for i, m := range t.methods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - } - for i, typ := range t.embeddeds { - if i > 0 || len(t.methods) > 0 { - buf.WriteString("; ") - } - writeType(buf, typ, qf, visited) - } - } - buf.WriteByte('}') - - case *Map: - buf.WriteString("map[") - writeType(buf, t.key, qf, visited) - buf.WriteByte(']') - writeType(buf, t.elem, qf, visited) - - case *Chan: - var s string - var parens bool - switch t.dir { - case SendRecv: - s = "chan " - // chan (<-chan T) requires parentheses - if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly { - parens = true - } - case SendOnly: - s = "chan<- " - case RecvOnly: - s = "<-chan " - default: - panic("unreachable") - } - buf.WriteString(s) - if parens { - buf.WriteByte('(') - } - writeType(buf, t.elem, qf, visited) - if parens { - buf.WriteByte(')') - } - - case *Named: - s := "" - if obj := t.obj; obj != nil { - if obj.pkg != nil { - writePackage(buf, obj.pkg, qf) - } - // TODO(gri): function-local named types should be displayed - // differently from named types at package level to avoid - // ambiguity. - s = obj.name - } - buf.WriteString(s) - - default: - // For externally defined implementations of Type. - buf.WriteString(t.String()) - } -} - -func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) { - buf.WriteByte('(') - if tup != nil { - for i, v := range tup.vars { - if i > 0 { - buf.WriteString(", ") - } - if v.name != "" { - buf.WriteString(v.name) - buf.WriteByte(' ') - } - typ := v.typ - if variadic && i == len(tup.vars)-1 { - if s, ok := typ.(*Slice); ok { - buf.WriteString("...") - typ = s.elem - } else { - // special case: - // append(s, "foo"...) leads to signature func([]byte, string...) - if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String { - panic("internal error: string type expected") - } - writeType(buf, typ, qf, visited) - buf.WriteString("...") - continue - } - } - writeType(buf, typ, qf, visited) - } - } - buf.WriteByte(')') -} - -// WriteSignature writes the representation of the signature sig to buf, -// without a leading "func" keyword. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { - writeSignature(buf, sig, qf, make([]Type, 8)) -} - -func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { - writeTuple(buf, sig.params, sig.variadic, qf, visited) - - n := sig.results.Len() - if n == 0 { - // no result - return - } - - buf.WriteByte(' ') - if n == 1 && sig.results.vars[0].name == "" { - // single unnamed result - writeType(buf, sig.results.vars[0].typ, qf, visited) - return - } - - // multiple or named result(s) - writeTuple(buf, sig.results, false, qf, visited) -} diff --git a/go/types/typestring_test.go b/go/types/typestring_test.go deleted file mode 100644 index 975b62389c..0000000000 --- a/go/types/typestring_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2012 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_test - -import ( - "go/ast" - "go/parser" - "go/token" - "testing" - - _ "golang.org/x/tools/go/gcimporter" - . "golang.org/x/tools/go/types" -) - -const filename = "" - -func makePkg(t *testing.T, src string) (*Package, error) { - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) - if err != nil { - return nil, err - } - // use the package name as package path - return Check(file.Name.Name, fset, []*ast.File{file}) -} - -type testEntry struct { - src, str string -} - -// dup returns a testEntry where both src and str are the same. -func dup(s string) testEntry { - return testEntry{s, s} -} - -// types that don't depend on any other type declarations -var independentTestTypes = []testEntry{ - // basic types - dup("int"), - dup("float32"), - dup("string"), - - // arrays - dup("[10]int"), - - // slices - dup("[]int"), - dup("[][]int"), - - // structs - dup("struct{}"), - dup("struct{x int}"), - {`struct { - x, y int - z float32 "foo" - }`, `struct{x int; y int; z float32 "foo"}`}, - {`struct { - string - elems []complex128 - }`, `struct{string; elems []complex128}`}, - - // pointers - dup("*int"), - dup("***struct{}"), - dup("*struct{a int; b float32}"), - - // functions - dup("func()"), - dup("func(x int)"), - {"func(x, y int)", "func(x int, y int)"}, - {"func(x, y int, z string)", "func(x int, y int, z string)"}, - dup("func(int)"), - {"func(int, string, byte)", "func(int, string, byte)"}, - - dup("func() int"), - {"func() (string)", "func() string"}, - dup("func() (u int)"), - {"func() (u, v int, w string)", "func() (u int, v int, w string)"}, - - dup("func(int) string"), - dup("func(x int) string"), - dup("func(x int) (u string)"), - {"func(x, y int) (u string)", "func(x int, y int) (u string)"}, - - dup("func(...int) string"), - dup("func(x ...int) string"), - dup("func(x ...int) (u string)"), - {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"}, - - // interfaces - dup("interface{}"), - dup("interface{m()}"), - dup(`interface{String() string; m(int) float32}`), - - // maps - dup("map[string]int"), - {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"}, - - // channels - dup("chan<- chan int"), - dup("chan<- <-chan int"), - dup("<-chan <-chan int"), - dup("chan (<-chan int)"), - dup("chan<- func()"), - dup("<-chan []func() int"), -} - -// types that depend on other type declarations (src in TestTypes) -var dependentTestTypes = []testEntry{ - // interfaces - dup(`interface{io.Reader; io.Writer}`), - dup(`interface{m() int; io.Writer}`), - {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`}, -} - -func TestTypeString(t *testing.T) { - skipSpecialPlatforms(t) - - var tests []testEntry - tests = append(tests, independentTestTypes...) - tests = append(tests, dependentTestTypes...) - - for _, test := range tests { - src := `package p; import "io"; type _ io.Writer; type T ` + test.src - pkg, err := makePkg(t, src) - if err != nil { - t.Errorf("%s: %s", src, err) - continue - } - typ := pkg.Scope().Lookup("T").Type().Underlying() - if got := typ.String(); got != test.str { - t.Errorf("%s: got %s, want %s", test.src, got, test.str) - } - } -} - -func TestQualifiedTypeString(t *testing.T) { - p, _ := pkgFor("p.go", "package p; type T int", nil) - q, _ := pkgFor("q.go", "package q", nil) - - pT := p.Scope().Lookup("T").Type() - for _, test := range []struct { - typ Type - this *Package - want string - }{ - {pT, nil, "p.T"}, - {pT, p, "T"}, - {pT, q, "p.T"}, - {NewPointer(pT), p, "*T"}, - {NewPointer(pT), q, "*p.T"}, - } { - qualifier := func(pkg *Package) string { - if pkg != test.this { - return pkg.Name() - } - return "" - } - if got := TypeString(test.typ, qualifier); got != test.want { - t.Errorf("TypeString(%s, %s) = %s, want %s", - test.this, test.typ, got, test.want) - } - } -} diff --git a/go/types/typeutil/imports14.go b/go/types/typeutil/imports14.go deleted file mode 100644 index 9741df36de..0000000000 --- a/go/types/typeutil/imports14.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package typeutil - -import "golang.org/x/tools/go/types" - -// Dependencies returns all dependencies of the specified packages. -// -// Dependent packages appear in topological order: if package P imports -// package Q, Q appears earlier than P in the result. -// The algorithm follows import statements in the order they -// appear in the source code, so the result is a total order. -// -func Dependencies(pkgs ...*types.Package) []*types.Package { - var result []*types.Package - seen := make(map[*types.Package]bool) - var visit func(pkgs []*types.Package) - visit = func(pkgs []*types.Package) { - for _, p := range pkgs { - if !seen[p] { - seen[p] = true - visit(p.Imports()) - result = append(result, p) - } - } - } - visit(pkgs) - return result -} diff --git a/go/types/typeutil/imports14_test.go b/go/types/typeutil/imports14_test.go deleted file mode 100644 index b70f5f048c..0000000000 --- a/go/types/typeutil/imports14_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package typeutil_test - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "testing" - - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -func TestDependencies(t *testing.T) { - packages := make(map[string]*types.Package) - conf := types.Config{ - Packages: packages, - Import: func(_ map[string]*types.Package, path string) (*types.Package, error) { - return packages[path], nil - }, - } - fset := token.NewFileSet() - - // All edges go to the right. - // /--D--B--A - // F \_C_/ - // \__E_/ - for i, content := range []string{ - `package a`, - `package c; import (_ "a")`, - `package b; import (_ "a")`, - `package e; import (_ "c")`, - `package d; import (_ "b"; _ "c")`, - `package f; import (_ "d"; _ "e")`, - } { - f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0) - if err != nil { - t.Fatal(err) - } - pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) - if err != nil { - t.Fatal(err) - } - packages[pkg.Path()] = pkg - } - - for _, test := range []struct { - roots, want string - }{ - {"a", "a"}, - {"b", "ab"}, - {"c", "ac"}, - {"d", "abcd"}, - {"e", "ace"}, - {"f", "abcdef"}, - - {"be", "abce"}, - {"eb", "aceb"}, - {"de", "abcde"}, - {"ed", "acebd"}, - {"ef", "acebdf"}, - } { - var pkgs []*types.Package - for _, r := range test.roots { - pkgs = append(pkgs, packages[string(r)]) - } - var got string - for _, p := range typeutil.Dependencies(pkgs...) { - got += p.Path() - } - if got != test.want { - t.Errorf("Dependencies(%q) = %q, want %q", test.roots, got, test.want) - } - } -} diff --git a/go/types/typeutil/map14.go b/go/types/typeutil/map14.go deleted file mode 100644 index 16209e3bfc..0000000000 --- a/go/types/typeutil/map14.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// Package typeutil defines various utilities for types, such as Map, -// a mapping from types.Type to interface{} values. -package typeutil // import "golang.org/x/tools/go/types/typeutil" - -import ( - "bytes" - "fmt" - "reflect" - - "golang.org/x/tools/go/types" -) - -// Map is a hash-table-based mapping from types (types.Type) to -// arbitrary interface{} values. The concrete types that implement -// the Type interface are pointers. Since they are not canonicalized, -// == cannot be used to check for equivalence, and thus we cannot -// simply use a Go map. -// -// Just as with map[K]V, a nil *Map is a valid empty map. -// -// Not thread-safe. -// -type Map struct { - hasher Hasher // shared by many Maps - table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused - length int // number of map entries -} - -// entry is an entry (key/value association) in a hash bucket. -type entry struct { - key types.Type - value interface{} -} - -// SetHasher sets the hasher used by Map. -// -// All Hashers are functionally equivalent but contain internal state -// used to cache the results of hashing previously seen types. -// -// A single Hasher created by MakeHasher() may be shared among many -// Maps. This is recommended if the instances have many keys in -// common, as it will amortize the cost of hash computation. -// -// A Hasher may grow without bound as new types are seen. Even when a -// type is deleted from the map, the Hasher never shrinks, since other -// types in the map may reference the deleted type indirectly. -// -// Hashers are not thread-safe, and read-only operations such as -// Map.Lookup require updates to the hasher, so a full Mutex lock (not a -// read-lock) is require around all Map operations if a shared -// hasher is accessed from multiple threads. -// -// If SetHasher is not called, the Map will create a private hasher at -// the first call to Insert. -// -func (m *Map) SetHasher(hasher Hasher) { - m.hasher = hasher -} - -// Delete removes the entry with the given key, if any. -// It returns true if the entry was found. -// -func (m *Map) Delete(key types.Type) bool { - if m != nil && m.table != nil { - hash := m.hasher.Hash(key) - bucket := m.table[hash] - for i, e := range bucket { - if e.key != nil && types.Identical(key, e.key) { - // We can't compact the bucket as it - // would disturb iterators. - bucket[i] = entry{} - m.length-- - return true - } - } - } - return false -} - -// At returns the map entry for the given key. -// The result is nil if the entry is not present. -// -func (m *Map) At(key types.Type) interface{} { - if m != nil && m.table != nil { - for _, e := range m.table[m.hasher.Hash(key)] { - if e.key != nil && types.Identical(key, e.key) { - return e.value - } - } - } - return nil -} - -// Set sets the map entry for key to val, -// and returns the previous entry, if any. -func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { - if m.table != nil { - hash := m.hasher.Hash(key) - bucket := m.table[hash] - var hole *entry - for i, e := range bucket { - if e.key == nil { - hole = &bucket[i] - } else if types.Identical(key, e.key) { - prev = e.value - bucket[i].value = value - return - } - } - - if hole != nil { - *hole = entry{key, value} // overwrite deleted entry - } else { - m.table[hash] = append(bucket, entry{key, value}) - } - } else { - if m.hasher.memo == nil { - m.hasher = MakeHasher() - } - hash := m.hasher.Hash(key) - m.table = map[uint32][]entry{hash: {entry{key, value}}} - } - - m.length++ - return -} - -// Len returns the number of map entries. -func (m *Map) Len() int { - if m != nil { - return m.length - } - return 0 -} - -// Iterate calls function f on each entry in the map in unspecified order. -// -// If f should mutate the map, Iterate provides the same guarantees as -// Go maps: if f deletes a map entry that Iterate has not yet reached, -// f will not be invoked for it, but if f inserts a map entry that -// Iterate has not yet reached, whether or not f will be invoked for -// it is unspecified. -// -func (m *Map) Iterate(f func(key types.Type, value interface{})) { - if m != nil { - for _, bucket := range m.table { - for _, e := range bucket { - if e.key != nil { - f(e.key, e.value) - } - } - } - } -} - -// Keys returns a new slice containing the set of map keys. -// The order is unspecified. -func (m *Map) Keys() []types.Type { - keys := make([]types.Type, 0, m.Len()) - m.Iterate(func(key types.Type, _ interface{}) { - keys = append(keys, key) - }) - return keys -} - -func (m *Map) toString(values bool) string { - if m == nil { - return "{}" - } - var buf bytes.Buffer - fmt.Fprint(&buf, "{") - sep := "" - m.Iterate(func(key types.Type, value interface{}) { - fmt.Fprint(&buf, sep) - sep = ", " - fmt.Fprint(&buf, key) - if values { - fmt.Fprintf(&buf, ": %q", value) - } - }) - fmt.Fprint(&buf, "}") - return buf.String() -} - -// String returns a string representation of the map's entries. -// Values are printed using fmt.Sprintf("%v", v). -// Order is unspecified. -// -func (m *Map) String() string { - return m.toString(true) -} - -// KeysString returns a string representation of the map's key set. -// Order is unspecified. -// -func (m *Map) KeysString() string { - return m.toString(false) -} - -//////////////////////////////////////////////////////////////////////// -// Hasher - -// A Hasher maps each type to its hash value. -// For efficiency, a hasher uses memoization; thus its memory -// footprint grows monotonically over time. -// Hashers are not thread-safe. -// Hashers have reference semantics. -// Call MakeHasher to create a Hasher. -type Hasher struct { - memo map[types.Type]uint32 -} - -// MakeHasher returns a new Hasher instance. -func MakeHasher() Hasher { - return Hasher{make(map[types.Type]uint32)} -} - -// Hash computes a hash value for the given type t such that -// Identical(t, t') => Hash(t) == Hash(t'). -func (h Hasher) Hash(t types.Type) uint32 { - hash, ok := h.memo[t] - if !ok { - hash = h.hashFor(t) - h.memo[t] = hash - } - return hash -} - -// hashString computes the Fowler–Noll–Vo hash of s. -func hashString(s string) uint32 { - var h uint32 - for i := 0; i < len(s); i++ { - h ^= uint32(s[i]) - h *= 16777619 - } - return h -} - -// hashFor computes the hash of t. -func (h Hasher) hashFor(t types.Type) uint32 { - // See Identical for rationale. - switch t := t.(type) { - case *types.Basic: - return uint32(t.Kind()) - - case *types.Array: - return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) - - case *types.Slice: - return 9049 + 2*h.Hash(t.Elem()) - - case *types.Struct: - var hash uint32 = 9059 - for i, n := 0, t.NumFields(); i < n; i++ { - f := t.Field(i) - if f.Anonymous() { - hash += 8861 - } - hash += hashString(t.Tag(i)) - hash += hashString(f.Name()) // (ignore f.Pkg) - hash += h.Hash(f.Type()) - } - return hash - - case *types.Pointer: - return 9067 + 2*h.Hash(t.Elem()) - - case *types.Signature: - var hash uint32 = 9091 - if t.Variadic() { - hash *= 8863 - } - return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) - - case *types.Interface: - var hash uint32 = 9103 - for i, n := 0, t.NumMethods(); i < n; i++ { - // See go/types.identicalMethods for rationale. - // Method order is not significant. - // Ignore m.Pkg(). - m := t.Method(i) - hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type()) - } - return hash - - case *types.Map: - return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) - - case *types.Chan: - return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) - - case *types.Named: - // Not safe with a copying GC; objects may move. - return uint32(reflect.ValueOf(t.Obj()).Pointer()) - - case *types.Tuple: - return h.hashTuple(t) - } - panic(t) -} - -func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { - // See go/types.identicalTypes for rationale. - n := tuple.Len() - var hash uint32 = 9137 + 2*uint32(n) - for i := 0; i < n; i++ { - hash += 3 * h.Hash(tuple.At(i).Type()) - } - return hash -} diff --git a/go/types/typeutil/map14_test.go b/go/types/typeutil/map14_test.go deleted file mode 100644 index 9043d05f28..0000000000 --- a/go/types/typeutil/map14_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package typeutil_test - -// TODO(adonovan): -// - test use of explicit hasher across two maps. -// - test hashcodes are consistent with equals for a range of types -// (e.g. all types generated by type-checking some body of real code). - -import ( - "testing" - - "golang.org/x/tools/go/types" - "golang.org/x/tools/go/types/typeutil" -) - -var ( - tStr = types.Typ[types.String] // string - tPStr1 = types.NewPointer(tStr) // *string - tPStr2 = types.NewPointer(tStr) // *string, again - tInt = types.Typ[types.Int] // int - tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int - tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again -) - -func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { - if !types.Identical(x, y) { - t.Errorf("%s: not equal: %s, %s", comment, x, y) - } - if x == y { - t.Errorf("%s: identical: %v, %v", comment, x, y) - } -} - -func TestAxioms(t *testing.T) { - checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}") - checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}") -} - -func TestMap(t *testing.T) { - var tmap *typeutil.Map - - // All methods but Set are safe on on (*T)(nil). - tmap.Len() - tmap.At(tPStr1) - tmap.Delete(tPStr1) - tmap.KeysString() - tmap.String() - - tmap = new(typeutil.Map) - - // Length of empty map. - if l := tmap.Len(); l != 0 { - t.Errorf("Len() on empty Map: got %d, want 0", l) - } - // At of missing key. - if v := tmap.At(tPStr1); v != nil { - t.Errorf("At() on empty Map: got %v, want nil", v) - } - // Deletion of missing key. - if tmap.Delete(tPStr1) { - t.Errorf("Delete() on empty Map: got true, want false") - } - // Set of new key. - if prev := tmap.Set(tPStr1, "*string"); prev != nil { - t.Errorf("Set() on empty Map returned non-nil previous value %s", prev) - } - - // Now: {*string: "*string"} - - // Length of non-empty map. - if l := tmap.Len(); l != 1 { - t.Errorf("Len(): got %d, want 1", l) - } - // At via insertion key. - if v := tmap.At(tPStr1); v != "*string" { - t.Errorf("At(): got %q, want \"*string\"", v) - } - // At via equal key. - if v := tmap.At(tPStr2); v != "*string" { - t.Errorf("At(): got %q, want \"*string\"", v) - } - // Iteration over sole entry. - tmap.Iterate(func(key types.Type, value interface{}) { - if key != tPStr1 { - t.Errorf("Iterate: key: got %s, want %s", key, tPStr1) - } - if want := "*string"; value != want { - t.Errorf("Iterate: value: got %s, want %s", value, want) - } - }) - - // Setion with key equal to present one. - if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" { - t.Errorf("Set() previous value: got %s, want \"*string\"", prev) - } - - // Setion of another association. - if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil { - t.Errorf("Set() previous value: got %s, want nil", prev) - } - - // Now: {*string: "*string again", <-chan int: "<-chan int"} - - want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}" - want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}" - if s := tmap.String(); s != want1 && s != want2 { - t.Errorf("String(): got %s, want %s", s, want1) - } - - want1 = "{*string, <-chan int}" - want2 = "{<-chan int, *string}" - if s := tmap.KeysString(); s != want1 && s != want2 { - t.Errorf("KeysString(): got %s, want %s", s, want1) - } - - // Keys(). - I := types.Identical - switch k := tmap.Keys(); { - case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok - case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok - default: - t.Errorf("Keys(): got %v, want %s", k, want2) - } - - if l := tmap.Len(); l != 2 { - t.Errorf("Len(): got %d, want 1", l) - } - // At via original key. - if v := tmap.At(tPStr1); v != "*string again" { - t.Errorf("At(): got %q, want \"*string again\"", v) - } - hamming := 1 - tmap.Iterate(func(key types.Type, value interface{}) { - switch { - case I(key, tChanInt1): - hamming *= 2 // ok - case I(key, tPStr1): - hamming *= 3 // ok - } - }) - if hamming != 6 { - t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6) - } - - if v := tmap.At(tChanInt2); v != "<-chan int" { - t.Errorf("At(): got %q, want \"<-chan int\"", v) - } - // Deletion with key equal to present one. - if !tmap.Delete(tChanInt2) { - t.Errorf("Delete() of existing key: got false, want true") - } - - // Now: {*string: "*string again"} - - if l := tmap.Len(); l != 1 { - t.Errorf("Len(): got %d, want 1", l) - } - // Deletion again. - if !tmap.Delete(tPStr2) { - t.Errorf("Delete() of existing key: got false, want true") - } - - // Now: {} - - if l := tmap.Len(); l != 0 { - t.Errorf("Len(): got %d, want %d", l, 0) - } - if s := tmap.String(); s != "{}" { - t.Errorf("Len(): got %q, want %q", s, "") - } -} diff --git a/go/types/typeutil/methodsetcache14.go b/go/types/typeutil/methodsetcache14.go deleted file mode 100644 index 83b5e764c2..0000000000 --- a/go/types/typeutil/methodsetcache14.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -// This file implements a cache of method sets. - -package typeutil - -import ( - "sync" - - "golang.org/x/tools/go/types" -) - -// A MethodSetCache records the method set of each type T for which -// MethodSet(T) is called so that repeat queries are fast. -// The zero value is a ready-to-use cache instance. -type MethodSetCache struct { - mu sync.Mutex - named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N - others map[types.Type]*types.MethodSet // all other types -} - -// MethodSet returns the method set of type T. It is thread-safe. -// -// If cache is nil, this function is equivalent to types.NewMethodSet(T). -// Utility functions can thus expose an optional *MethodSetCache -// parameter to clients that care about performance. -// -func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { - if cache == nil { - return types.NewMethodSet(T) - } - cache.mu.Lock() - defer cache.mu.Unlock() - - switch T := T.(type) { - case *types.Named: - return cache.lookupNamed(T).value - - case *types.Pointer: - if N, ok := T.Elem().(*types.Named); ok { - return cache.lookupNamed(N).pointer - } - } - - // all other types - // (The map uses pointer equivalence, not type identity.) - mset := cache.others[T] - if mset == nil { - mset = types.NewMethodSet(T) - if cache.others == nil { - cache.others = make(map[types.Type]*types.MethodSet) - } - cache.others[T] = mset - } - return mset -} - -func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { - if cache.named == nil { - cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) - } - // Avoid recomputing mset(*T) for each distinct Pointer - // instance whose underlying type is a named type. - msets, ok := cache.named[named] - if !ok { - msets.value = types.NewMethodSet(named) - msets.pointer = types.NewMethodSet(types.NewPointer(named)) - cache.named[named] = msets - } - return msets -} diff --git a/go/types/typeutil/ui14.go b/go/types/typeutil/ui14.go deleted file mode 100644 index bb78e0b2a2..0000000000 --- a/go/types/typeutil/ui14.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 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. - -// +build !go1.5 - -package typeutil - -// This file defines utilities for user interfaces that display types. - -import "golang.org/x/tools/go/types" - -// IntuitiveMethodSet returns the intuitive method set of a type, T. -// -// The result contains MethodSet(T) and additionally, if T is a -// concrete type, methods belonging to *T if there is no identically -// named method on T itself. This corresponds to user intuition about -// method sets; this function is intended only for user interfaces. -// -// The order of the result is as for types.MethodSet(T). -// -func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { - var result []*types.Selection - mset := msets.MethodSet(T) - if _, ok := T.Underlying().(*types.Interface); ok { - for i, n := 0, mset.Len(); i < n; i++ { - result = append(result, mset.At(i)) - } - } else { - pmset := msets.MethodSet(types.NewPointer(T)) - for i, n := 0, pmset.Len(); i < n; i++ { - meth := pmset.At(i) - if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { - meth = m - } - result = append(result, meth) - } - } - return result -} diff --git a/go/types/typexpr.go b/go/types/typexpr.go deleted file mode 100644 index bd2d7ba272..0000000000 --- a/go/types/typexpr.go +++ /dev/null @@ -1,713 +0,0 @@ -// 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. - -// This file implements type-checking of identifiers and type expressions. - -package types - -import ( - "go/ast" - "go/token" - "sort" - "strconv" - - "golang.org/x/tools/go/exact" -) - -// ident type-checks identifier e and initializes x with the value or type of e. -// If an error occurred, x.mode is set to invalid. -// For the meaning of def and path, see check.typ, below. -// -func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeName) { - x.mode = invalid - x.expr = e - - scope, obj := check.scope.LookupParent(e.Name, check.pos) - if obj == nil { - if e.Name == "_" { - check.errorf(e.Pos(), "cannot use _ as value or type") - } else { - check.errorf(e.Pos(), "undeclared name: %s", e.Name) - } - return - } - check.recordUse(e, obj) - - check.objDecl(obj, def, path) - typ := obj.Type() - assert(typ != nil) - - // The object may be dot-imported: If so, remove its package from - // the map of unused dot imports for the respective file scope. - // (This code is only needed for dot-imports. Without them, - // we only have to mark variables, see *Var case below). - if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil { - delete(check.unusedDotImports[scope], pkg) - } - - switch obj := obj.(type) { - case *PkgName: - check.errorf(e.Pos(), "use of package %s not in selector", obj.name) - return - - case *Const: - check.addDeclDep(obj) - if typ == Typ[Invalid] { - return - } - if obj == universeIota { - if check.iota == nil { - check.errorf(e.Pos(), "cannot use iota outside constant declaration") - return - } - x.val = check.iota - } else { - x.val = obj.val - } - assert(x.val != nil) - x.mode = constant - - case *TypeName: - x.mode = typexpr - // check for cycle - // (it's ok to iterate forward because each named type appears at most once in path) - for i, prev := range path { - if prev == obj { - check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) - // print cycle - for _, obj := range path[i:] { - check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented - } - check.errorf(obj.Pos(), "\t%s", obj.Name()) - // maintain x.mode == typexpr despite error - typ = Typ[Invalid] - break - } - } - - case *Var: - if obj.pkg == check.pkg { - obj.used = true - } - check.addDeclDep(obj) - if typ == Typ[Invalid] { - return - } - x.mode = variable - - case *Func: - check.addDeclDep(obj) - x.mode = value - - case *Builtin: - x.id = obj.id - x.mode = builtin - - case *Nil: - x.mode = value - - default: - unreachable() - } - - x.typ = typ -} - -// typExpr type-checks the type expression e and returns its type, or Typ[Invalid]. -// If def != nil, e is the type specification for the named type def, declared -// in a type declaration, and def.underlying will be set to the type of e before -// any components of e are type-checked. Path contains the path of named types -// referring to this type. -// -func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) { - if trace { - check.trace(e.Pos(), "%s", e) - check.indent++ - defer func() { - check.indent-- - check.trace(e.Pos(), "=> %s", T) - }() - } - - T = check.typExprInternal(e, def, path) - assert(isTyped(T)) - check.recordTypeAndValue(e, typexpr, T, nil) - - return -} - -func (check *Checker) typ(e ast.Expr) Type { - return check.typExpr(e, nil, nil) -} - -// funcType type-checks a function or method type. -func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) { - scope := NewScope(check.scope, token.NoPos, token.NoPos, "function") - check.recordScope(ftyp, scope) - - recvList, _ := check.collectParams(scope, recvPar, false) - params, variadic := check.collectParams(scope, ftyp.Params, true) - results, _ := check.collectParams(scope, ftyp.Results, false) - - if recvPar != nil { - // recv parameter list present (may be empty) - // spec: "The receiver is specified via an extra parameter section preceeding the - // method name. That parameter section must declare a single parameter, the receiver." - var recv *Var - switch len(recvList) { - case 0: - check.error(recvPar.Pos(), "method is missing receiver") - recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below - default: - // more than one receiver - check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver") - fallthrough // continue with first receiver - case 1: - recv = recvList[0] - } - // spec: "The receiver type must be of the form T or *T where T is a type name." - // (ignore invalid types - error was reported before) - if t, _ := deref(recv.typ); t != Typ[Invalid] { - var err string - if T, _ := t.(*Named); T != nil { - // spec: "The type denoted by T is called the receiver base type; it must not - // be a pointer or interface type and it must be declared in the same package - // as the method." - if T.obj.pkg != check.pkg { - err = "type not defined in this package" - } else { - // TODO(gri) This is not correct if the underlying type is unknown yet. - switch u := T.underlying.(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" - } - case *Pointer, *Interface: - err = "pointer or interface type" - } - } - } else { - err = "basic or unnamed type" - } - if err != "" { - check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err) - // ok to continue - } - } - sig.recv = recv - } - - sig.scope = scope - sig.params = NewTuple(params...) - sig.results = NewTuple(results...) - sig.variadic = variadic -} - -// typExprInternal drives type checking of types. -// Must only be called by typExpr. -// -func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) Type { - switch e := e.(type) { - case *ast.BadExpr: - // ignore - error reported before - - case *ast.Ident: - var x operand - check.ident(&x, e, def, path) - - switch x.mode { - case typexpr: - typ := x.typ - def.setUnderlying(typ) - return typ - case invalid: - // ignore - error reported before - case novalue: - check.errorf(x.pos(), "%s used as type", &x) - default: - check.errorf(x.pos(), "%s is not a type", &x) - } - - case *ast.SelectorExpr: - var x operand - check.selector(&x, e) - - switch x.mode { - case typexpr: - typ := x.typ - def.setUnderlying(typ) - return typ - case invalid: - // ignore - error reported before - case novalue: - check.errorf(x.pos(), "%s used as type", &x) - default: - check.errorf(x.pos(), "%s is not a type", &x) - } - - case *ast.ParenExpr: - return check.typExpr(e.X, def, path) - - case *ast.ArrayType: - if e.Len != nil { - typ := new(Array) - def.setUnderlying(typ) - typ.len = check.arrayLength(e.Len) - typ.elem = check.typExpr(e.Elt, nil, path) - return typ - - } else { - typ := new(Slice) - def.setUnderlying(typ) - typ.elem = check.typ(e.Elt) - return typ - } - - case *ast.StructType: - typ := new(Struct) - def.setUnderlying(typ) - check.structType(typ, e, path) - return typ - - case *ast.StarExpr: - typ := new(Pointer) - def.setUnderlying(typ) - typ.base = check.typ(e.X) - return typ - - case *ast.FuncType: - typ := new(Signature) - def.setUnderlying(typ) - check.funcType(typ, nil, e) - return typ - - case *ast.InterfaceType: - typ := new(Interface) - def.setUnderlying(typ) - check.interfaceType(typ, e, def, path) - return typ - - case *ast.MapType: - typ := new(Map) - def.setUnderlying(typ) - - typ.key = check.typ(e.Key) - typ.elem = check.typ(e.Value) - - // spec: "The comparison operators == and != must be fully defined - // for operands of the key type; thus the key type must not be a - // function, map, or slice." - // - // Delay this check because it requires fully setup types; - // it is safe to continue in any case (was issue 6667). - check.delay(func() { - if !Comparable(typ.key) { - check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) - } - }) - - return typ - - case *ast.ChanType: - typ := new(Chan) - def.setUnderlying(typ) - - dir := SendRecv - switch e.Dir { - case ast.SEND | ast.RECV: - // nothing to do - case ast.SEND: - dir = SendOnly - case ast.RECV: - dir = RecvOnly - default: - check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir) - // ok to continue - } - - typ.dir = dir - typ.elem = check.typ(e.Value) - return typ - - default: - check.errorf(e.Pos(), "%s is not a type", e) - } - - typ := Typ[Invalid] - def.setUnderlying(typ) - return typ -} - -// typeOrNil type-checks the type expression (or nil value) e -// and returns the typ of e, or nil. -// If e is neither a type nor nil, typOrNil returns Typ[Invalid]. -// -func (check *Checker) typOrNil(e ast.Expr) Type { - var x operand - check.rawExpr(&x, e, nil) - switch x.mode { - case invalid: - // ignore - error reported before - case novalue: - check.errorf(x.pos(), "%s used as type", &x) - case typexpr: - return x.typ - case value: - if x.isNil() { - return nil - } - fallthrough - default: - check.errorf(x.pos(), "%s is not a type", &x) - } - return Typ[Invalid] -} - -func (check *Checker) arrayLength(e ast.Expr) int64 { - var x operand - check.expr(&x, e) - if x.mode != constant { - if x.mode != invalid { - check.errorf(x.pos(), "array length %s must be constant", &x) - } - return 0 - } - if !x.isInteger() { - check.errorf(x.pos(), "array length %s must be integer", &x) - return 0 - } - n, ok := exact.Int64Val(x.val) - if !ok || n < 0 { - check.errorf(x.pos(), "invalid array length %s", &x) - return 0 - } - return n -} - -func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { - if list == nil { - return - } - - var named, anonymous bool - for i, field := range list.List { - ftype := field.Type - if t, _ := ftype.(*ast.Ellipsis); t != nil { - ftype = t.Elt - if variadicOk && i == len(list.List)-1 { - variadic = true - } else { - check.invalidAST(field.Pos(), "... not permitted") - // ignore ... and continue - } - } - typ := check.typ(ftype) - // The parser ensures that f.Tag is nil and we don't - // care if a constructed AST contains a non-nil tag. - if len(field.Names) > 0 { - // named parameter - for _, name := range field.Names { - if name.Name == "" { - check.invalidAST(name.Pos(), "anonymous parameter") - // ok to continue - } - par := NewParam(name.Pos(), check.pkg, name.Name, typ) - check.declare(scope, name, par, scope.pos) - params = append(params, par) - } - named = true - } else { - // anonymous parameter - par := NewParam(ftype.Pos(), check.pkg, "", typ) - check.recordImplicit(field, par) - params = append(params, par) - anonymous = true - } - } - - if named && anonymous { - check.invalidAST(list.Pos(), "list contains both named and anonymous parameters") - // ok to continue - } - - // For a variadic function, change the last parameter's type from T to []T. - if variadic && len(params) > 0 { - last := params[len(params)-1] - last.typ = &Slice{elem: last.typ} - } - - return -} - -func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool { - if alt := oset.insert(obj); alt != nil { - check.errorf(pos, "%s redeclared", obj.Name()) - check.reportAltDecl(alt) - return false - } - return true -} - -func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) { - // empty interface: common case - if ityp.Methods == nil { - return - } - - // The parser ensures that field tags are nil and we don't - // care if a constructed AST contains non-nil tags. - - // use named receiver type if available (for better error messages) - var recvTyp Type = iface - if def != nil { - recvTyp = def - } - - // Phase 1: Collect explicitly declared methods, the corresponding - // signature (AST) expressions, and the list of embedded - // type (AST) expressions. Do not resolve signatures or - // embedded types yet to avoid cycles referring to this - // interface. - - var ( - mset objset - signatures []ast.Expr // list of corresponding method signatures - embedded []ast.Expr // list of embedded types - ) - for _, f := range ityp.Methods.List { - if len(f.Names) > 0 { - // The parser ensures that there's only one method - // and we don't care if a constructed AST has more. - name := f.Names[0] - pos := name.Pos() - // spec: "As with all method sets, in an interface type, - // each method must have a unique non-blank name." - if name.Name == "_" { - check.errorf(pos, "invalid method name _") - continue - } - // Don't type-check signature yet - use an - // empty signature now and update it later. - // Since we know the receiver, set it up now - // (required to avoid crash in ptrRecv; see - // e.g. test case for issue 6638). - // TODO(gri) Consider marking methods signatures - // as incomplete, for better error messages. See - // also the T4 and T5 tests in testdata/cycles2.src. - sig := new(Signature) - sig.recv = NewVar(pos, check.pkg, "", recvTyp) - m := NewFunc(pos, check.pkg, name.Name, sig) - if check.declareInSet(&mset, pos, m) { - iface.methods = append(iface.methods, m) - iface.allMethods = append(iface.allMethods, m) - signatures = append(signatures, f.Type) - check.recordDef(name, m) - } - } else { - // embedded type - embedded = append(embedded, f.Type) - } - } - - // Phase 2: Resolve embedded interfaces. Because an interface must not - // embed itself (directly or indirectly), each embedded interface - // can be fully resolved without depending on any method of this - // interface (if there is a cycle or another error, the embedded - // type resolves to an invalid type and is ignored). - // In particular, the list of methods for each embedded interface - // must be complete (it cannot depend on this interface), and so - // those methods can be added to the list of all methods of this - // interface. - - for _, e := range embedded { - pos := e.Pos() - typ := check.typExpr(e, nil, path) - // Determine underlying embedded (possibly incomplete) type - // by following its forward chain. - named, _ := typ.(*Named) - under := underlying(named) - embed, _ := under.(*Interface) - if embed == nil { - if typ != Typ[Invalid] { - check.errorf(pos, "%s is not an interface", typ) - } - continue - } - iface.embeddeds = append(iface.embeddeds, named) - // collect embedded methods - for _, m := range embed.allMethods { - if check.declareInSet(&mset, pos, m) { - iface.allMethods = append(iface.allMethods, m) - } - } - } - - // Phase 3: At this point all methods have been collected for this interface. - // It is now safe to type-check the signatures of all explicitly - // declared methods, even if they refer to this interface via a cycle - // and embed the methods of this interface in a parameter of interface - // type. - - for i, m := range iface.methods { - expr := signatures[i] - typ := check.typ(expr) - sig, _ := typ.(*Signature) - if sig == nil { - if typ != Typ[Invalid] { - check.invalidAST(expr.Pos(), "%s is not a method signature", typ) - } - continue // keep method with empty method signature - } - // update signature, but keep recv that was set up before - old := m.typ.(*Signature) - sig.recv = old.recv - *old = *sig // update signature (don't replace it!) - } - - // TODO(gri) The list of explicit methods is only sorted for now to - // produce the same Interface as NewInterface. We may be able to - // claim source order in the future. Revisit. - sort.Sort(byUniqueMethodName(iface.methods)) - - // TODO(gri) The list of embedded types is only sorted for now to - // produce the same Interface as NewInterface. We may be able to - // claim source order in the future. Revisit. - sort.Sort(byUniqueTypeName(iface.embeddeds)) - - sort.Sort(byUniqueMethodName(iface.allMethods)) -} - -// byUniqueTypeName named type lists can be sorted by their unique type names. -type byUniqueTypeName []*Named - -func (a byUniqueTypeName) Len() int { return len(a) } -func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } -func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -// byUniqueMethodName method lists can be sorted by their unique method names. -type byUniqueMethodName []*Func - -func (a byUniqueMethodName) Len() int { return len(a) } -func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() } -func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func (check *Checker) tag(t *ast.BasicLit) string { - if t != nil { - if t.Kind == token.STRING { - if val, err := strconv.Unquote(t.Value); err == nil { - return val - } - } - check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value) - } - return "" -} - -func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeName) { - list := e.Fields - if list == nil { - return - } - - // struct fields and tags - var fields []*Var - var tags []string - - // for double-declaration checks - var fset objset - - // current field typ and tag - var typ Type - var tag string - // anonymous != nil indicates an anonymous field. - add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) { - if tag != "" && tags == nil { - tags = make([]string, len(fields)) - } - if tags != nil { - tags = append(tags, tag) - } - - name := ident.Name - fld := NewField(pos, check.pkg, name, typ, anonymous != nil) - // spec: "Within a struct, non-blank field names must be unique." - if name == "_" || check.declareInSet(&fset, pos, fld) { - fields = append(fields, fld) - check.recordDef(ident, fld) - } - if anonymous != nil { - check.recordUse(ident, anonymous) - } - } - - for _, f := range list.List { - typ = check.typExpr(f.Type, nil, path) - tag = check.tag(f.Tag) - if len(f.Names) > 0 { - // named fields - for _, name := range f.Names { - add(f, name, nil, name.Pos()) - } - } else { - // anonymous field - name := anonymousFieldIdent(f.Type) - pos := f.Type.Pos() - t, isPtr := deref(typ) - switch t := t.(type) { - case *Basic: - if t == Typ[Invalid] { - // error was reported before - continue - } - // unsafe.Pointer is treated like a regular pointer - if t.kind == UnsafePointer { - check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") - continue - } - add(f, name, Universe.Lookup(t.name).(*TypeName), pos) - - case *Named: - // spec: "An embedded type must be specified as a type name - // T or as a pointer to a non-interface type name *T, and T - // itself may not be a pointer type." - switch u := t.underlying.(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") - continue - } - case *Pointer: - check.errorf(pos, "anonymous field type cannot be a pointer") - continue - case *Interface: - if isPtr { - check.errorf(pos, "anonymous field type cannot be a pointer to an interface") - continue - } - } - add(f, name, t.obj, pos) - - default: - check.invalidAST(pos, "anonymous field type %s must be named", typ) - } - } - } - - styp.fields = fields - styp.tags = tags -} - -func anonymousFieldIdent(e ast.Expr) *ast.Ident { - switch e := e.(type) { - case *ast.Ident: - return e - case *ast.StarExpr: - return anonymousFieldIdent(e.X) - case *ast.SelectorExpr: - return e.Sel - } - return nil // invalid anonymous field -} diff --git a/go/types/universe.go b/go/types/universe.go deleted file mode 100644 index 12a34ef853..0000000000 --- a/go/types/universe.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2011 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. - -// This file sets up the universe scope and the unsafe package. - -package types - -import ( - "go/token" - "strings" - - "golang.org/x/tools/go/exact" -) - -var ( - Universe *Scope - Unsafe *Package - universeIota *Const - universeByte *Basic // uint8 alias, but has name "byte" - universeRune *Basic // int32 alias, but has name "rune" -) - -var Typ = []*Basic{ - Invalid: {Invalid, 0, "invalid type"}, - - Bool: {Bool, IsBoolean, "bool"}, - Int: {Int, IsInteger, "int"}, - Int8: {Int8, IsInteger, "int8"}, - Int16: {Int16, IsInteger, "int16"}, - Int32: {Int32, IsInteger, "int32"}, - Int64: {Int64, IsInteger, "int64"}, - Uint: {Uint, IsInteger | IsUnsigned, "uint"}, - Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"}, - Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"}, - Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"}, - Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"}, - Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"}, - Float32: {Float32, IsFloat, "float32"}, - Float64: {Float64, IsFloat, "float64"}, - Complex64: {Complex64, IsComplex, "complex64"}, - Complex128: {Complex128, IsComplex, "complex128"}, - String: {String, IsString, "string"}, - UnsafePointer: {UnsafePointer, 0, "Pointer"}, - - UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"}, - UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"}, - UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"}, - UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"}, - UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"}, - UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"}, - UntypedNil: {UntypedNil, IsUntyped, "untyped nil"}, -} - -var aliases = [...]*Basic{ - {Byte, IsInteger | IsUnsigned, "byte"}, - {Rune, IsInteger, "rune"}, -} - -func defPredeclaredTypes() { - for _, t := range Typ { - def(NewTypeName(token.NoPos, nil, t.name, t)) - } - for _, t := range aliases { - def(NewTypeName(token.NoPos, nil, t.name, t)) - } - - // Error has a nil package in its qualified name since it is in no package - res := NewVar(token.NoPos, nil, "", Typ[String]) - sig := &Signature{results: NewTuple(res)} - err := NewFunc(token.NoPos, nil, "Error", sig) - typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()} - sig.recv = NewVar(token.NoPos, nil, "", typ) - def(NewTypeName(token.NoPos, nil, "error", typ)) -} - -var predeclaredConsts = [...]struct { - name string - kind BasicKind - val exact.Value -}{ - {"true", UntypedBool, exact.MakeBool(true)}, - {"false", UntypedBool, exact.MakeBool(false)}, - {"iota", UntypedInt, exact.MakeInt64(0)}, -} - -func defPredeclaredConsts() { - for _, c := range predeclaredConsts { - def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val)) - } -} - -func defPredeclaredNil() { - def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}}) -} - -// A builtinId is the id of a builtin function. -type builtinId int - -const ( - // universe scope - _Append builtinId = iota - _Cap - _Close - _Complex - _Copy - _Delete - _Imag - _Len - _Make - _New - _Panic - _Print - _Println - _Real - _Recover - - // package unsafe - _Alignof - _Offsetof - _Sizeof - - // testing support - _Assert - _Trace -) - -var predeclaredFuncs = [...]struct { - name string - nargs int - variadic bool - kind exprKind -}{ - _Append: {"append", 1, true, expression}, - _Cap: {"cap", 1, false, expression}, - _Close: {"close", 1, false, statement}, - _Complex: {"complex", 2, false, expression}, - _Copy: {"copy", 2, false, statement}, - _Delete: {"delete", 2, false, statement}, - _Imag: {"imag", 1, false, expression}, - _Len: {"len", 1, false, expression}, - _Make: {"make", 1, true, expression}, - _New: {"new", 1, false, expression}, - _Panic: {"panic", 1, false, statement}, - _Print: {"print", 0, true, statement}, - _Println: {"println", 0, true, statement}, - _Real: {"real", 1, false, expression}, - _Recover: {"recover", 0, false, statement}, - - _Alignof: {"Alignof", 1, false, expression}, - _Offsetof: {"Offsetof", 1, false, expression}, - _Sizeof: {"Sizeof", 1, false, expression}, - - _Assert: {"assert", 1, false, statement}, - _Trace: {"trace", 0, true, statement}, -} - -func defPredeclaredFuncs() { - for i := range predeclaredFuncs { - id := builtinId(i) - if id == _Assert || id == _Trace { - continue // only define these in testing environment - } - def(newBuiltin(id)) - } -} - -// DefPredeclaredTestFuncs defines the assert and trace built-ins. -// These built-ins are intended for debugging and testing of this -// package only. -func DefPredeclaredTestFuncs() { - if Universe.Lookup("assert") != nil { - return // already defined - } - def(newBuiltin(_Assert)) - def(newBuiltin(_Trace)) -} - -func init() { - Universe = NewScope(nil, token.NoPos, token.NoPos, "universe") - Unsafe = NewPackage("unsafe", "unsafe") - Unsafe.complete = true - - defPredeclaredTypes() - defPredeclaredConsts() - defPredeclaredNil() - defPredeclaredFuncs() - - universeIota = Universe.Lookup("iota").(*Const) - universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic) - universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic) -} - -// Objects with names containing blanks are internal and not entered into -// a scope. Objects with exported names are inserted in the unsafe package -// scope; other objects are inserted in the universe scope. -// -func def(obj Object) { - name := obj.Name() - if strings.Index(name, " ") >= 0 { - return // nothing to do - } - // fix Obj link for named types - if typ, ok := obj.Type().(*Named); ok { - typ.obj = obj.(*TypeName) - } - // exported identifiers go into package unsafe - scope := Universe - if obj.Exported() { - scope = Unsafe.scope - // set Pkg field - switch obj := obj.(type) { - case *TypeName: - obj.pkg = Unsafe - case *Builtin: - obj.pkg = Unsafe - default: - unreachable() - } - } - if scope.Insert(obj) != nil { - panic("internal error: double declaration") - } -}