From: Rob Pike Date: Tue, 12 Mar 2013 20:55:58 +0000 (-0700) Subject: go/types: delete from main repo; part of move to go.exp X-Git-Tag: go1.1rc2~547 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4ce79096b6a88051bd1253672593f07ce0234155;p=gostls13.git go/types: delete from main repo; part of move to go.exp See also https://golang.org/cl/7656044 R=golang-dev, gri, rsc CC=golang-dev https://golang.org/cl/7625048 --- diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go index 32584e175e..09af85be04 100644 --- a/src/cmd/vet/types.go +++ b/src/cmd/vet/types.go @@ -5,13 +5,16 @@ // +build gotypes // This file contains the pieces of the tool that require the go/types package. +// To compile this file, you must first run +// $ go get code.google.com/p/go.exp/go/types package main import ( "go/ast" "go/token" - "go/types" + + "code.google.com/p/go.exp/go/types" ) // Type is equivalent to go/types.Type. Repeating it here allows us to avoid diff --git a/src/make.bash b/src/make.bash index a06964c3a2..c15711b31a 100755 --- a/src/make.bash +++ b/src/make.bash @@ -134,15 +134,14 @@ echo if [ "$GOHOSTARCH" != "$GOARCH" -o "$GOHOSTOS" != "$GOOS" ]; then echo "# Building packages and commands for host, $GOHOSTOS/$GOHOSTARCH." - # TODO: Drop the -tags gotypes before releasing Go 1.1. It is to allow type checking in go vet. GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \ - "$GOTOOLDIR"/go_bootstrap install -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -tags gotypes -v std + "$GOTOOLDIR"/go_bootstrap install -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std echo fi echo "# Building packages and commands for $GOOS/$GOARCH." # TODO: Drop the -tags gotypes before releasing Go 1.1. It is to allow type checking in go vet. -"$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -tags gotypes -v std +"$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std echo rm -f "$GOTOOLDIR"/go_bootstrap diff --git a/src/make.bat b/src/make.bat index 5caba73b17..be1c8f52dc 100644 --- a/src/make.bat +++ b/src/make.bat @@ -90,16 +90,14 @@ echo # Building tools for local system. %GOHOSTOS%/%GOHOSTARCH% setlocal set GOOS=%GOHOSTOS% set GOARCH=%GOHOSTARCH% -:: TODO: Drop the -tags gotypes before releasing Go 1.1. It is to allow type checking in go vet. -"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -tags gotypes -v std +"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -v std endlocal if errorlevel 1 goto fail echo. :mainbuild echo # Building packages and commands. -:: TODO: Drop the -tags gotypes before releasing Go 1.1. It is to allow type checking in go vet. -"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -a -tags gotypes -v std +"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -a -v std if errorlevel 1 goto fail del "%GOTOOLDIR%\go_bootstrap.exe" echo. diff --git a/src/pkg/go/types/api.go b/src/pkg/go/types/api.go deleted file mode 100644 index 13b453faac..0000000000 --- a/src/pkg/go/types/api.go +++ /dev/null @@ -1,105 +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 structures for representing -// Go types and implements typechecking of package files. -// -// WARNING: THE TYPES API IS SUBJECT TO CHANGE. -// -package types - -import ( - "go/ast" - "go/token" -) - -// A Context specifies the supporting context for type checking. -// An empty Context is a ready-to-use default context. -type Context struct { - // If Error != nil, it is called with each error found - // during type checking. The error strings of errors with - // detailed position information are formatted as follows: - // filename:line:column: message - Error func(err error) - - // If Ident != nil, it is called for each identifier id - // denoting an Object in the files provided to Check, and - // obj is the denoted object. - // Ident is not called for fields and methods in struct or - // interface types or composite literals, or for blank (_) - // or dot (.) identifiers in dot-imports. - // TODO(gri) Consider making Fields and Methods ordinary - // Objects - than we could lift this restriction. - Ident func(id *ast.Ident, obj Object) - - // If Expr != nil, it is called exactly once for each expression x - // that is type-checked: typ is the expression type, and val is the - // value if x is constant, val is nil otherwise. - // - // If x is a literal value (constant, composite literal), typ is always - // the dynamic type of x (never an interface type). Otherwise, typ is x's - // static type (possibly an interface type). - // - // Constants are represented as follows: - // - // bool -> bool - // numeric -> int64, *big.Int, *big.Rat, Complex - // string -> string - // nil -> NilType - // - // Constant values are normalized, that is, they are represented - // using the "smallest" possible type that can represent the value. - // For instance, 1.0 is represented as an int64 because it can be - // represented accurately as an int64. - Expr func(x ast.Expr, typ Type, val interface{}) - - // If Import != nil, it is called for each imported package. - // Otherwise, GcImporter is called. - Import Importer - - // If Alignof != nil, it is called to determine the alignment - // of the given type. Otherwise DefaultAlignmentof is called. - // Alignof must implement the alignment guarantees required by - // the spec. - Alignof func(Type) int64 - - // If Offsetsof != nil, it is called to determine the offsets - // of the given struct fields, in bytes. Otherwise DefaultOffsetsof - // is called. Offsetsof must implement the offset guarantees - // required by the spec. - Offsetsof func(fields []*Field) []int64 - - // If Sizeof != nil, it is called to determine the size of the - // given type. Otherwise, DefaultSizeof is called. Sizeof must - // implement the size guarantees required by the spec. - Sizeof func(Type) int64 -} - -// An Importer resolves import paths to Package objects. -// The imports map records the packages already imported, -// indexed by package id (canonical import path). -// An Importer must determine the canonical import path and -// check the map to see if it is already present in the imports map. -// If so, the Importer can return the map entry. Otherwise, the -// Importer should load the package data for the given path into -// a new *Package, record pkg in the imports map, and then -// return pkg. -type Importer func(imports map[string]*Package, path string) (pkg *Package, err error) - -// Check resolves and typechecks a set of package files within the given -// context. It returns the package and the first error encountered, if -// any. If the context's Error handler is nil, Check terminates as soon -// as the first error is encountered; otherwise it continues until the -// entire package is checked. If there are errors, the package may be -// only partially type-checked, and the resulting package may be incomplete -// (missing objects, imports, etc.). -func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) { - return check(ctxt, fset, files) -} - -// Check is shorthand for ctxt.Check where ctxt is a default (empty) context. -func Check(fset *token.FileSet, files []*ast.File) (*Package, error) { - var ctxt Context - return ctxt.Check(fset, files) -} diff --git a/src/pkg/go/types/builtins.go b/src/pkg/go/types/builtins.go deleted file mode 100644 index 220be08b5d..0000000000 --- a/src/pkg/go/types/builtins.go +++ /dev/null @@ -1,451 +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" -) - -// TODO(gri): Several built-ins are missing assignment checks. As a result, -// non-constant shift arguments may not be properly type-checked. - -// builtin typechecks a built-in call. The built-in type is bin, and iota is the current -// value of iota or -1 if iota doesn't have a value in the current context. The result -// of the call is returned via x. If the call has type errors, the returned x is marked -// as invalid (x.mode == invalid). -// -func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { - args := call.Args - id := bin.id - - // declare before goto's - var arg0 ast.Expr // first argument, if present - - // check argument count - n := len(args) - msg := "" - if n < bin.nargs { - msg = "not enough" - } else if !bin.isVariadic && n > bin.nargs { - msg = "too many" - } - if msg != "" { - check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n) - goto Error - } - - // common case: evaluate first argument if present; - // if it is an expression, x has the expression value - if n > 0 { - arg0 = args[0] - switch id { - case _Make, _New, _Print, _Println, _Offsetof, _Trace: - // respective cases below do the work - default: - // argument must be an expression - check.expr(x, arg0, nil, iota) - if x.mode == invalid { - goto Error - } - } - } - - switch id { - case _Append: - if _, ok := underlying(x.typ).(*Slice); !ok { - check.invalidArg(x.pos(), "%s is not a typed slice", x) - goto Error - } - resultTyp := x.typ - for _, arg := range args[1:] { - check.expr(x, arg, nil, iota) - if x.mode == invalid { - goto Error - } - // TODO(gri) check assignability - } - x.mode = value - x.typ = resultTyp - - case _Cap, _Len: - mode := invalid - var val interface{} - switch typ := implicitArrayDeref(underlying(x.typ)).(type) { - case *Basic: - if isString(typ) && id == _Len { - if x.mode == constant { - mode = constant - val = int64(len(x.val.(string))) - } 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.containsCallsOrReceives(arg0) { - mode = constant - val = typ.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) - goto Error - } - x.mode = mode - x.typ = Typ[Int] - x.val = val - - case _Close: - ch, ok := underlying(x.typ).(*Chan) - if !ok { - check.invalidArg(x.pos(), "%s is not a channel", x) - goto Error - } - if ch.Dir&ast.SEND == 0 { - check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) - goto Error - } - x.mode = novalue - - case _Complex: - if !check.complexArg(x) { - goto Error - } - - var y operand - check.expr(&y, args[1], nil, iota) - if y.mode == invalid { - goto Error - } - if !check.complexArg(&y) { - goto Error - } - - check.convertUntyped(x, y.typ) - if x.mode == invalid { - goto Error - } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - goto Error - } - - if !IsIdentical(x.typ, y.typ) { - check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) - goto Error - } - - typ := underlying(x.typ).(*Basic) - if x.mode == constant && y.mode == constant { - x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ) - } else { - x.mode = value - } - - switch typ.Kind { - case Float32: - x.typ = Typ[Complex64] - case Float64: - x.typ = Typ[Complex128] - case UntypedInt, UntypedRune, UntypedFloat: - x.typ = Typ[UntypedComplex] - default: - check.invalidArg(x.pos(), "float32 or float64 arguments expected") - goto Error - } - - // arguments have final type - check.updateExprType(args[0], typ, true) - check.updateExprType(args[1], typ, true) - - case _Copy: - var y operand - check.expr(&y, args[1], nil, iota) - if y.mode == invalid { - goto Error - } - - var dst, src Type - if t, ok := underlying(x.typ).(*Slice); ok { - dst = t.Elt - } - switch t := underlying(y.typ).(type) { - case *Basic: - if isString(y.typ) { - src = Typ[Byte] - } - case *Slice: - src = t.Elt - } - - if dst == nil || src == nil { - check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) - goto Error - } - - if !IsIdentical(dst, src) { - check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) - goto Error - } - - x.mode = value - x.typ = Typ[Int] - - case _Delete: - m, ok := underlying(x.typ).(*Map) - if !ok { - check.invalidArg(x.pos(), "%s is not a map", x) - goto Error - } - check.expr(x, args[1], nil, iota) - if x.mode == invalid { - goto Error - } - if !x.isAssignable(check.ctxt, m.Key) { - check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) - goto Error - } - x.mode = novalue - - case _Imag, _Real: - if !isComplex(x.typ) { - check.invalidArg(x.pos(), "%s must be a complex number", x) - goto Error - } - if x.mode == constant { - // nothing to do for x.val == 0 - if !isZeroConst(x.val) { - c := x.val.(Complex) - if id == _Real { - x.val = c.Re - } else { - x.val = c.Im - } - } - } else { - x.mode = value - } - k := Invalid - switch underlying(x.typ).(*Basic).Kind { - case Complex64: - k = Float32 - case Complex128: - k = Float64 - case UntypedComplex: - k = UntypedFloat - default: - unreachable() - } - x.typ = Typ[k] - - case _Make: - resultTyp := check.typ(arg0, false) - if resultTyp == Typ[Invalid] { - goto Error - } - var min int // minimum number of arguments - switch underlying(resultTyp).(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) - goto Error - } - if n := len(args); n < min || min+1 < n { - check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) - goto Error - } - var sizes []int64 // constant integer arguments, if any - for _, arg := range args[1:] { - if s, ok := check.index(arg, -1, iota); ok && s >= 0 { - sizes = append(sizes, s) - } - } - if len(sizes) == 2 && sizes[0] > sizes[1] { - check.invalidArg(args[1].Pos(), "length and capacity swapped") - // safe to continue - } - x.mode = variable - x.typ = resultTyp - - case _New: - resultTyp := check.typ(arg0, false) - if resultTyp == Typ[Invalid] { - goto Error - } - x.mode = variable - x.typ = &Pointer{Base: resultTyp} - - case _Panic: - x.mode = novalue - - case _Print, _Println: - for _, arg := range args { - check.expr(x, arg, nil, -1) - if x.mode == invalid { - goto Error - } - } - x.mode = novalue - - case _Recover: - x.mode = value - x.typ = new(Interface) - - case _Alignof: - x.mode = constant - x.val = check.ctxt.alignof(x.typ) - x.typ = Typ[Uintptr] - - case _Offsetof: - arg, ok := unparen(arg0).(*ast.SelectorExpr) - if !ok { - check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) - goto Error - } - check.expr(x, arg.X, nil, -1) - if x.mode == invalid { - goto Error - } - sel := arg.Sel.Name - res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name}) - if res.index == nil { - check.invalidArg(x.pos(), "%s has no single field %s", x, sel) - goto Error - } - offs := check.ctxt.offsetof(deref(x.typ), res.index) - if offs < 0 { - check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) - goto Error - } - x.mode = constant - x.val = offs - x.typ = Typ[Uintptr] - - case _Sizeof: - x.mode = constant - x.val = check.ctxt.sizeof(x.typ) - x.typ = Typ[Uintptr] - - 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) - goto Error - } - pred, ok := x.val.(bool) - if !ok { - check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) - goto Error - } - if !pred { - check.errorf(call.Pos(), "%s failed", call) - // compile-time assertion failure - safe to continue - } - - 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. - if len(args) == 0 { - check.dump("%s: trace() without arguments", call.Pos()) - x.mode = novalue - x.expr = call - return - } - var t operand - x1 := x - for _, arg := range args { - check.rawExpr(x1, arg, nil, iota, true) // 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 - } - - default: - check.invalidAST(call.Pos(), "unknown builtin id %d", id) - goto Error - } - - x.expr = call - return - -Error: - x.mode = invalid - x.expr = call -} - -// 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 := underlying(p.Base).(*Array); ok { - return a - } - } - return typ -} - -// containsCallsOrReceives reports if x contains function calls or channel receives. -// Expects that x was type-checked already. -// -func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) { - ast.Inspect(x, func(x ast.Node) bool { - switch x := x.(type) { - case *ast.CallExpr: - // calls and conversions look the same - if !check.conversions[x] { - found = true - } - case *ast.UnaryExpr: - if x.Op == token.ARROW { - found = true - } - } - return !found // no need to continue if found - }) - return -} - -// unparen removes any parentheses surrounding an expression and returns -// the naked expression. -// -func unparen(x ast.Expr) ast.Expr { - if p, ok := x.(*ast.ParenExpr); ok { - return unparen(p.X) - } - return x -} - -func (check *checker) complexArg(x *operand) bool { - t, _ := underlying(x.typ).(*Basic) - if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) { - 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/src/pkg/go/types/check.go b/src/pkg/go/types/check.go deleted file mode 100644 index e8ee9bc336..0000000000 --- a/src/pkg/go/types/check.go +++ /dev/null @@ -1,512 +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 typechecks a package. - -package types - -import ( - "fmt" - "go/ast" - "go/token" -) - -// debugging support -const ( - debug = true // leave on during development - trace = false // turn on for detailed type resolution traces -) - -// exprInfo stores type and constant value for an untyped expression. -type exprInfo struct { - isConst bool // expression has a, possibly unknown, constant value - isLhs bool // expression is lhs operand of a shift with delayed type check - typ *Basic - val interface{} // constant value (may be nil if unknown); valid if isConst -} - -// A checker is an instance of the type checker. -type checker struct { - ctxt *Context - fset *token.FileSet - files []*ast.File - - // lazily initialized - pkg *Package // current package - firsterr error // first error encountered - idents map[*ast.Ident]Object // maps identifiers to their unique object - objects map[*ast.Object]Object // maps *ast.Objects to their unique object - initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations - methods map[*TypeName]*Scope // maps type names to associated methods - conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls) - untyped map[ast.Expr]exprInfo // map of expressions without final type - - // functions - funclist []function // list of functions/methods with correct signatures and non-empty bodies - funcsig *Signature // signature of currently typechecked function - pos []token.Pos // stack of expr positions; debugging support, used if trace is set -} - -func (check *checker) register(id *ast.Ident, obj Object) { - // When an expression is evaluated more than once (happens - // in rare cases, e.g. for statement expressions, see - // comment in stmt.go), the object has been registered - // before. Don't do anything in that case. - if alt := check.idents[id]; alt != nil { - assert(alt == obj) - return - } - check.idents[id] = obj - if f := check.ctxt.Ident; f != nil { - f(id, obj) - } -} - -// lookup returns the unique Object denoted by the identifier. -// For identifiers without assigned *ast.Object, it uses the -// checker.idents map; for identifiers with an *ast.Object it -// uses the checker.objects map. -// -// TODO(gri) Once identifier resolution is done entirely by -// the typechecker, only the idents map is needed. -// -func (check *checker) lookup(ident *ast.Ident) Object { - obj := check.idents[ident] - astObj := ident.Obj - - if obj != nil { - assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj) - return obj - } - - if astObj == nil { - return nil - } - - if obj = check.objects[astObj]; obj == nil { - obj = newObj(check.pkg, astObj) - check.objects[astObj] = obj - } - check.register(ident, obj) - - return obj -} - -type function struct { - obj *Func // for debugging/tracing only - sig *Signature - body *ast.BlockStmt -} - -// later adds a function with non-empty body to the list of functions -// that need to be processed after all package-level declarations -// are typechecked. -// -func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) { - // functions implemented elsewhere (say in assembly) have no body - if body != nil { - check.funclist = append(check.funclist, function{f, sig, body}) - } -} - -func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { - assert(check.lookup(ident) == nil) // identifier already declared or resolved - check.register(ident, obj) - if ident.Name != "_" { - if alt := scope.Insert(obj); alt != nil { - prevDecl := "" - if pos := alt.GetPos(); pos.IsValid() { - prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) - } - check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) - } - } -} - -func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) { - if len(lhs) == 0 { - check.invalidAST(pos, "missing lhs in declaration") - return - } - - // determine type for all of lhs, if any - // (but only set it for the object we typecheck!) - var typ Type - if spec.Type != nil { - typ = check.typ(spec.Type, false) - } - - // len(lhs) > 0 - rhs := spec.Values - if len(lhs) == len(rhs) { - // check only lhs and rhs corresponding to obj - var l, r ast.Expr - for i, name := range lhs { - if check.lookup(name) == obj { - l = lhs[i] - r = rhs[i] - break - } - } - assert(l != nil) - switch obj := obj.(type) { - case *Const: - obj.Type = typ - case *Var: - obj.Type = typ - default: - unreachable() - } - check.assign1to1(l, r, nil, true, iota) - return - } - - // there must be a type or initialization expressions - if typ == nil && len(rhs) == 0 { - check.invalidAST(pos, "missing type or initialization expression") - typ = Typ[Invalid] - } - - // if we have a type, mark all of lhs - if typ != nil { - for _, name := range lhs { - switch obj := check.lookup(name).(type) { - case *Const: - obj.Type = typ - case *Var: - obj.Type = typ - default: - unreachable() - } - } - } - - // check initial values, if any - if len(rhs) > 0 { - // TODO(gri) should try to avoid this conversion - lhx := make([]ast.Expr, len(lhs)) - for i, e := range lhs { - lhx[i] = e - } - check.assignNtoM(lhx, rhs, true, iota) - } -} - -// object typechecks an object by assigning it a type. -// -func (check *checker) object(obj Object, cycleOk bool) { - switch obj := obj.(type) { - case *Package: - // nothing to do - case *Const: - if obj.Type != nil { - return // already checked - } - // The obj.Val field for constants is initialized to its respective - // iota value (type int) by the parser. - // If the object's type is Typ[Invalid], the object value is ignored. - // If the object's type is valid, the object value must be a legal - // constant value; it may be nil to indicate that we don't know the - // value of the constant (e.g., in: "const x = float32("foo")" we - // know that x is a constant and has type float32, but we don't - // have a value due to the error in the conversion). - if obj.visited { - check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name) - obj.Type = Typ[Invalid] - return - } - obj.visited = true - spec := obj.spec - iota := obj.Val.(int) - obj.Val = nil // set to a valid (but unknown) constant value - // determine spec for type and initialization expressions - init := spec - if len(init.Values) == 0 { - init = check.initspecs[spec] - } - check.valueSpec(spec.Pos(), obj, spec.Names, init, iota) - - case *Var: - if obj.Type != nil { - return // already checked - } - if obj.visited { - check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name) - obj.Type = Typ[Invalid] - return - } - obj.visited = true - switch d := obj.decl.(type) { - case *ast.Field: - unreachable() // function parameters are always typed when collected - case *ast.ValueSpec: - check.valueSpec(d.Pos(), obj, d.Names, d, 0) - case *ast.AssignStmt: - unreachable() // assign1to1 sets the type for failing short var decls - default: - unreachable() // see also function newObj - } - - case *TypeName: - if obj.Type != nil { - return // already checked - } - typ := &NamedType{Obj: obj} - obj.Type = typ // "mark" object so recursion terminates - typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk)) - // typecheck associated method signatures - if scope := check.methods[obj]; scope != nil { - switch t := typ.Underlying.(type) { - case *Struct: - // struct fields must not conflict with methods - for _, f := range t.Fields { - if m := scope.Lookup(f.Name); m != nil { - check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name) - // ok to continue - } - } - case *Interface: - // methods cannot be associated with an interface type - for _, m := range scope.Entries { - recv := m.(*Func).decl.Recv.List[0].Type - check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) - // ok to continue - } - } - // typecheck method signatures - var methods []*Method - for _, obj := range scope.Entries { - m := obj.(*Func) - sig := check.typ(m.decl.Type, cycleOk).(*Signature) - params, _ := check.collectParams(m.decl.Recv, false) - sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter - m.Type = sig - methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig}) - check.later(m, sig, m.decl.Body) - } - typ.Methods = methods - delete(check.methods, obj) // we don't need this scope anymore - } - - case *Func: - if obj.Type != nil { - return // already checked - } - fdecl := obj.decl - // methods are typechecked when their receivers are typechecked - if fdecl.Recv == nil { - sig := check.typ(fdecl.Type, cycleOk).(*Signature) - if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) { - check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") - // ok to continue - } - obj.Type = sig - check.later(obj, sig, fdecl.Body) - } - - default: - unreachable() - } -} - -// assocInitvals associates "inherited" initialization expressions -// with the corresponding *ast.ValueSpec in the check.initspecs map -// for constant declarations without explicit initialization expressions. -// -func (check *checker) assocInitvals(decl *ast.GenDecl) { - var last *ast.ValueSpec - for _, s := range decl.Specs { - if s, ok := s.(*ast.ValueSpec); ok { - if len(s.Values) > 0 { - last = s - } else { - check.initspecs[s] = last - } - } - } - if last == nil { - check.invalidAST(decl.Pos(), "no initialization values provided") - } -} - -// assocMethod associates a method declaration with the respective -// receiver base type. meth.Recv must exist. -// -func (check *checker) assocMethod(meth *ast.FuncDecl) { - // The receiver type is one of the following (enforced by parser): - // - *ast.Ident - // - *ast.StarExpr{*ast.Ident} - // - *ast.BadExpr (parser error) - typ := meth.Recv.List[0].Type - if ptr, ok := typ.(*ast.StarExpr); ok { - typ = ptr.X - } - // determine receiver base type name - ident, ok := typ.(*ast.Ident) - if !ok { - // not an identifier - parser reported error already - return // ignore this method - } - // determine receiver base type object - var tname *TypeName - if obj := check.lookup(ident); obj != nil { - obj, ok := obj.(*TypeName) - if !ok { - check.errorf(ident.Pos(), "%s is not a type", ident.Name) - return // ignore this method - } - if obj.spec == nil { - check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name) - return // ignore this method - } - tname = obj - } else { - // identifier not declared/resolved - parser reported error already - return // ignore this method - } - // declare method in receiver base type scope - scope := check.methods[tname] - if scope == nil { - scope = new(Scope) - check.methods[tname] = scope - } - check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth}) -} - -func (check *checker) decl(decl ast.Decl) { - switch d := decl.(type) { - case *ast.BadDecl: - // ignore - case *ast.GenDecl: - for _, spec := range d.Specs { - switch s := spec.(type) { - case *ast.ImportSpec: - // nothing to do (handled by check.resolve) - case *ast.ValueSpec: - for _, name := range s.Names { - check.object(check.lookup(name), false) - } - case *ast.TypeSpec: - check.object(check.lookup(s.Name), false) - default: - check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) - } - } - case *ast.FuncDecl: - // methods are checked when their respective base types are checked - if d.Recv != nil { - return - } - obj := check.lookup(d.Name) - // Initialization functions don't have an object associated with them - // since they are not in any scope. Create a dummy object for them. - if d.Name.Name == "init" { - assert(obj == nil) // all other functions should have an object - obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d} - check.register(d.Name, obj) - } - check.object(obj, false) - default: - check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) - } -} - -// A bailout panic is raised to indicate early termination. -type bailout struct{} - -func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) { - // initialize checker - check := checker{ - ctxt: ctxt, - fset: fset, - files: files, - idents: make(map[*ast.Ident]Object), - objects: make(map[*ast.Object]Object), - initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec), - methods: make(map[*TypeName]*Scope), - conversions: make(map[*ast.CallExpr]bool), - untyped: make(map[ast.Expr]exprInfo), - } - - // set results and handle panics - defer func() { - pkg = check.pkg - switch p := recover().(type) { - case nil, bailout: - // normal return or early exit - err = check.firsterr - default: - // unexpected panic: don't crash clients - if debug { - check.dump("INTERNAL PANIC: %v", p) - panic(p) - } - // TODO(gri) add a test case for this scenario - err = fmt.Errorf("types internal error: %v", p) - } - }() - - // resolve identifiers - imp := ctxt.Import - if imp == nil { - imp = GcImport - } - methods := check.resolve(imp) - - // associate methods with types - for _, m := range methods { - check.assocMethod(m) - } - - // typecheck all declarations - for _, f := range check.files { - for _, d := range f.Decls { - check.decl(d) - } - } - - // typecheck all function/method bodies - // (funclist may grow when checking statements - do not use range clause!) - for i := 0; i < len(check.funclist); i++ { - f := check.funclist[i] - if trace { - s := "" - if f.obj != nil { - s = f.obj.Name - } - fmt.Println("---", s) - } - check.funcsig = f.sig - check.stmtList(f.body.List) - if len(f.sig.Results) > 0 && f.body != nil && !check.isTerminating(f.body, "") { - check.errorf(f.body.Rbrace, "missing return") - } - } - - // remaining untyped expressions must indeed be untyped - if debug { - for x, info := range check.untyped { - if !isUntyped(info.typ) { - check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, info.typ) - panic(0) - } - } - } - - // notify client of any untyped types left - // TODO(gri) Consider doing this before and - // after function body checking for smaller - // map size and more immediate feedback. - if ctxt.Expr != nil { - for x, info := range check.untyped { - var val interface{} - if info.isConst { - val = info.val - } - ctxt.Expr(x, info.typ, val) - } - } - - return -} diff --git a/src/pkg/go/types/check_test.go b/src/pkg/go/types/check_test.go deleted file mode 100644 index 2ee7f6eef8..0000000000 --- a/src/pkg/go/types/check_test.go +++ /dev/null @@ -1,244 +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 - -import ( - "flag" - "fmt" - "go/ast" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "os" - "regexp" - "testing" -) - -var listErrors = flag.Bool("list", false, "list errors") - -// 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. - -var tests = []struct { - name string - files []string -}{ - {"decls0", []string{"testdata/decls0.src"}}, - {"decls1", []string{"testdata/decls1.src"}}, - {"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}}, - {"decls3", []string{"testdata/decls3.src"}}, - {"const0", []string{"testdata/const0.src"}}, - {"expr0", []string{"testdata/expr0.src"}}, - {"expr1", []string{"testdata/expr1.src"}}, - {"expr2", []string{"testdata/expr2.src"}}, - {"expr3", []string{"testdata/expr3.src"}}, - {"shifts", []string{"testdata/shifts.src"}}, - {"builtins", []string{"testdata/builtins.src"}}, - {"conversions", []string{"testdata/conversions.src"}}, - {"stmt0", []string{"testdata/stmt0.src"}}, - {"stmt1", []string{"testdata/stmt1.src"}}, -} - -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, testname string, filenames []string) ([]*ast.File, []error) { - var files []*ast.File - var errlist []error - for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors) - if file == nil { - t.Fatalf("%s: could not parse file %s", testname, filename) - } - 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 be of the form /* ERROR "rx" */ and rx is -// a regular expression that matches the expected error message. -// -var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`) - -// 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, fset.Base(), len(src)), src, nil, scanner.ScanComments) - var prev string // position string of last non-comment, non-semicolon token - - scanFile: - for { - pos, tok, lit := s.Scan() - switch tok { - case token.EOF: - break scanFile - case token.COMMENT: - if s := errRx.FindStringSubmatch(lit); len(s) == 2 { - errmap[prev] = append(errmap[prev], s[1]) - } - case token.SEMICOLON: - // ignore automatically inserted semicolon - if lit == "\n" { - continue scanFile - } - fallthrough - default: - prev = fset.Position(pos).String() - } - } - } - - 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, testname string, testfiles []string) { - // parse files and collect parser errors - files, errlist := parseFiles(t, testname, testfiles) - - // typecheck and collect typechecker errors - var ctxt Context - ctxt.Error = func(err error) { errlist = append(errlist, err) } - ctxt.Check(fset, files) - - if *listErrors { - t.Errorf("--- %s: %d errors found:", testname, len(errlist)) - for _, err := range errlist { - t.Error(err) - } - return - } - - // match and eliminate errors; - // we are expecting the following errors - errmap := errMap(t, testname, 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:", testname, len(errmap)) - for pos, list := range errmap { - for _, rx := range list { - t.Errorf("%s: %q", pos, rx) - } - } - } -} - -var testBuiltinsDeclared = false - -func TestCheck(t *testing.T) { - // Declare builtins for testing. - // Not done in an init func to avoid an init race with - // the construction of the Universe var. - if !testBuiltinsDeclared { - testBuiltinsDeclared = true - // Pkg == nil for Universe objects - def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}}) - def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}}) - } - - // For easy debugging w/o changing the testing code, - // if there is a local test file, only test that file. - const testfile = "testdata/test.go" - if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() { - fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile) - checkFiles(t, testfile, []string{testfile}) - return - } - - // Otherwise, run all the tests. - for _, test := range tests { - checkFiles(t, test.name, test.files) - } -} diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go deleted file mode 100644 index 7df9038bbe..0000000000 --- a/src/pkg/go/types/const.go +++ /dev/null @@ -1,724 +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 operations on constant values. - -package types - -import ( - "fmt" - "go/token" - "math/big" - "strconv" -) - -// TODO(gri) At the moment, constants are different types -// passed around as interface{} values. Introduce a Const -// interface and use methods instead of xConst functions. - -// Representation of constant values. -// -// invalid -> nil (i.e., we don't know the constant value; this can only happen in erroneous programs) -// bool -> bool (true, false) -// numeric -> int64, *big.Int, *big.Rat, Complex (ordered by increasing data structure "size") -// string -> string -// nil -> NilType (nilConst) -// -// Numeric constants are normalized after each operation such -// that they are represented by the "smallest" data structure -// required to represent the constant, independent of actual -// type. Non-numeric constants are always normalized. - -// Representation of complex numbers. -type Complex struct { - Re, Im *big.Rat -} - -func (c Complex) String() string { - if c.Re.Sign() == 0 { - return fmt.Sprintf("%si", c.Im) - } - // normalized complex values always have an imaginary part - return fmt.Sprintf("(%s + %si)", c.Re, c.Im) -} - -// Representation of nil. -type NilType struct{} - -func (NilType) String() string { - return "nil" -} - -// Frequently used values. -var ( - nilConst = NilType{} - zeroConst = int64(0) -) - -// int64 bounds -var ( - minInt64 = big.NewInt(-1 << 63) - maxInt64 = big.NewInt(1<<63 - 1) -) - -// normalizeIntConst returns the smallest constant representation -// for the specific value of x; either an int64 or a *big.Int value. -// -func normalizeIntConst(x *big.Int) interface{} { - if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 { - return x.Int64() - } - return x -} - -// normalizeRatConst returns the smallest constant representation -// for the specific value of x; either an int64, *big.Int, -// or *big.Rat value. -// -func normalizeRatConst(x *big.Rat) interface{} { - if x.IsInt() { - return normalizeIntConst(x.Num()) - } - return x -} - -// newComplex returns the smallest constant representation -// for the specific value re + im*i; either an int64, *big.Int, -// *big.Rat, or complex value. -// -func newComplex(re, im *big.Rat) interface{} { - if im.Sign() == 0 { - return normalizeRatConst(re) - } - return Complex{re, im} -} - -// makeRuneConst returns the int64 code point for the rune literal -// lit. The result is nil if lit is not a correct rune literal. -// -func makeRuneConst(lit string) interface{} { - if n := len(lit); n >= 2 { - if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil { - return int64(code) - } - } - return nil -} - -// makeRuneConst returns the smallest integer constant representation -// (int64, *big.Int) for the integer literal lit. The result is nil if -// lit is not a correct integer literal. -// -func makeIntConst(lit string) interface{} { - if x, err := strconv.ParseInt(lit, 0, 64); err == nil { - return x - } - if x, ok := new(big.Int).SetString(lit, 0); ok { - return x - } - return nil -} - -// makeFloatConst returns the smallest floating-point constant representation -// (int64, *big.Int, *big.Rat) for the floating-point literal lit. The result -// is nil if lit is not a correct floating-point literal. -// -func makeFloatConst(lit string) interface{} { - if x, ok := new(big.Rat).SetString(lit); ok { - return normalizeRatConst(x) - } - return nil -} - -// makeComplexConst returns the complex constant representation (Complex) for -// the imaginary literal lit. The result is nil if lit is not a correct imaginary -// literal. -// -func makeComplexConst(lit string) interface{} { - n := len(lit) - if n > 0 && lit[n-1] == 'i' { - if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok { - return newComplex(big.NewRat(0, 1), im) - } - } - return nil -} - -// makeStringConst returns the string constant representation (string) for -// the string literal lit. The result is nil if lit is not a correct string -// literal. -// -func makeStringConst(lit string) interface{} { - if s, err := strconv.Unquote(lit); err == nil { - return s - } - return nil -} - -// toImagConst returns the constant Complex(0, x) for a non-complex x. -func toImagConst(x interface{}) interface{} { - var im *big.Rat - switch x := x.(type) { - case nil: - im = rat0 - case int64: - im = big.NewRat(x, 1) - case *big.Int: - im = new(big.Rat).SetFrac(x, int1) - case *big.Rat: - im = x - default: - unreachable() - } - return Complex{rat0, im} -} - -// isZeroConst reports whether the value of constant x is 0. -// x must be normalized. -// -func isZeroConst(x interface{}) bool { - i, ok := x.(int64) // good enough since constants are normalized - return ok && i == 0 -} - -// isRepresentableConst reports whether the value of constant x can -// be represented as a value of the basic type Typ[as] without loss -// of precision. -// -func isRepresentableConst(x interface{}, ctxt *Context, as BasicKind) bool { - if x == nil { - return true // avoid spurious errors - } - - switch x := x.(type) { - case bool: - return as == Bool || as == UntypedBool - - case int64: - switch as { - case Int: - var s = uint(ctxt.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: - var s = uint(ctxt.sizeof(Typ[as])) * 8 - return 0 <= x && x <= int64(1)<<(s-1)-1 - case Uint8: - const s = 8 - return 0 <= x && x <= 1<= 0 && x.BitLen() <= int(s) - case Uint64: - return x.Sign() >= 0 && x.BitLen() <= 64 - case Float32: - return true // TODO(gri) fix this - case Float64: - return true // TODO(gri) fix this - case Complex64: - return true // TODO(gri) fix this - case Complex128: - return true // TODO(gri) fix this - case UntypedInt, UntypedFloat, UntypedComplex: - return true - } - - case *big.Rat: - switch as { - case Float32: - return true // TODO(gri) fix this - case Float64: - return true // TODO(gri) fix this - case Complex64: - return true // TODO(gri) fix this - case Complex128: - return true // TODO(gri) fix this - case UntypedFloat, UntypedComplex: - return true - } - - case Complex: - switch as { - case Complex64: - return true // TODO(gri) fix this - case Complex128: - return true // TODO(gri) fix this - case UntypedComplex: - return true - } - - case string: - return as == String || as == UntypedString - - case NilType: - return as == UntypedNil || as == UnsafePointer - - default: - unreachable() - } - - return false -} - -var ( - int1 = big.NewInt(1) - rat0 = big.NewRat(0, 1) -) - -// complexity returns a measure of representation complexity for constant x. -func complexity(x interface{}) int { - switch x.(type) { - case bool, string, NilType: - return 1 - case int64: - return 2 - case *big.Int: - return 3 - case *big.Rat: - return 4 - case Complex: - return 5 - } - unreachable() - return 0 -} - -// matchConst returns the matching representation (same type) with the -// smallest complexity for two constant values x and y. They must be -// of the same "kind" (boolean, numeric, string, or NilType). -// -func matchConst(x, y interface{}) (_, _ interface{}) { - if complexity(x) > complexity(y) { - y, x = matchConst(y, x) - return x, y - } - // complexity(x) <= complexity(y) - - switch x := x.(type) { - case bool, Complex, string, NilType: - return x, y - - case int64: - switch y := y.(type) { - case int64: - return x, y - case *big.Int: - return big.NewInt(x), y - case *big.Rat: - return big.NewRat(x, 1), y - case Complex: - return Complex{big.NewRat(x, 1), rat0}, y - } - - case *big.Int: - switch y := y.(type) { - case *big.Int: - return x, y - case *big.Rat: - return new(big.Rat).SetFrac(x, int1), y - case Complex: - return Complex{new(big.Rat).SetFrac(x, int1), rat0}, y - } - - case *big.Rat: - switch y := y.(type) { - case *big.Rat: - return x, y - case Complex: - return Complex{x, rat0}, y - } - } - - unreachable() - return nil, nil -} - -// is32bit reports whether x can be represented using 32 bits. -func is32bit(x int64) bool { - return -1<<31 <= x && x <= 1<<31-1 -} - -// is63bit reports whether x can be represented using 63 bits. -func is63bit(x int64) bool { - return -1<<62 <= x && x <= 1<<62-1 -} - -// unaryOpConst returns the result of the constant evaluation op x where x is of the given type. -func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} { - if x == nil { - return nil - } - - switch op { - case token.ADD: - return x // nothing to do - case token.SUB: - switch x := x.(type) { - case int64: - if z := -x; z != x { - return z // no overflow - } - // overflow - need to convert to big.Int - return normalizeIntConst(new(big.Int).Neg(big.NewInt(x))) - case *big.Int: - return normalizeIntConst(new(big.Int).Neg(x)) - case *big.Rat: - return normalizeRatConst(new(big.Rat).Neg(x)) - case Complex: - return newComplex(new(big.Rat).Neg(x.Re), new(big.Rat).Neg(x.Im)) - } - case token.XOR: - var z big.Int - switch x := x.(type) { - case int64: - z.Not(big.NewInt(x)) - case *big.Int: - z.Not(x) - default: - unreachable() - } - // For unsigned types, the result will be negative and - // thus "too large": We must limit the result size to - // the type's size. - if typ.Info&IsUnsigned != 0 { - s := uint(ctxt.sizeof(typ)) * 8 - z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<>). x must be an -// integer constant. -// -func shiftConst(x interface{}, s uint, op token.Token) interface{} { - switch x := x.(type) { - case nil: - return nil - - case int64: - switch op { - case token.SHL: - z := big.NewInt(x) - return normalizeIntConst(z.Lsh(z, s)) - case token.SHR: - return x >> s - } - - case *big.Int: - var z big.Int - switch op { - case token.SHL: - return normalizeIntConst(z.Lsh(x, s)) - case token.SHR: - return normalizeIntConst(z.Rsh(x, s)) - } - } - - unreachable() - return nil -} - -// compareConst returns the result of the constant comparison x op y; -// both operands must be of the same "kind" (boolean, numeric, string, -// or NilType). -// -func compareConst(x, y interface{}, op token.Token) (z bool) { - if x == nil || y == nil { - return false - } - - x, y = matchConst(x, y) - - // x == y => x == y - // x != y => x != y - // x > y => y < x - // x >= y => u <= x - swap := false - switch op { - case token.GTR: - swap = true - op = token.LSS - case token.GEQ: - swap = true - op = token.LEQ - } - - // x == y => x == y - // x != y => !(x == y) - // x < y => x < y - // x <= y => !(y < x) - negate := false - switch op { - case token.NEQ: - negate = true - op = token.EQL - case token.LEQ: - swap = !swap - negate = true - op = token.LSS - } - - if negate { - defer func() { z = !z }() - } - - if swap { - x, y = y, x - } - - switch x := x.(type) { - case bool: - if op == token.EQL { - return x == y.(bool) - } - - case int64: - y := y.(int64) - switch op { - case token.EQL: - return x == y - case token.LSS: - return x < y - } - - case *big.Int: - s := x.Cmp(y.(*big.Int)) - switch op { - case token.EQL: - return s == 0 - case token.LSS: - return s < 0 - } - - case *big.Rat: - s := x.Cmp(y.(*big.Rat)) - switch op { - case token.EQL: - return s == 0 - case token.LSS: - return s < 0 - } - - case Complex: - y := y.(Complex) - if op == token.EQL { - return x.Re.Cmp(y.Re) == 0 && x.Im.Cmp(y.Im) == 0 - } - - case string: - y := y.(string) - switch op { - case token.EQL: - return x == y - case token.LSS: - return x < y - } - - case NilType: - if op == token.EQL { - return x == y.(NilType) - } - } - - fmt.Printf("x = %s (%T), y = %s (%T)\n", x, x, y, y) - unreachable() - return -} diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go deleted file mode 100644 index f75568e27c..0000000000 --- a/src/pkg/go/types/conversions.go +++ /dev/null @@ -1,155 +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 ( - "go/ast" -) - -// conversion typechecks the type conversion conv to type typ. iota is the current -// value of iota or -1 if iota doesn't have a value in the current context. The result -// of the conversion is returned via x. If the conversion has type errors, the returned -// x is marked as invalid (x.mode == invalid). -// -func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { - // all conversions have one argument - if len(conv.Args) != 1 { - check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) - goto Error - } - - // evaluate argument - check.expr(x, conv.Args[0], nil, iota) - if x.mode == invalid { - goto Error - } - - if x.mode == constant && isConstType(typ) { - // constant conversion - typ := underlying(typ).(*Basic) - // For now just implement string(x) where x is an integer, - // as a temporary work-around for issue 4982, which is a - // common issue. - if typ.Kind == String { - switch { - case x.isInteger(): - codepoint, ok := x.val.(int64) - if !ok { - // absolute value too large (or unknown) for conversion; - // same as converting any other out-of-range value - let - // string(codepoint) do the work - codepoint = -1 - } - x.val = string(codepoint) - case isString(x.typ): - // nothing to do - default: - goto ErrorMsg - } - } - // TODO(gri) verify the remaining conversions. - } else { - // non-constant conversion - if !x.isConvertible(check.ctxt, typ) { - goto ErrorMsg - } - x.mode = value - } - - // the conversion argument types are final - check.updateExprType(x.expr, x.typ, true) - - check.conversions[conv] = true // for cap/len checking - x.expr = conv - x.typ = typ - return - -ErrorMsg: - check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) -Error: - x.mode = invalid - x.expr = conv -} - -func (x *operand) isConvertible(ctxt *Context, T Type) bool { - // "x is assignable to T" - if x.isAssignable(ctxt, T) { - return true - } - - // "x's type and T have identical underlying types" - V := x.typ - Vu := underlying(V) - Tu := underlying(T) - if IsIdentical(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 IsIdentical(underlying(V.Base), underlying(T.Base)) { - 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.(*Basic) - return ok && t.Kind == Uintptr -} - -func isUnsafePointer(typ Type) bool { - t, ok := typ.(*Basic) - return ok && t.Kind == UnsafePointer -} - -func isPointer(typ Type) bool { - _, ok := typ.(*Pointer) - return ok -} - -func isBytesOrRunes(typ Type) bool { - if s, ok := typ.(*Slice); ok { - t, ok := underlying(s.Elt).(*Basic) - return ok && (t.Kind == Byte || t.Kind == Rune) - } - return false -} diff --git a/src/pkg/go/types/errors.go b/src/pkg/go/types/errors.go deleted file mode 100644 index 62ee547917..0000000000 --- a/src/pkg/go/types/errors.go +++ /dev/null @@ -1,335 +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 ( - "bytes" - "fmt" - "go/ast" - "go/token" -) - -// TODO(gri) eventually assert and unimplemented should disappear. -func assert(p bool) { - if !p { - panic("assertion failed") - } -} - -func unreachable() { - panic("unreachable") -} - -func (check *checker) printTrace(format string, args []interface{}) { - const dots = ". . . . . . . . . . . . . . . . . . . . " - n := len(check.pos) - 1 - i := 3 * n - for i > len(dots) { - fmt.Print(dots) - i -= len(dots) - } - // i <= len(dots) - fmt.Printf("%s:\t", check.fset.Position(check.pos[n])) - fmt.Print(dots[0:i]) - fmt.Println(check.formatMsg(format, args)) -} - -func (check *checker) trace(pos token.Pos, format string, args ...interface{}) { - check.pos = append(check.pos, pos) - check.printTrace(format, args) -} - -func (check *checker) untrace(format string, args ...interface{}) { - if len(format) > 0 { - check.printTrace(format, args) - } - check.pos = check.pos[:len(check.pos)-1] -} - -func (check *checker) formatMsg(format string, args []interface{}) string { - for i, arg := range args { - switch a := arg.(type) { - case token.Pos: - args[i] = check.fset.Position(a).String() - case ast.Expr: - args[i] = exprString(a) - case Type: - args[i] = typeString(a) - case operand: - panic("internal error: should always pass *operand") - } - } - return fmt.Sprintf(format, args...) -} - -// dump is only needed for debugging -func (check *checker) dump(format string, args ...interface{}) { - fmt.Println(check.formatMsg(format, args)) -} - -func (check *checker) err(err error) { - if check.firsterr == nil { - check.firsterr = err - } - f := check.ctxt.Error - if f == nil { - panic(bailout{}) // report only first error - } - f(err) -} - -func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) { - check.err(fmt.Errorf("%s: %s", check.fset.Position(pos), check.formatMsg(format, args))) -} - -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...) -} - -// exprString returns a (simplified) string representation for an expression. -func exprString(expr ast.Expr) string { - var buf bytes.Buffer - writeExpr(&buf, expr) - return buf.String() -} - -// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a)) -func writeExpr(buf *bytes.Buffer, expr ast.Expr) { - switch x := expr.(type) { - case *ast.Ident: - buf.WriteString(x.Name) - - case *ast.BasicLit: - buf.WriteString(x.Value) - - case *ast.FuncLit: - buf.WriteString("(func literal)") - - case *ast.CompositeLit: - buf.WriteString("(composite literal)") - - 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) - } - buf.WriteByte(']') - - case *ast.TypeAssertExpr: - writeExpr(buf, x.X) - buf.WriteString(".(...)") - - case *ast.CallExpr: - writeExpr(buf, x.Fun) - buf.WriteByte('(') - for i, arg := range x.Args { - if i > 0 { - buf.WriteString(", ") - } - writeExpr(buf, arg) - } - 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: - // The AST preserves source-level parentheses so there is - // no need to introduce parentheses here for correctness. - writeExpr(buf, x.X) - buf.WriteByte(' ') - buf.WriteString(x.Op.String()) - buf.WriteByte(' ') - writeExpr(buf, x.Y) - - default: - fmt.Fprintf(buf, "", x) - } -} - -// typeString returns a string representation for typ. -func typeString(typ Type) string { - var buf bytes.Buffer - writeType(&buf, typ) - return buf.String() -} - -func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) { - buf.WriteByte('(') - for i, par := range params { - if i > 0 { - buf.WriteString(", ") - } - if par.Name != "" { - buf.WriteString(par.Name) - buf.WriteByte(' ') - } - if isVariadic && i == len(params)-1 { - buf.WriteString("...") - } - writeType(buf, par.Type) - } - buf.WriteByte(')') -} - -func writeSignature(buf *bytes.Buffer, sig *Signature) { - writeParams(buf, sig.Params, sig.IsVariadic) - if len(sig.Results) == 0 { - // no result - return - } - - buf.WriteByte(' ') - if len(sig.Results) == 1 && sig.Results[0].Name == "" { - // single unnamed result - writeType(buf, sig.Results[0].Type.(Type)) - return - } - - // multiple or named result(s) - writeParams(buf, sig.Results, false) -} - -func writeType(buf *bytes.Buffer, typ Type) { - switch t := typ.(type) { - case nil: - buf.WriteString("") - - case *Basic: - buf.WriteString(t.Name) - - case *Array: - fmt.Fprintf(buf, "[%d]", t.Len) - writeType(buf, t.Elt) - - case *Slice: - buf.WriteString("[]") - writeType(buf, t.Elt) - - case *Struct: - buf.WriteString("struct{") - for i, f := range t.Fields { - if i > 0 { - buf.WriteString("; ") - } - if !f.IsAnonymous { - buf.WriteString(f.Name) - buf.WriteByte(' ') - } - writeType(buf, f.Type) - if f.Tag != "" { - fmt.Fprintf(buf, " %q", f.Tag) - } - } - buf.WriteByte('}') - - case *Pointer: - buf.WriteByte('*') - writeType(buf, t.Base) - - case *Result: - writeParams(buf, t.Values, false) - - case *Signature: - buf.WriteString("func") - writeSignature(buf, t) - - case *builtin: - fmt.Fprintf(buf, "", t.name) - - case *Interface: - buf.WriteString("interface{") - for i, m := range t.Methods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.Name) - writeSignature(buf, m.Type) - } - buf.WriteByte('}') - - case *Map: - buf.WriteString("map[") - writeType(buf, t.Key) - buf.WriteByte(']') - writeType(buf, t.Elt) - - case *Chan: - var s string - switch t.Dir { - case ast.SEND: - s = "chan<- " - case ast.RECV: - s = "<-chan " - default: - s = "chan " - } - buf.WriteString(s) - writeType(buf, t.Elt) - - case *NamedType: - s := "" - if obj := t.Obj; obj != nil { - if obj.Pkg != nil && obj.Pkg.Path != "" { - buf.WriteString(obj.Pkg.Path) - buf.WriteString(".") - } - s = t.Obj.GetName() - } - buf.WriteString(s) - - default: - fmt.Fprintf(buf, "", t) - } -} - -func (t *Array) String() string { return typeString(t) } -func (t *Basic) String() string { return typeString(t) } -func (t *Chan) String() string { return typeString(t) } -func (t *Interface) String() string { return typeString(t) } -func (t *Map) String() string { return typeString(t) } -func (t *NamedType) String() string { return typeString(t) } -func (t *Pointer) String() string { return typeString(t) } -func (t *Result) String() string { return typeString(t) } -func (t *Signature) String() string { return typeString(t) } -func (t *Slice) String() string { return typeString(t) } -func (t *Struct) String() string { return typeString(t) } -func (t *builtin) String() string { return typeString(t) } diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go deleted file mode 100644 index 1f6a3c7252..0000000000 --- a/src/pkg/go/types/exportdata.go +++ /dev/null @@ -1,111 +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 FindGcExportData. - -package types - -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 -} - -// FindGcExportData 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 FindGcExportData(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, which should - // be second archive entry. - var name string - var size int - - // First entry should be __.GOSYMDEF. - // Older archives used __.SYMDEF, so allow that too. - // Read and discard. - if name, size, err = readGopackHeader(r); err != nil { - return - } - if name != "__.SYMDEF" && name != "__.GOSYMDEF" { - err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF") - return - } - 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 - } - - // Second entry should be __.PKGDEF. - if name, size, err = readGopackHeader(r); err != nil { - return - } - 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/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go deleted file mode 100644 index 5ccd75f99b..0000000000 --- a/src/pkg/go/types/expr.go +++ /dev/null @@ -1,1637 +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 ( - "go/ast" - "go/token" - "strconv" -) - -// TODO(gri) Cleanups -// - don't print error messages referring to invalid types (they are likely spurious errors) -// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values? -// - rethink error handling: should all callers check if x.mode == valid after making a call? -// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used -// - use "" or "_" consistently for anonymous identifiers? (e.g. reeceivers that have no name) -// - consider storing error messages in invalid operands for better error messages/debugging output - -// TODO(gri) API issues -// - clients need access to builtins type information -// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks) - -/* -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). - -If an iota argument >= 0 is present, it is the value of iota for the -specific expression. - -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, -if present the Context.Expr method is invoked to notify a go/types client. -*/ - -func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) { - if list == nil { - return - } - var last *Var - 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 { - isVariadic = true - } else { - check.invalidAST(field.Pos(), "... not permitted") - // ok to continue - } - } - // the parser ensures that f.Tag is nil and we don't - // care if a constructed AST contains a non-nil tag - typ := check.typ(ftype, true) - if len(field.Names) > 0 { - // named parameter - for _, name := range field.Names { - par := check.lookup(name).(*Var) - par.Type = typ - last = par - copy := *par - params = append(params, ©) - } - } else { - // anonymous parameter - par := &Var{Type: typ} - last = nil // not accessible inside function - params = append(params, par) - } - } - // For a variadic function, change the last parameter's object type - // from T to []T (this is the type used inside the function), but - // keep the params list unchanged (this is the externally visible type). - if isVariadic && last != nil { - last.Type = &Slice{Elt: last.Type} - } - return -} - -func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) { - if list == nil { - return - } - for _, f := range list.List { - typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces - // the parser ensures that f.Tag is nil and we don't - // care if a constructed AST contains a non-nil tag - if len(f.Names) > 0 { - // methods (the parser ensures that there's only one - // and we don't care if a constructed AST has more) - sig, ok := typ.(*Signature) - if !ok { - check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ) - continue - } - for _, name := range f.Names { - methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig}) - } - } else { - // embedded interface - utyp := underlying(typ) - if ityp, ok := utyp.(*Interface); ok { - methods = append(methods, ityp.Methods...) - } else if utyp != Typ[Invalid] { - // if utyp is invalid, don't complain (the root cause was reported before) - check.errorf(f.Type.Pos(), "%s is not an interface type", typ) - } - } - } - // Check for double declarations. - // The parser inserts methods into an interface-local scope, so local - // double declarations are reported by the parser already. We need to - // check again for conflicts due to embedded interfaces. This will lead - // to a 2nd error message if the double declaration was reported before - // by the parser. - // TODO(gri) clean this up a bit - seen := make(map[string]bool) - for _, m := range methods { - if seen[m.Name] { - check.errorf(list.Pos(), "multiple methods named %s", m.Name) - return // keep multiple entries, lookup will only return the first entry - } - seen[m.Name] = true - } - return -} - -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) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) { - if list == nil { - return - } - - var typ Type // current field typ - var tag string // current field tag - add := func(name string, isAnonymous bool) { - fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous}) - } - - for _, f := range list.List { - typ = check.typ(f.Type, cycleOk) - tag = check.tag(f.Tag) - if len(f.Names) > 0 { - // named fields - for _, name := range f.Names { - add(name.Name, false) - } - } else { - // anonymous field - switch t := deref(typ).(type) { - case *Basic: - add(t.Name, true) - case *NamedType: - add(t.Obj.GetName(), true) - default: - if typ != Typ[Invalid] { - check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) - } - } - } - } - - return -} - -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 -} - -func (check *checker) unary(x *operand, 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 - } - if x.mode != variable { - check.invalidOp(x.pos(), "cannot take address of %s", x) - goto Error - } - x.typ = &Pointer{Base: x.typ} - return - - case token.ARROW: - typ, ok := underlying(x.typ).(*Chan) - if !ok { - check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) - goto Error - } - if typ.Dir&ast.RECV == 0 { - check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) - goto Error - } - x.mode = valueok - x.typ = typ.Elt - return - } - - if !check.op(unaryOpPredicates, x, op) { - goto Error - } - - if x.mode == constant { - typ := underlying(x.typ).(*Basic) - x.val = unaryOpConst(x.val, check.ctxt, op, typ) - // Typed constants must be representable in - // their type after each constant operation. - check.isRepresentable(x, typ) - return - } - - x.mode = value - // x.typ remains unchanged - return - -Error: - x.mode = invalid -} - -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 -} - -// isRepresentable checks that a constant operand is representable in the given type. -func (check *checker) isRepresentable(x *operand, typ *Basic) { - if x.mode != constant || isUntyped(typ) { - return - } - - if !isRepresentableConst(x.val, check.ctxt, typ.Kind) { - var msg string - if isNumeric(x.typ) && isNumeric(typ) { - msg = "%s overflows (or cannot be accurately represented as) %s" - } 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), Context.Expr is invoked, if present. -// 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.isConst { - break - } - check.updateExprType(x.X, typ, final) - - case *ast.BinaryExpr: - if old.isConst { - 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 = underlying(typ).(*Basic) - check.untyped[x] = old - return - } - - // Otherwise we have the final (typed or untyped type). - // Remove it from the map. - 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, notify client of final type for x. - if f := check.ctxt.Expr; f != nil { - var val interface{} - if old.isConst { - val = old.val - } - f(x, typ, val) - } -} - -// 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 || !isUntyped(x.typ) { - 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 := underlying(target).(type) { - case nil: - // We may reach here due to previous type errors. - // Be conservative and don't crash. - x.mode = invalid - return - case *Basic: - check.isRepresentable(x, t) - if x.mode == invalid { - return // error already reported - } - case *Interface: - if !x.isNil() && len(t.Methods) > 0 /* 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 len(t.Methods) > 0 { - 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: - if debug { - check.dump("convertUntyped(x = %v, target = %v)", x, target) - } - unreachable() - } - - 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) { - // TODO(gri) deal with interface vs non-interface comparison - - valid := false - if x.isAssignable(check.ctxt, y.typ) || y.isAssignable(check.ctxt, x.typ) { - switch op { - case token.EQL, token.NEQ: - valid = isComparable(x.typ) || - x.isNil() && hasNil(y.typ) || - y.isNil() && hasNil(x.typ) - case token.LSS, token.LEQ, token.GTR, token.GEQ: - valid = isOrdered(x.typ) - default: - unreachable() - } - } - - if !valid { - check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y) - x.mode = invalid - return - } - - if x.mode == constant && y.mode == constant { - x.val = compareConst(x.val, y.val, op) - } else { - x.mode = value - } - - // The result type of a comparison is always boolean and - // independent of the argument types. They have now their - // final types (untyped or typed): update the respective - // expression trees. - check.updateExprType(x.expr, x.typ, true) - check.updateExprType(y.expr, y.typ, true) - - 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 !isInteger(x.typ) && (!untypedx || !isRepresentableConst(x.val, nil, UntypedInt)) { - 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 { - if untypedx { - x.typ = Typ[UntypedInt] - } - if x.val != nil && y.val != nil { - // rhs must be within reasonable bounds - const stupidShift = 1024 - s, ok := y.val.(int64) - if !ok || s < 0 || s >= stupidShift { - check.invalidOp(y.pos(), "%s: stupid shift", y) - x.mode = invalid - return - } - // everything's ok - x.val = shiftConst(x.val, uint(s), op) - } else { - x.val = nil - } - 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; - // the type is int if it cannot be determined from the context (for - // instance, if the shift expression is an operand in a comparison - // against an untyped constant)". - - // 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. - if info, ok := check.untyped[x.expr]; ok { - info.isLhs = true - check.untyped[x.expr] = info - } else { - unreachable() - } - // keep x's type - x.mode = value - return - } - } - - // 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 - } - - // non-constant shift - 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, -} - -func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) { - var y operand - - check.expr(x, lhs, nil, iota) - check.expr(&y, rhs, nil, iota) - - 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 !IsIdentical(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) && y.mode == constant && isZeroConst(y.val) { - check.invalidOp(y.pos(), "division by zero") - x.mode = invalid - return - } - - if x.mode == constant && y.mode == constant { - typ := underlying(x.typ).(*Basic) - x.val = binaryOpConst(x.val, y.val, op, typ) - // Typed constants must be representable in - // their type after each constant operation. - check.isRepresentable(x, typ) - return - } - - x.mode = value - // x.typ is unchanged -} - -// index checks an index/size expression arg for validity. -// If length >= 0, it is the upper bound for arg. -// TODO(gri): Do we need iota? -func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) { - var x operand - check.expr(&x, arg, nil, iota) - - // an untyped constant must be representable as Int - check.convertUntyped(&x, Typ[Int]) - if x.mode == invalid { - return - } - - // the index/size must be of integer type - if !isInteger(x.typ) { - check.invalidArg(x.pos(), "%s must be integer", &x) - return - } - - // a constant index/size i must be 0 <= i < length - if x.mode == constant && x.val != nil { - i = x.val.(int64) - if i < 0 { - check.invalidArg(x.pos(), "%s must not be negative", &x) - return - } - if length >= 0 && i >= length { - check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length) - return - } - // 0 <= i [ && i < length ] - return i, true - } - - return -1, true -} - -// compositeLitKey resolves unresolved composite literal keys. -// For details, see comment in go/parser/parser.go, method parseElement. -func (check *checker) compositeLitKey(key ast.Expr) { - if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil { - if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil { - check.register(ident, obj) - } else if obj := Universe.Lookup(ident.Name); obj != nil { - check.register(ident, obj) - } else { - check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) - } - } -} - -// 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, iota int) 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 { - check.compositeLitKey(kv.Key) - if i, ok := check.index(kv.Key, length, iota); ok { - if i >= 0 { - index = i - } - validIndex = true - } - 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.expr(&x, eval, typ, iota) - 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 -} - -// argument typechecks passing an argument arg (if arg != nil) or -// x (if arg == nil) to the i'th parameter of the given signature. -// If passSlice is set, the argument is followed by ... in the call. -// -func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { - // determine parameter - var par *Var - n := len(sig.Params) - if i < n { - par = sig.Params[i] - } else if sig.IsVariadic { - par = sig.Params[n-1] - } else { - check.errorf(arg.Pos(), "too many arguments") - return - } - - // determine argument - var z operand - z.mode = variable - z.expr = nil // TODO(gri) can we do better here? (for good error messages) - z.typ = par.Type - - if arg != nil { - check.expr(x, arg, z.typ, -1) - } - if x.mode == invalid { - return // ignore this argument - } - - // check last argument of the form x... - if passSlice { - if i+1 != n { - check.errorf(x.pos(), "can only use ... with matching parameter") - return // ignore this argument - } - // spec: "If the final argument is assignable to a slice type []T, - // it may be passed unchanged as the value for a ...T parameter if - // the argument is followed by ..." - z.typ = &Slice{Elt: z.typ} // change final parameter type to []T - } - - if !check.assignment(x, z.typ) && x.mode != invalid { - check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) - } -} - -var emptyResult Result - -func (check *checker) callExpr(x *operand) { - // convert x into a user-friendly set of values - var typ Type - var val interface{} - switch x.mode { - case invalid: - return // nothing to do - case novalue: - typ = &emptyResult - case constant: - typ = x.typ - val = x.val - default: - typ = x.typ - } - - // if the operand is untyped, delay notification - // until it becomes typed or until the end of - // type checking - if isUntyped(typ) { - check.untyped[x.expr] = exprInfo{x.mode == constant, false, typ.(*Basic), val} - return - } - - // TODO(gri) ensure that literals always report - // their dynamic (never interface) type. - // This is not the case yet. - - if check.ctxt.Expr != nil { - check.ctxt.Expr(x.expr, typ, val) - } -} - -// 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. -// iota >= 0 indicates that the expression is part of a constant declaration. -// cycleOk indicates whether it is ok for a type expression to refer to itself. -// -func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) { - if trace { - c := "" - if cycleOk { - c = " ⨁" - } - check.trace(e.Pos(), "%s (%s, %d%s)", e, typeString(hint), iota, c) - defer check.untrace("=> %s", x) - } - - // record final type of x if untyped, notify clients of type otherwise - defer check.callExpr(x) - - switch e := e.(type) { - case *ast.BadExpr: - goto Error // error was reported before - - case *ast.Ident: - if e.Name == "_" { - check.invalidOp(e.Pos(), "cannot use _ as value or type") - goto Error - } - obj := check.lookup(e) - if obj == nil { - goto Error // error was reported before - } - check.object(obj, cycleOk) - switch obj := obj.(type) { - case *Package: - check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) - goto Error - case *Const: - if obj.Type == Typ[Invalid] { - goto Error - } - x.mode = constant - if obj == universeIota { - if iota < 0 { - check.invalidAST(e.Pos(), "cannot use iota outside constant declaration") - goto Error - } - x.val = int64(iota) - } else { - x.val = obj.Val // may be nil if we don't know the constant value - } - case *TypeName: - x.mode = typexpr - if !cycleOk && underlying(obj.Type) == nil { - check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) - x.expr = e - x.typ = Typ[Invalid] - return // don't goto Error - need x.mode == typexpr - } - case *Var: - x.mode = variable - case *Func: - x.mode = value - default: - unreachable() - } - x.typ = obj.GetType() - - case *ast.Ellipsis: - // ellipses are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.errorf(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, false).(*Signature); ok { - x.mode = value - x.typ = sig - check.later(nil, sig, e.Body) - } 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, Elt: check.typ(atyp.Elt, cycleOk)} - openArray = true - } - } - if typ == nil { - typ = check.typ(e.Type, false) - } - } - if typ == nil { - check.errorf(e.Pos(), "missing type in composite literal") - goto Error - } - - switch utyp := underlying(deref(typ)).(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.errorf(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 := utyp.fieldIndex(QualifiedName{check.pkg, key.Name}) - if i < 0 { - check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) - continue - } - // 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, nil, iota) - etyp := fields[i].Type - 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.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") - continue - } - check.expr(x, e, nil, iota) - if i >= len(fields) { - check.errorf(x.pos(), "too many values in struct literal") - break // cannot continue - } - // i < len(fields) - etyp := fields[i].Type - 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.errorf(e.Rbrace, "too few values in struct literal") - // ok to continue - } - } - - case *Array: - n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota) - // if we have an "open" [...]T array, set the length now that we know it - if openArray { - utyp.Len = n - } - - case *Slice: - check.indexedElts(e.Elts, utyp.Elt, -1, iota) - - case *Map: - visited := make(map[interface{}]bool, len(e.Elts)) - for _, e := range e.Elts { - kv, _ := e.(*ast.KeyValueExpr) - if kv == nil { - check.errorf(e.Pos(), "missing key in map literal") - continue - } - check.compositeLitKey(kv.Key) - check.expr(x, kv.Key, nil, iota) - 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 && x.val != nil { - if visited[x.val] { - check.errorf(x.pos(), "duplicate key %s in map literal", x.val) - continue - } - visited[x.val] = true - } - check.expr(x, kv.Value, utyp.Elt, iota) - if !check.assignment(x, utyp.Elt) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt) - } - continue - } - } - - default: - check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) - goto Error - } - - x.mode = value - x.typ = typ - - case *ast.ParenExpr: - check.rawExpr(x, e.X, nil, iota, cycleOk) - - case *ast.SelectorExpr: - 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 { - if pkg, ok := check.lookup(ident).(*Package); ok { - exp := pkg.Scope.Lookup(sel) - // gcimported package scopes contain non-exported - // objects such as types used in partially exported - // objects - do not accept them - if exp == nil || !ast.IsExported(exp.GetName()) { - check.errorf(e.Pos(), "cannot refer to unexported %s", e) - goto Error - } - check.register(e.Sel, exp) - // Simplified version of the code for *ast.Idents: - // - imported packages use types.Scope and types.Objects - // - imported objects are always fully initialized - switch exp := exp.(type) { - case *Const: - assert(exp.Val != nil) - x.mode = constant - x.typ = exp.Type - x.val = exp.Val - case *TypeName: - x.mode = typexpr - x.typ = exp.Type - case *Var: - x.mode = variable - x.typ = exp.Type - case *Func: - x.mode = value - x.typ = exp.Type - default: - unreachable() - } - x.expr = e - return - } - } - - check.exprOrType(x, e.X, iota, false) - if x.mode == invalid { - goto Error - } - res := lookupField(x.typ, QualifiedName{check.pkg, sel}) - if res.mode == invalid { - check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) - goto Error - } - if x.mode == typexpr { - // method expression - sig, ok := res.typ.(*Signature) - if !ok { - check.invalidOp(e.Pos(), "%s has no method %s", x, sel) - goto Error - } - // the receiver type becomes the type of the first function - // argument of the method expression's function type - // TODO(gri) at the moment, method sets don't correctly track - // pointer vs non-pointer receivers => typechecker is too lenient - x.mode = value - x.typ = &Signature{ - Params: append([]*Var{{Type: x.typ}}, sig.Params...), - Results: sig.Results, - IsVariadic: sig.IsVariadic, - } - } else { - // regular selector - x.mode = res.mode - x.typ = res.typ - } - - case *ast.IndexExpr: - check.expr(x, e.X, nil, iota) - if x.mode == invalid { - goto Error - } - - valid := false - length := int64(-1) // valid if >= 0 - switch typ := underlying(x.typ).(type) { - case *Basic: - if isString(typ) { - valid = true - if x.mode == constant && x.val != nil { - length = int64(len(x.val.(string))) - } - // 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 = Typ[Byte] - } - - case *Array: - valid = true - length = typ.Len - if x.mode != variable { - x.mode = value - } - x.typ = typ.Elt - - case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { - valid = true - length = typ.Len - x.mode = variable - x.typ = typ.Elt - } - - case *Slice: - valid = true - x.mode = variable - x.typ = typ.Elt - - case *Map: - var key operand - check.expr(&key, e.Index, nil, iota) - 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 = valueok - x.typ = typ.Elt - x.expr = e - return - } - - if !valid { - check.invalidOp(x.pos(), "cannot index %s", x) - goto Error - } - - if e.Index == nil { - check.invalidAST(e.Pos(), "missing index expression for %s", x) - return - } - - check.index(e.Index, length, iota) - // ok to continue - - case *ast.SliceExpr: - check.expr(x, e.X, nil, iota) - if x.mode == invalid { - goto Error - } - - valid := false - length := int64(-1) // valid if >= 0 - switch typ := underlying(x.typ).(type) { - case *Basic: - if isString(typ) { - valid = true - if x.mode == constant && x.val != nil { - length = int64(len(x.val.(string))) + 1 // +1 for slice - } - // a sliced string always yields a string value - // of the same type as the original string (not - // a constant) even if the string and the indices - // are constant - x.mode = value - // x.typ doesn't change, but if it is an untyped - // string it becomes string (see also issue 4913). - if typ.Kind == UntypedString { - x.typ = Typ[String] - } - } - - case *Array: - valid = true - length = typ.Len + 1 // +1 for slice - if x.mode != variable { - check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) - goto Error - } - x.typ = &Slice{Elt: typ.Elt} - - case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { - valid = true - length = typ.Len + 1 // +1 for slice - x.mode = variable - x.typ = &Slice{Elt: typ.Elt} - } - - case *Slice: - valid = true - x.mode = variable - // x.typ doesn't change - } - - if !valid { - check.invalidOp(x.pos(), "cannot slice %s", x) - goto Error - } - - lo := int64(0) - if e.Low != nil { - if i, ok := check.index(e.Low, length, iota); ok && i >= 0 { - lo = i - } - } - - hi := int64(-1) - if e.High != nil { - if i, ok := check.index(e.High, length, iota); ok && i >= 0 { - hi = i - } - } else if length >= 0 { - hi = length - } - - if lo >= 0 && hi >= 0 && lo > hi { - check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi) - // ok to continue - } - - case *ast.TypeAssertExpr: - check.expr(x, e.X, nil, iota) - if x.mode == invalid { - goto Error - } - var T *Interface - if T, _ = underlying(x.typ).(*Interface); T == 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.errorf(e.Pos(), "use of .(type) outside type switch") - goto Error - } - typ := check.typ(e.Type, false) - if typ == Typ[Invalid] { - goto Error - } - if method, wrongType := missingMethod(typ, T); method != nil { - var msg string - if wrongType { - msg = "%s cannot have dynamic type %s (wrong type for method %s)" - } else { - msg = "%s cannot have dynamic type %s (missing method %s)" - } - check.errorf(e.Type.Pos(), msg, x, typ, method.Name) - // ok to continue - } - x.mode = valueok - x.expr = e - x.typ = typ - - case *ast.CallExpr: - check.exprOrType(x, e.Fun, iota, false) - if x.mode == invalid { - goto Error - } else if x.mode == typexpr { - check.conversion(x, e, x.typ, iota) - } else if sig, ok := underlying(x.typ).(*Signature); ok { - // check parameters - - // If we have a trailing ... at the end of the parameter - // list, the last argument must match the parameter type - // []T of a variadic function parameter x ...T. - passSlice := false - if e.Ellipsis.IsValid() { - if sig.IsVariadic { - passSlice = true - } else { - check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) - // ok to continue - } - } - - // If we have a single argument that is a function call - // we need to handle it separately. Determine if this - // is the case without checking the argument. - var call *ast.CallExpr - if len(e.Args) == 1 { - call, _ = unparen(e.Args[0]).(*ast.CallExpr) - } - - n := 0 // parameter count - if call != nil { - // We have a single argument that is a function call. - check.expr(x, call, nil, -1) - if x.mode == invalid { - goto Error // TODO(gri): we can do better - } - if t, _ := x.typ.(*Result); t != nil { - // multiple result values - n = len(t.Values) - for i, obj := range t.Values { - x.mode = value - x.expr = nil // TODO(gri) can we do better here? (for good error messages) - x.typ = obj.Type - check.argument(sig, i, nil, x, passSlice && i+1 == n) - } - } else { - // single result value - n = 1 - check.argument(sig, 0, nil, x, passSlice) - } - - } else { - // We don't have a single argument or it is not a function call. - n = len(e.Args) - for i, arg := range e.Args { - check.argument(sig, i, arg, x, passSlice && i+1 == n) - } - } - - // determine if we have enough arguments - if sig.IsVariadic { - // a variadic function accepts an "empty" - // last argument: count one extra - n++ - } - if n < len(sig.Params) { - check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) - // ok to continue - } - - // determine result - switch len(sig.Results) { - case 0: - x.mode = novalue - case 1: - x.mode = value - x.typ = sig.Results[0].Type - default: - x.mode = value - x.typ = &Result{Values: sig.Results} - } - - } else if bin, ok := x.typ.(*builtin); ok { - check.builtin(x, e, bin, iota) - - } else { - check.invalidOp(x.pos(), "cannot call non-function %s", x) - goto Error - } - - case *ast.StarExpr: - check.exprOrType(x, e.X, iota, true) - switch x.mode { - case invalid: - goto Error - case typexpr: - x.typ = &Pointer{Base: x.typ} - default: - if typ, ok := underlying(x.typ).(*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, nil, iota) - if x.mode == invalid { - goto Error - } - check.unary(x, e.Op) - if x.mode == invalid { - goto Error - } - - case *ast.BinaryExpr: - check.binary(x, e.X, e.Y, e.Op, iota) - 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: - if e.Len != nil { - check.expr(x, e.Len, nil, iota) - if x.mode == invalid { - goto Error - } - if x.mode != constant { - if x.mode != invalid { - check.errorf(x.pos(), "array length %s must be constant", x) - } - goto Error - } - n, ok := x.val.(int64) - if !ok || n < 0 { - check.errorf(x.pos(), "invalid array length %s", x) - goto Error - } - x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)} - } else { - x.typ = &Slice{Elt: check.typ(e.Elt, true)} - } - x.mode = typexpr - - case *ast.StructType: - x.mode = typexpr - x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)} - - case *ast.FuncType: - params, isVariadic := check.collectParams(e.Params, true) - results, _ := check.collectParams(e.Results, false) - x.mode = typexpr - x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} - - case *ast.InterfaceType: - x.mode = typexpr - x.typ = &Interface{Methods: check.collectMethods(e.Methods)} - - case *ast.MapType: - x.mode = typexpr - x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)} - - case *ast.ChanType: - x.mode = typexpr - x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)} - - default: - if debug { - check.dump("expr = %v (%T)", e, e) - } - unreachable() - } - - // everything went well - x.expr = e - return - -Error: - x.mode = invalid - x.expr = e -} - -// exprOrType is like rawExpr but reports an error if e doesn't represents a value or type. -func (check *checker) exprOrType(x *operand, e ast.Expr, iota int, cycleOk bool) { - check.rawExpr(x, e, nil, iota, cycleOk) - if x.mode == novalue { - check.errorf(x.pos(), "%s used as value or type", x) - x.mode = invalid - } -} - -// expr is like rawExpr but reports an error if e doesn't represents a value. -func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) { - check.rawExpr(x, e, hint, iota, false) - switch x.mode { - case novalue: - check.errorf(x.pos(), "%s used as value", x) - x.mode = invalid - case typexpr: - check.errorf(x.pos(), "%s is not an expression", x) - x.mode = invalid - } -} - -func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type { - var x operand - check.rawExpr(&x, e, nil, -1, cycleOk) - 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 constant: - if nilOk && x.isNil() { - return nil - } - fallthrough - default: - check.errorf(x.pos(), "%s is not a type", &x) - } - return Typ[Invalid] -} - -// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil. -// It returns e's type, nil, or Typ[Invalid] if an error occurred. -// -func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type { - return check.rawTyp(e, cycleOk, true) -} - -// typ is like rawExpr but reports an error if e doesn't represents a type. -// It returns e's type, or Typ[Invalid] if an error occurred. -// -func (check *checker) typ(e ast.Expr, cycleOk bool) Type { - return check.rawTyp(e, cycleOk, false) -} diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go deleted file mode 100644 index 7f968eb8d0..0000000000 --- a/src/pkg/go/types/gcimporter.go +++ /dev/null @@ -1,950 +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 an Importer for gc-generated object files. - -package types - -import ( - "bufio" - "errors" - "fmt" - "go/ast" - "go/build" - "go/token" - "io" - "math/big" - "os" - "path/filepath" - "strconv" - "strings" - "text/scanner" -) - -var pkgExts = [...]string{".a", ".5", ".6", ".8"} - -// 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 -} - -// GcImportData imports a package by reading the gc-generated export data, -// adds the corresponding package object to the imports map indexed by id, -// and returns the object. -// -// The imports 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 imports[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 GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) { - // support for gcParser error handling - defer func() { - if r := recover(); r != nil { - err = r.(importError) // will re-panic if r is not an importError - } - }() - - var p gcParser - p.init(filename, id, data, imports) - pkg = p.parseExport() - - return -} - -// GcImport imports a gc-generated package given its import path, adds the -// corresponding package object to the imports map, and returns the object. -// Local import paths are interpreted relative to the current working directory. -// The imports map must contains all packages already imported. -// GcImport satisfies the ast.Importer signature. -// -func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) { - if path == "unsafe" { - return Unsafe, nil - } - - srcDir := "." - if build.IsLocalImport(path) { - srcDir, err = os.Getwd() - if err != nil { - return - } - } - - filename, id := FindPkg(path, srcDir) - if filename == "" { - err = errors.New("can't find import: " + id) - return - } - - // no need to re-import if the package was imported completely before - if pkg = imports[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 = FindGcExportData(buf); err != nil { - return - } - - pkg, err = GcImportData(imports, filename, id, buf) - - return -} - -// ---------------------------------------------------------------------------- -// gcParser - -// gcParser parses the exports inside a gc compiler-produced -// object/archive file and populates its scope with the results. -type gcParser 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 - imports map[string]*Package // package id -> package object -} - -func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*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.imports = imports - // leave for debugging - if false { - // check consistency of imports map - for _, pkg := range imports { - if pkg.Name == "" { - fmt.Printf("no package name for %s\n", pkg.Path) - } - } - } -} - -func (p *gcParser) 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 = "" - } - // leave for debugging - if false { - fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) - } -} - -func declConst(pkg *Package, name string) *Const { - // the constant may have been imported before - if it exists - // already in the respective scope, return that constant - scope := pkg.Scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*Const) - } - // otherwise create a new constant and insert it into the scope - obj := &Const{Pkg: pkg, Name: name} - scope.Insert(obj) - return obj -} - -func declTypeName(pkg *Package, name string) *TypeName { - scope := pkg.Scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*TypeName) - } - obj := &TypeName{Pkg: pkg, Name: name} - // a named type may be referred to before the underlying type - // is known - set it up - obj.Type = &NamedType{Obj: obj} - scope.Insert(obj) - return obj -} - -func declVar(pkg *Package, name string) *Var { - scope := pkg.Scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*Var) - } - obj := &Var{Pkg: pkg, Name: name} - scope.Insert(obj) - return obj -} - -func declFunc(pkg *Package, name string) *Func { - scope := pkg.Scope - if obj := scope.Lookup(name); obj != nil { - return obj.(*Func) - } - obj := &Func{Pkg: pkg, Name: name} - 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 *gcParser) 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 *gcParser) errorf(format string, args ...interface{}) { - p.error(fmt.Sprintf(format, args...)) -} - -func (p *gcParser) 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 *gcParser) 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 *gcParser) 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 *gcParser) 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 *gcParser) parsePackageName() string { - return p.expect(scanner.Ident) -} - -// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . -func (p *gcParser) 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 *gcParser) parseQualifiedName() (id, name string) { - p.expect('@') - id = p.parsePackageId() - p.expect('.') - 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.imports map. -// -func (p *gcParser) getPkg(id, name string) *Package { - // package unsafe is not in the imports map - handle explicitly - if id == "unsafe" { - return Unsafe - } - pkg := p.imports[id] - if pkg == nil && name != "" { - pkg = &Package{Name: name, Path: id, Scope: new(Scope)} - p.imports[id] = pkg - } - return pkg -} - -// parseExportedName is like parseQualifiedName, but -// the package id is resolved to an imported *Package. -// -func (p *gcParser) parseExportedName() (pkg *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 *gcParser) parseBasicType() Type { - id := p.expect(scanner.Ident) - obj := Universe.Lookup(id) - if obj, ok := obj.(*TypeName); ok { - return obj.Type - } - p.errorf("not a basic type: %s", id) - return nil -} - -// ArrayType = "[" int_lit "]" Type . -// -func (p *gcParser) parseArrayType() Type { - // "[" already consumed and lookahead known not to be "]" - lit := p.expect(scanner.Int) - p.expect(']') - elt := p.parseType() - n, err := strconv.ParseInt(lit, 10, 64) - if err != nil { - p.error(err) - } - return &Array{Len: n, Elt: elt} -} - -// MapType = "map" "[" Type "]" Type . -// -func (p *gcParser) parseMapType() Type { - p.expectKeyword("map") - p.expect('[') - key := p.parseType() - p.expect(']') - elt := p.parseType() - return &Map{Key: key, Elt: elt} -} - -// Name = identifier | "?" | QualifiedName . -// -// If materializePkg is set, a package is returned for fully qualified names. -// That 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 -// for QualifiedNames. Fake packages are created when the package id is not -// found in the p.imports map; we cannot create a real package in that case -// because we don't have a package name. -// -// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to -// simplify this code. -// -func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { - switch p.tok { - case scanner.Ident: - name = p.lit - p.next() - case '?': - // anonymous - 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 = &Package{Path: id} - } - } - default: - p.error("name expected") - } - return -} - -// Field = Name Type [ string_lit ] . -// -func (p *gcParser) parseField() *Field { - var f Field - f.Pkg, f.Name = p.parseName(true) - f.Type = p.parseType() - if p.tok == scanner.String { - f.Tag = p.expect(scanner.String) - } - if f.Name == "" { - // anonymous field - typ must be T or *T and T must be a type name - if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil { - f.Name = typ.Obj.GetName() - f.IsAnonymous = true - } else { - p.errorf("anonymous field expected") - } - } - return &f -} - -// StructType = "struct" "{" [ FieldList ] "}" . -// FieldList = Field { ";" Field } . -// -func (p *gcParser) parseStructType() Type { - var fields []*Field - - p.expectKeyword("struct") - p.expect('{') - for p.tok != '}' { - if len(fields) > 0 { - p.expect(';') - } - fields = append(fields, p.parseField()) - } - p.expect('}') - - return &Struct{Fields: fields} -} - -// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . -// -func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { - _, name := p.parseName(false) - if name == "" { - name = "_" // cannot access unnamed identifiers - } - if p.tok == '.' { - p.expectSpecial("...") - isVariadic = true - } - typ := p.parseType() - // ignore argument tag (e.g. "noescape") - if p.tok == scanner.String { - p.next() - } - par = &Var{Name: name, Type: typ} // Pkg == nil - return -} - -// Parameters = "(" [ ParameterList ] ")" . -// ParameterList = { Parameter "," } Parameter . -// -func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) { - p.expect('(') - for p.tok != ')' { - 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 *gcParser) parseSignature() *Signature { - params, isVariadic := p.parseParameters() - - // optional result type - var results []*Var - if p.tok == '(' { - var variadic bool - results, variadic = p.parseParameters() - if variadic { - p.error("... not permitted on result type") - } - } - - return &Signature{Params: params, Results: results, IsVariadic: 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 *gcParser) parseInterfaceType() Type { - var methods []*Method - - p.expectKeyword("interface") - p.expect('{') - for p.tok != '}' { - if len(methods) > 0 { - p.expect(';') - } - pkg, name := p.parseName(true) - typ := p.parseSignature() - methods = append(methods, &Method{QualifiedName{pkg, name}, typ}) - } - p.expect('}') - - return &Interface{Methods: methods} -} - -// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . -// -func (p *gcParser) parseChanType() Type { - dir := ast.SEND | ast.RECV - if p.tok == scanner.Ident { - p.expectKeyword("chan") - if p.tok == '<' { - p.expectSpecial("<-") - dir = ast.SEND - } - } else { - p.expectSpecial("<-") - p.expectKeyword("chan") - dir = ast.RECV - } - elt := p.parseType() - return &Chan{Dir: dir, Elt: elt} -} - -// 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 *gcParser) parseType() 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() - 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 &Slice{Elt: p.parseType()} - } - return p.parseArrayType() - case '*': - // PointerType - p.next() - return &Pointer{Base: 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 *gcParser) parseImportDecl() { - p.expectKeyword("import") - name := p.parsePackageName() - p.getPkg(p.parsePackageId(), name) -} - -// int_lit = [ "+" | "-" ] { "0" ... "9" } . -// -func (p *gcParser) parseInt() (neg bool, val string) { - switch p.tok { - case '-': - neg = true - fallthrough - case '+': - p.next() - } - val = p.expect(scanner.Int) - return -} - -// number = int_lit [ "p" int_lit ] . -// -func (p *gcParser) parseNumber() (x operand) { - x.mode = constant - - // mantissa - neg, val := p.parseInt() - mant, ok := new(big.Int).SetString(val, 0) - assert(ok) - if neg { - mant.Neg(mant) - } - - if p.lit == "p" { - // exponent (base 2) - p.next() - neg, val = p.parseInt() - exp64, err := strconv.ParseUint(val, 10, 0) - if err != nil { - p.error(err) - } - exp := uint(exp64) - if neg { - denom := big.NewInt(1) - denom.Lsh(denom, exp) - x.typ = Typ[UntypedFloat] - x.val = normalizeRatConst(new(big.Rat).SetFrac(mant, denom)) - return - } - if exp > 0 { - mant.Lsh(mant, exp) - } - x.typ = Typ[UntypedFloat] - x.val = normalizeIntConst(mant) - return - } - - x.typ = Typ[UntypedInt] - x.val = normalizeIntConst(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 *gcParser) parseConstDecl() { - p.expectKeyword("const") - pkg, name := p.parseExportedName() - obj := declConst(pkg, name) - var x operand - if p.tok != '=' { - obj.Type = p.parseType() - } - p.expect('=') - switch p.tok { - case scanner.Ident: - // bool_lit - if p.lit != "true" && p.lit != "false" { - p.error("expected true or false") - } - x.typ = Typ[UntypedBool] - x.val = p.lit == "true" - p.next() - - case '-', scanner.Int: - // int_lit - x = p.parseNumber() - - case '(': - // complex_lit or rune_lit - p.next() - if p.tok == scanner.Char { - p.next() - p.expect('+') - x = p.parseNumber() - x.typ = Typ[UntypedRune] - p.expect(')') - break - } - re := p.parseNumber() - p.expect('+') - im := p.parseNumber() - p.expectKeyword("i") - p.expect(')') - x.typ = Typ[UntypedComplex] - // TODO(gri) fix this - _, _ = re, im - x.val = zeroConst - - case scanner.Char: - // rune_lit - x.setConst(token.CHAR, p.lit) - p.next() - - case scanner.String: - // string_lit - x.setConst(token.STRING, p.lit) - p.next() - - default: - p.errorf("expected literal got %s", scanner.TokenString(p.tok)) - } - if obj.Type == nil { - obj.Type = x.typ - } - assert(x.val != nil) - obj.Val = x.val -} - -// TypeDecl = "type" ExportedName Type . -// -func (p *gcParser) 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.(*NamedType); name.Underlying == nil { - name.Underlying = typ - } -} - -// VarDecl = "var" ExportedName Type . -// -func (p *gcParser) parseVarDecl() { - p.expectKeyword("var") - pkg, name := p.parseExportedName() - obj := declVar(pkg, name) - obj.Type = p.parseType() -} - -// Func = Signature [ Body ] . -// Body = "{" ... "}" . -// -func (p *gcParser) parseFunc() *Signature { - sig := p.parseSignature() - 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 *gcParser) parseMethodDecl() { - // "func" already consumed - p.expect('(') - recv, _ := p.parseParameter() // receiver - p.expect(')') - - // determine receiver base type object - typ := recv.Type - if ptr, ok := typ.(*Pointer); ok { - typ = ptr.Base - } - base := typ.(*NamedType) - - // parse method name, signature, and possibly inlined body - pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package. - sig := p.parseFunc() - sig.Recv = recv - - // add method to type unless type was imported before - // and method exists already - // TODO(gri) investigate if this can be avoided - for _, m := range base.Methods { - if m.Name == name { - return // method was added before - } - } - base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig}) -} - -// FuncDecl = "func" ExportedName Func . -// -func (p *gcParser) parseFuncDecl() { - // "func" already consumed - pkg, name := p.parseExportedName() - typ := p.parseFunc() - declFunc(pkg, name).Type = typ -} - -// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . -// -func (p *gcParser) parseDecl() { - 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 *gcParser) parseExport() *Package { - p.expectKeyword("package") - name := p.parsePackageName() - if p.tok != '\n' { - // A package is safe if it was compiled with the -u flag, - // which disables the unsafe package. - // TODO(gri) remember "safe" package - p.expectKeyword("safe") - } - 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) - } - - // package was imported completely and without errors - pkg.Complete = true - - return pkg -} diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go deleted file mode 100644 index b793eb4cb3..0000000000 --- a/src/pkg/go/types/gcimporter_test.go +++ /dev/null @@ -1,180 +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 - -import ( - "go/ast" - "go/build" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "testing" - "time" -) - -var gcPath string // Go compiler path - -func init() { - // determine compiler - var gc string - switch runtime.GOARCH { - case "386": - gc = "8g" - case "amd64": - gc = "6g" - case "arm": - gc = "5g" - default: - gcPath = "unknown-GOARCH-compiler" - return - } - gcPath = filepath.Join(build.ToolDir, gc) -} - -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]*Package) - -func testPath(t *testing.T, path string) bool { - t0 := time.Now() - _, err := GcImport(imports, path) - if err != nil { - t.Errorf("testPath(%s): %s", path, err) - return false - } - t.Logf("testPath(%s): %v", path, time.Since(t0)) - return true -} - -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)) { - nimports++ - } - } - } - case f.IsDir(): - nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) - } - } - return -} - -func TestGcImport(t *testing.T) { - // 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 testPath(t, "./testdata/exports") { - nimports++ - } - nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages - t.Logf("tested %d imports", nimports) -} - -var importedObjectTests = []struct { - name string - kind ast.ObjKind - typ string -}{ - {"unsafe.Pointer", ast.Typ, "Pointer"}, - {"math.Pi", ast.Con, "untyped float"}, - {"io.Reader", ast.Typ, "interface{Read(p []byte) (n int, err error)}"}, - {"io.ReadWriter", ast.Typ, "interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"}, - {"math.Sin", ast.Fun, "func(x·2 float64) (_ float64)"}, - // TODO(gri) add more tests -} - -func TestGcImportedTypes(t *testing.T) { - // This package does not yet know how to read gccgo export data. - if runtime.Compiler == "gccgo" { - 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 := GcImport(imports, importPath) - if err != nil { - t.Error(err) - continue - } - - obj := pkg.Scope.Lookup(objName) - - // TODO(gri) should define an accessor on Object - var kind ast.ObjKind - var typ Type - switch obj := obj.(type) { - case *Const: - kind = ast.Con - typ = obj.Type - case *TypeName: - kind = ast.Typ - typ = obj.Type - case *Var: - kind = ast.Var - typ = obj.Type - case *Func: - kind = ast.Fun - typ = obj.Type - default: - unreachable() - } - - if kind != test.kind { - t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind) - } - - str := typeString(underlying(typ)) - if str != test.typ { - t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ) - } - } -} diff --git a/src/pkg/go/types/objects.go b/src/pkg/go/types/objects.go deleted file mode 100644 index 73301c6ca4..0000000000 --- a/src/pkg/go/types/objects.go +++ /dev/null @@ -1,187 +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" -) - -// 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 { - GetPkg() *Package - GetName() string - GetType() Type - GetPos() token.Pos - - anObject() -} - -// A Package represents the contents (objects) of a Go package. -type Package struct { - Name string - Path string // import path, "" for current (non-imported) package - Scope *Scope // package-level scope - Imports map[string]*Package // map of import paths to imported packages - Complete bool // if set, this package was imported completely - - spec *ast.ImportSpec -} - -// A Const represents a declared constant. -type Const struct { - Pkg *Package - Name string - Type Type - Val interface{} // nil means unknown constant value due to type error - - visited bool // for initialization cycle detection - spec *ast.ValueSpec -} - -// A TypeName represents a declared type. -type TypeName struct { - Pkg *Package - Name string - Type Type // *NamedType or *Basic - - spec *ast.TypeSpec -} - -// A Variable represents a declared variable (including function parameters and results). -type Var struct { - Pkg *Package // nil for parameters - Name string - Type Type - - visited bool // for initialization cycle detection - decl interface{} -} - -// A Func represents a declared function. -type Func struct { - Pkg *Package - Name string - Type Type // *Signature or *Builtin - - decl *ast.FuncDecl -} - -func (obj *Package) GetPkg() *Package { return obj } -func (obj *Const) GetPkg() *Package { return obj.Pkg } -func (obj *TypeName) GetPkg() *Package { return obj.Pkg } -func (obj *Var) GetPkg() *Package { return obj.Pkg } -func (obj *Func) GetPkg() *Package { return obj.Pkg } - -func (obj *Package) GetName() string { return obj.Name } -func (obj *Const) GetName() string { return obj.Name } -func (obj *TypeName) GetName() string { return obj.Name } -func (obj *Var) GetName() string { return obj.Name } -func (obj *Func) GetName() string { return obj.Name } - -func (obj *Package) GetType() Type { return Typ[Invalid] } -func (obj *Const) GetType() Type { return obj.Type } -func (obj *TypeName) GetType() Type { return obj.Type } -func (obj *Var) GetType() Type { return obj.Type } -func (obj *Func) GetType() Type { return obj.Type } - -func (obj *Package) GetPos() token.Pos { - if obj.spec != nil { - return obj.spec.Pos() - } - return token.NoPos -} - -func (obj *Const) GetPos() token.Pos { - for _, n := range obj.spec.Names { - if n.Name == obj.Name { - return n.Pos() - } - } - return token.NoPos -} -func (obj *TypeName) GetPos() token.Pos { - if obj.spec != nil { - return obj.spec.Pos() - } - return token.NoPos -} - -func (obj *Var) GetPos() token.Pos { - switch d := obj.decl.(type) { - case *ast.Field: - for _, n := range d.Names { - if n.Name == obj.Name { - return n.Pos() - } - } - case *ast.ValueSpec: - for _, n := range d.Names { - if n.Name == obj.Name { - return n.Pos() - } - } - case *ast.AssignStmt: - for _, x := range d.Lhs { - if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name { - return ident.Pos() - } - } - } - return token.NoPos -} -func (obj *Func) GetPos() token.Pos { - if obj.decl != nil && obj.decl.Name != nil { - return obj.decl.Name.Pos() - } - return token.NoPos -} - -func (*Package) anObject() {} -func (*Const) anObject() {} -func (*TypeName) anObject() {} -func (*Var) anObject() {} -func (*Func) anObject() {} - -// newObj returns a new Object for a given *ast.Object. -// It does not canonicalize them (it always returns a new one). -// For canonicalization, see check.lookup. -// -// TODO(gri) Once we do identifier resolution completely in -// in the typechecker, this functionality can go. -// -func newObj(pkg *Package, astObj *ast.Object) Object { - assert(pkg != nil) - name := astObj.Name - typ, _ := astObj.Type.(Type) - switch astObj.Kind { - case ast.Bad: - // ignore - case ast.Pkg: - unreachable() - case ast.Con: - return &Const{Pkg: pkg, Name: name, Type: typ, Val: astObj.Data, spec: astObj.Decl.(*ast.ValueSpec)} - case ast.Typ: - return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)} - case ast.Var: - switch astObj.Decl.(type) { - case *ast.Field: // function parameters - case *ast.ValueSpec: // proper variable declarations - case *ast.AssignStmt: // short variable declarations - default: - unreachable() // everything else is not ok - } - return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl} - case ast.Fun: - return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)} - case ast.Lbl: - unreachable() // for now - } - unreachable() - return nil -} diff --git a/src/pkg/go/types/operand.go b/src/pkg/go/types/operand.go deleted file mode 100644 index 6f6e058a85..0000000000 --- a/src/pkg/go/types/operand.go +++ /dev/null @@ -1,411 +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" - "fmt" - "go/ast" - "go/token" -) - -// An operandMode specifies the (addressing) mode of an operand. -type operandMode int - -const ( - invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore - novalue // operand represents no value (result of a function call w/o result) - typexpr // operand is a type - constant // operand is a constant; the operand's typ is a Basic type - variable // operand is an addressable variable - value // operand is a computed value - valueok // like mode == value, but operand may be used in a comma,ok expression -) - -var operandModeString = [...]string{ - invalid: "invalid", - novalue: "no value", - typexpr: "type", - constant: "constant", - variable: "variable", - value: "value", - valueok: "value,ok", -} - -// An operand represents an intermediate value during type checking. -// Operands have an (addressing) mode, the expression evaluating to -// the operand, the operand's type, and for constants a constant value. -// -type operand struct { - mode operandMode - expr ast.Expr - typ Type - val interface{} -} - -// 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() -} - -func (x *operand) String() string { - if x.mode == invalid { - return "invalid operand" - } - var buf bytes.Buffer - if x.expr != nil { - buf.WriteString(exprString(x.expr)) - buf.WriteString(" (") - } - buf.WriteString(operandModeString[x.mode]) - if x.mode == constant { - format := " %v" - if isString(x.typ) { - format = " %q" - } - fmt.Fprintf(&buf, format, x.val) - } - if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) { - fmt.Fprintf(&buf, " of type %s", typeString(x.typ)) - } - if x.expr != nil { - buf.WriteByte(')') - } - return buf.String() -} - -// setConst sets x to the untyped constant for literal lit. -func (x *operand) setConst(tok token.Token, lit string) { - x.mode = invalid - - var kind BasicKind - var val interface{} - switch tok { - case token.INT: - kind = UntypedInt - val = makeIntConst(lit) - - case token.FLOAT: - kind = UntypedFloat - val = makeFloatConst(lit) - - case token.IMAG: - kind = UntypedComplex - val = makeComplexConst(lit) - - case token.CHAR: - kind = UntypedRune - val = makeRuneConst(lit) - - case token.STRING: - kind = UntypedString - val = makeStringConst(lit) - } - - if val != nil { - x.mode = constant - x.typ = Typ[kind] - x.val = val - } -} - -// isNil reports whether x is the predeclared nil constant. -func (x *operand) isNil() bool { - return x.mode == constant && x.val == nilConst -} - -// TODO(gri) The functions operand.isAssignable, checker.convertUntyped, -// checker.isRepresentable, and checker.assignOperand are -// overlapping in functionality. Need to simplify and clean up. - -// isAssignable reports whether x is assignable to a variable of type T. -func (x *operand) isAssignable(ctxt *Context, 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 IsIdentical(V, T) { - return true - } - - Vu := underlying(V) - Tu := underlying(T) - - // x's type V and T have identical underlying types - // and at least one of V or T is not a named type - if IsIdentical(Vu, Tu) { - return !isNamed(V) || !isNamed(T) - } - - // T is an interface type and x implements T - if Ti, ok := Tu.(*Interface); ok { - if m, _ := missingMethod(x.typ, Ti); m == nil { - 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 == ast.SEND|ast.RECV { - if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) { - 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.isRepresentable. Need to clean up. - if isUntyped(Vu) { - switch t := Tu.(type) { - case *Basic: - if x.mode == constant { - return isRepresentableConst(x.val, ctxt, t.Kind) - } - // The result of a comparison is an untyped boolean, - // but may not be a constant. - if Vb, _ := Vu.(*Basic); Vb != nil { - return Vb.Kind == UntypedBool && isBoolean(Tu) - } - case *Interface: - return x.isNil() || len(t.Methods) == 0 - case *Pointer, *Signature, *Slice, *Map, *Chan: - return x.isNil() - } - } - - return false -} - -// isInteger reports whether x is a (typed or untyped) integer value. -func (x *operand) isInteger() bool { - return x.mode == invalid || - isInteger(x.typ) || - x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no context required for UntypedInt -} - -// lookupResult represents the result of a struct field/method lookup. -type lookupResult struct { - mode operandMode - typ Type - index []int // field index sequence; nil for methods -} - -type embeddedType struct { - typ *NamedType - index []int // field index sequence - multiples bool // if set, typ is embedded multiple times at the same level -} - -// lookupFieldBreadthFirst searches all types in list for a single entry (field -// or method) of the given name from the given package. If such a field is found, -// the result describes the field mode and type; otherwise the result mode is invalid. -// (This function is similar in structure to FieldByNameFunc in reflect/type.go) -// -func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) { - // visited records the types that have been searched already. - visited := make(map[*NamedType]bool) - - // embedded types of the next lower level - var next []embeddedType - - // potentialMatch is invoked every time a match is found. - potentialMatch := func(multiples bool, mode operandMode, typ Type) bool { - if multiples || res.mode != invalid { - // name appeared already at this level - annihilate - res.mode = invalid - return false - } - // first appearance of name - res.mode = mode - res.typ = typ - res.index = nil - return true - } - - // Search the current level if there is any work to do and collect - // embedded types of the next lower level in the next list. - for len(list) > 0 { - // The res.mode indicates whether we have found a match already - // on this level (mode != invalid), or not (mode == invalid). - assert(res.mode == invalid) - - // start with empty next list (don't waste underlying array) - next = next[:0] - - // look for name in all types at this level - for _, e := range list { - typ := e.typ - if visited[typ] { - continue - } - visited[typ] = true - - // look for a matching attached method - for _, m := range typ.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - if !potentialMatch(e.multiples, value, m.Type) { - return // name collision - } - } - } - - switch t := typ.Underlying.(type) { - case *Struct: - // look for a matching field and collect embedded types - for i, f := range t.Fields { - if name.IsSame(f.QualifiedName) { - assert(f.Type != nil) - if !potentialMatch(e.multiples, variable, f.Type) { - return // name collision - } - var index []int - index = append(index, e.index...) // copy e.index - index = append(index, i) - res.index = index - continue - } - // Collect embedded struct fields for searching the next - // lower level, 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 level; 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 typ appeared multiple times at - // this level, f.Type appears multiple times at the next - // level. - if f.IsAnonymous && res.mode == invalid { - // Ignore embedded basic types - only user-defined - // named types can have methods or have struct fields. - if t, _ := deref(f.Type).(*NamedType); t != nil { - var index []int - index = append(index, e.index...) // copy e.index - index = append(index, i) - next = append(next, embeddedType{t, index, e.multiples}) - } - } - } - - case *Interface: - // look for a matching method - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - if !potentialMatch(e.multiples, value, m.Type) { - return // name collision - } - } - } - } - } - - if res.mode != invalid { - // we found a single match on this level - return - } - - // No match and no collision so far. - // Compute the list to search for the next level. - list = list[:0] // don't waste underlying array - for _, e := range next { - // Instead of adding the same type multiple times, look for - // it in the list and mark it as multiple if it was added - // before. - // We use a sequential search (instead of a map for next) - // because the lists tend to be small, can easily be reused, - // and explicit search appears to be faster in this case. - if alt := findType(list, e.typ); alt != nil { - alt.multiples = true - } else { - list = append(list, e) - } - } - - } - - return -} - -func findType(list []embeddedType, typ *NamedType) *embeddedType { - for i := range list { - if p := &list[i]; p.typ == typ { - return p - } - } - return nil -} - -func lookupField(typ Type, name QualifiedName) lookupResult { - typ = deref(typ) - - if t, ok := typ.(*NamedType); ok { - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - assert(m.Type != nil) - return lookupResult{value, m.Type, nil} - } - } - typ = t.Underlying - } - - switch t := typ.(type) { - case *Struct: - var next []embeddedType - for i, f := range t.Fields { - if name.IsSame(f.QualifiedName) { - return lookupResult{variable, f.Type, []int{i}} - } - if f.IsAnonymous { - // Possible optimization: If the embedded type - // is a pointer to the current type we could - // ignore it. - // Ignore embedded basic types - only user-defined - // named types can have methods or have struct fields. - if t, _ := deref(f.Type).(*NamedType); t != nil { - next = append(next, embeddedType{t, []int{i}, false}) - } - } - } - if len(next) > 0 { - return lookupFieldBreadthFirst(next, name) - } - - case *Interface: - for _, m := range t.Methods { - if name.IsSame(m.QualifiedName) { - return lookupResult{value, m.Type, nil} - } - } - } - - // not found - return lookupResult{mode: invalid} -} diff --git a/src/pkg/go/types/predicates.go b/src/pkg/go/types/predicates.go deleted file mode 100644 index a99c91a4ef..0000000000 --- a/src/pkg/go/types/predicates.go +++ /dev/null @@ -1,303 +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 - -func isNamed(typ Type) bool { - if _, ok := typ.(*Basic); ok { - return ok - } - _, ok := typ.(*NamedType) - return ok -} - -func isBoolean(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsBoolean != 0 -} - -func isInteger(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsInteger != 0 -} - -func isUnsigned(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsUnsigned != 0 -} - -func isFloat(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsFloat != 0 -} - -func isComplex(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsComplex != 0 -} - -func isNumeric(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsNumeric != 0 -} - -func isString(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsString != 0 -} - -func isUntyped(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsUntyped != 0 -} - -func isOrdered(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsOrdered != 0 -} - -func isConstType(typ Type) bool { - t, ok := underlying(typ).(*Basic) - return ok && t.Info&IsConstType != 0 -} - -func isComparable(typ Type) bool { - switch t := underlying(typ).(type) { - case *Basic: - return t.Kind != Invalid && t.Kind != UntypedNil - case *Pointer, *Interface, *Chan: - // assumes types are equal for pointers and channels - return true - case *Struct: - for _, f := range t.Fields { - if !isComparable(f.Type) { - return false - } - } - return true - case *Array: - return isComparable(t.Elt) - } - return false -} - -func hasNil(typ Type) bool { - switch underlying(typ).(type) { - case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: - return true - } - return false -} - -// IsIdentical returns true if x and y are identical. -func IsIdentical(x, y Type) 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 && IsIdentical(x.Elt, y.Elt) - } - - case *Slice: - // Two slice types are identical if they have identical element types. - if y, ok := y.(*Slice); ok { - return IsIdentical(x.Elt, y.Elt) - } - - 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 len(x.Fields) == len(y.Fields) { - for i, f := range x.Fields { - g := y.Fields[i] - if !f.QualifiedName.IsSame(g.QualifiedName) || - !IsIdentical(f.Type, g.Type) || - f.Tag != g.Tag || - f.IsAnonymous != g.IsAnonymous { - return false - } - } - return true - } - } - - case *Pointer: - // Two pointer types are identical if they have identical base types. - if y, ok := y.(*Pointer); ok { - return IsIdentical(x.Base, y.Base) - } - - 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 identicalTypes(x.Params, y.Params) && - identicalTypes(x.Results, y.Results) && - x.IsVariadic == y.IsVariadic - } - - 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 { - return identicalMethods(x.Methods, y.Methods) // methods are sorted - } - - case *Map: - // Two map types are identical if they have identical key and value types. - if y, ok := y.(*Map); ok { - return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt) - } - - case *Chan: - // Two channel types are identical if they have identical value types - // and the same direction. - if y, ok := y.(*Chan); ok { - return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt) - } - - case *NamedType: - // Two named types are identical if their type names originate - // in the same type declaration. - if y, ok := y.(*NamedType); ok { - return x.Obj == y.Obj - } - } - - return false -} - -// identicalTypes returns true if both lists a and b have the -// same length and corresponding objects have identical types. -func identicalTypes(a, b []*Var) bool { - if len(a) != len(b) { - return false - } - for i, x := range a { - y := b[i] - if !IsIdentical(x.Type, y.Type) { - return false - } - } - return true -} - -// identicalMethods returns true if both lists a and b have the -// same length and corresponding methods have identical types. -// TODO(gri) make this more efficient -func identicalMethods(a, b []*Method) bool { - if len(a) != len(b) { - return false - } - m := make(map[QualifiedName]*Method) - for _, x := range a { - assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries - m[x.QualifiedName] = x - } - for _, y := range b { - if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) { - return false - } - } - return true -} - -// underlying returns the underlying type of typ. -func underlying(typ Type) Type { - // Basic types are representing themselves directly even though they are named. - if typ, ok := typ.(*NamedType); ok { - return typ.Underlying // underlying types are never NamedTypes - } - return typ -} - -// deref returns a pointer's base type; otherwise it returns typ. -func deref(typ Type) Type { - if typ, ok := underlying(typ).(*Pointer); ok { - return typ.Base - } - return typ -} - -// defaultType returns the default "typed" type for an "untyped" type; -// it returns the incoming type for all other types. If there is no -// corresponding untyped type, the result is Typ[Invalid]. -// -func defaultType(typ Type) Type { - if t, ok := typ.(*Basic); ok { - k := Invalid - switch t.Kind { - // case UntypedNil: - // There is no default type for nil. For a good error message, - // catch this case before calling this function. - case UntypedBool: - k = Bool - case UntypedInt: - k = Int - case UntypedRune: - k = Rune - case UntypedFloat: - k = Float64 - case UntypedComplex: - k = Complex128 - case UntypedString: - k = String - } - typ = Typ[k] - } - return typ -} - -// missingMethod returns (nil, false) if typ implements T, otherwise -// it returns the first missing method required by T and whether it -// is missing or simply has the wrong type. -// -func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) { - // TODO(gri): this needs to correctly compare method names (taking package into account) - // TODO(gri): distinguish pointer and non-pointer receivers - // an interface type implements T if it has no methods with conflicting signatures - // Note: This is stronger than the current spec. Should the spec require this? - if ityp, _ := underlying(typ).(*Interface); ityp != nil { - for _, m := range T.Methods { - res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField - if res.mode != invalid && !IsIdentical(res.typ, m.Type) { - return m, true - } - } - return - } - - // a concrete type implements T if it implements all methods of T. - for _, m := range T.Methods { - res := lookupField(typ, m.QualifiedName) - if res.mode == invalid { - return m, false - } - if !IsIdentical(res.typ, m.Type) { - return m, true - } - } - return -} diff --git a/src/pkg/go/types/resolve.go b/src/pkg/go/types/resolve.go deleted file mode 100644 index 43db60708f..0000000000 --- a/src/pkg/go/types/resolve.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. - -package types - -import ( - "fmt" - "go/ast" - "go/token" - "strconv" -) - -func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport token.Pos) { - alt := scope.Insert(obj) - if alt == nil && altScope != nil { - // see if there is a conflicting declaration in altScope - alt = altScope.Lookup(obj.GetName()) - } - if alt != nil { - prevDecl := "" - - // for dot-imports, local declarations are declared first - swap messages - if dotImport.IsValid() { - if pos := alt.GetPos(); pos.IsValid() { - check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s", - obj.GetName(), check.fset.Position(dotImport))) - return - } - - // get by w/o other position - check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName())) - return - } - - if pos := alt.GetPos(); pos.IsValid() { - prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos)) - } - check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl)) - } -} - -func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool { - for ; scope != nil; scope = scope.Outer { - if obj := scope.Lookup(ident.Name); obj != nil { - check.register(ident, obj) - return true - } - } - return false -} - -func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { - pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)} - check.pkg = pkg - - // complete package scope - i := 0 - for _, file := range check.files { - // package names must match - switch name := file.Name.Name; { - case pkg.Name == "": - pkg.Name = name - case name != pkg.Name: - check.errorf(file.Package, "package %s; expected %s", name, pkg.Name) - continue // ignore this file - } - - // keep this file - check.files[i] = file - i++ - - // the package identifier denotes the current package - check.register(file.Name, pkg) - - // insert top-level file objects in package scope - // (the parser took care of declaration errors) - for _, decl := range file.Decls { - switch d := decl.(type) { - case *ast.BadDecl: - // ignore - case *ast.GenDecl: - if d.Tok == token.CONST { - check.assocInitvals(d) - } - for _, spec := range d.Specs { - switch s := spec.(type) { - case *ast.ImportSpec: - // handled separately below - case *ast.ValueSpec: - for _, name := range s.Names { - if name.Name == "_" { - continue - } - pkg.Scope.Insert(check.lookup(name)) - } - case *ast.TypeSpec: - if s.Name.Name == "_" { - continue - } - pkg.Scope.Insert(check.lookup(s.Name)) - default: - check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) - } - } - case *ast.FuncDecl: - if d.Recv != nil { - // collect method - methods = append(methods, d) - continue - } - if d.Name.Name == "_" || d.Name.Name == "init" { - continue // blank (_) and init functions are inaccessible - } - pkg.Scope.Insert(check.lookup(d.Name)) - default: - check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) - } - } - } - check.files = check.files[0:i] - - // complete file scopes with imports and resolve identifiers - for _, file := range check.files { - // build file scope by processing all imports - importErrors := false - fileScope := &Scope{Outer: pkg.Scope} - for _, spec := range file.Imports { - if importer == nil { - importErrors = true - continue - } - path, _ := strconv.Unquote(spec.Path.Value) - imp, err := importer(pkg.Imports, path) - if err != nil { - check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) - importErrors = true - continue - } - // TODO(gri) If a local package name != "." is provided, - // global identifier resolution could proceed even if the - // import failed. Consider adjusting the logic here a bit. - - // local name overrides imported package name - name := imp.Name - if spec.Name != nil { - name = spec.Name.Name - } - - // add import to file scope - if name == "." { - // merge imported scope with file scope - for _, obj := range imp.Scope.Entries { - // gcimported package scopes contain non-exported - // objects such as types used in partially exported - // objects - do not accept them - if ast.IsExported(obj.GetName()) { - check.declareObj(fileScope, pkg.Scope, obj, spec.Pos()) - } - } - // TODO(gri) consider registering the "." identifier - // if we have Context.Ident callbacks for say blank - // (_) identifiers - // check.register(spec.Name, pkg) - } else if name != "_" { - // declare imported package object in file scope - // (do not re-use imp in the file scope but create - // a new object instead; the Decl field is different - // for different files) - obj := &Package{Name: name, Scope: imp.Scope, spec: spec} - check.declareObj(fileScope, pkg.Scope, obj, token.NoPos) - } - } - - // resolve identifiers - if importErrors { - // don't use the universe scope without correct imports - // (objects in the universe may be shadowed by imports; - // with missing imports, identifiers might get resolved - // incorrectly to universe objects) - pkg.Scope.Outer = nil - } - i := 0 - for _, ident := range file.Unresolved { - if !check.resolveIdent(fileScope, ident) { - check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) - file.Unresolved[i] = ident - i++ - } - - } - file.Unresolved = file.Unresolved[0:i] - pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors) - } - - return -} diff --git a/src/pkg/go/types/resolver_test.go b/src/pkg/go/types/resolver_test.go deleted file mode 100644 index d4e364451d..0000000000 --- a/src/pkg/go/types/resolver_test.go +++ /dev/null @@ -1,167 +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 - -import ( - "go/ast" - "go/parser" - "go/token" - "testing" -) - -var 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" - func f() string { - _ = "foo" - return fmt.Sprintf("%d", g()) - } - func g() (x int) { return } - `, - ` - package p - import . "go/parser" - import "sync" - func g() 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() {} - `, -} - -var pkgnames = []string{ - "fmt", - "math", -} - -func TestResolveQualifiedIdents(t *testing.T) { - // parse package files - fset := token.NewFileSet() - var files []*ast.File - for _, src := range sources { - f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors) - if err != nil { - t.Fatal(err) - } - files = append(files, f) - } - - // resolve and type-check package AST - idents := make(map[*ast.Ident]Object) - var ctxt Context - ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj } - pkg, err := ctxt.Check(fset, files) - if err != nil { - t.Fatal(err) - } - - // check that all packages were imported - for _, name := range pkgnames { - if pkg.Imports[name] == nil { - t.Errorf("package %s not imported", name) - } - } - - // check that there are no top-level unresolved identifiers - for _, f := range files { - for _, x := range f.Unresolved { - t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.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 := idents[x] - if obj == nil { - t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) - return false - } - if _, ok := obj.(*Package); ok && idents[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 - }) - } - - // Currently, the Check API doesn't call Ident for fields, methods, and composite literal keys. - // Introduce them artifically so that we can run the check below. - for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - switch x := n.(type) { - case *ast.StructType: - for _, list := range x.Fields.List { - for _, f := range list.Names { - assert(idents[f] == nil) - idents[f] = &Var{Pkg: pkg, Name: f.Name} - } - } - case *ast.InterfaceType: - for _, list := range x.Methods.List { - for _, f := range list.Names { - assert(idents[f] == nil) - idents[f] = &Func{Pkg: pkg, Name: f.Name} - } - } - case *ast.CompositeLit: - for _, e := range x.Elts { - if kv, ok := e.(*ast.KeyValueExpr); ok { - if k, ok := kv.Key.(*ast.Ident); ok { - assert(idents[k] == nil) - idents[k] = &Var{Pkg: pkg, Name: k.Name} - } - } - } - } - return true - }) - } - - // check that each identifier in the source is enumerated by the Context.Ident callback - for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - if x, ok := n.(*ast.Ident); ok && x.Name != "_" && x.Name != "." { - obj := idents[x] - if obj == nil { - t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name) - } else { - delete(idents, x) - } - return false - } - return true - }) - } - - // TODO(gri) enable code below - // At the moment, the type checker introduces artifical identifiers which are not - // present in the source. Once it doesn't do that anymore, enable the checks below. - /* - for x := range idents { - t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) - } - */ -} diff --git a/src/pkg/go/types/return.go b/src/pkg/go/types/return.go deleted file mode 100644 index 8644d28c91..0000000000 --- a/src/pkg/go/types/return.go +++ /dev/null @@ -1,189 +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 panic() function is terminating - if call, _ := s.X.(*ast.CallExpr); call != nil { - if id, _ := call.Fun.(*ast.Ident); id != nil { - if obj := check.lookup(id); obj != nil { - // TODO(gri) Predeclared functions should be modelled as objects - // rather then ordinary functions that have a predeclared - // function type. This would simplify code here and else- - // where. - if f, _ := obj.(*Func); f != nil && f.Type == predeclaredFunctions[_Panic] { - 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/src/pkg/go/types/scope.go b/src/pkg/go/types/scope.go deleted file mode 100644 index 463ee40c54..0000000000 --- a/src/pkg/go/types/scope.go +++ /dev/null @@ -1,78 +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" -) - -// A Scope maintains the set of named language entities declared -// in the scope and a link to the immediately surrounding (outer) -// scope. -// -type Scope struct { - Outer *Scope - Entries []Object // scope entries in insertion order - large map[string]Object // for fast lookup - only used for larger scopes -} - -// Lookup returns the object with the given name if it is -// found in scope s, otherwise it returns nil. Outer scopes -// are ignored. -// -func (s *Scope) Lookup(name string) Object { - if s.large != nil { - return s.large[name] - } - for _, obj := range s.Entries { - if obj.GetName() == name { - return obj - } - } - return nil -} - -// Insert attempts to insert an object obj into scope s. -// If s already contains an object with the same name, -// Insert leaves s unchanged and returns that object. -// Otherwise it inserts obj and returns nil. -// -func (s *Scope) Insert(obj Object) Object { - name := obj.GetName() - if alt := s.Lookup(name); alt != nil { - return alt - } - s.Entries = append(s.Entries, obj) - - // If the scope size reaches a threshold, use a map for faster lookups. - const threshold = 20 - if len(s.Entries) > threshold { - if s.large == nil { - m := make(map[string]Object, len(s.Entries)) - for _, obj := range s.Entries { - m[obj.GetName()] = obj - } - s.large = m - } - s.large[name] = obj - } - - return nil -} - -// Debugging support -func (s *Scope) String() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "scope %p {", s) - if s != nil && len(s.Entries) > 0 { - fmt.Fprintln(&buf) - for _, obj := range s.Entries { - fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj) - } - } - fmt.Fprintf(&buf, "}\n") - return buf.String() -} diff --git a/src/pkg/go/types/sizes.go b/src/pkg/go/types/sizes.go deleted file mode 100644 index ef6499ba43..0000000000 --- a/src/pkg/go/types/sizes.go +++ /dev/null @@ -1,162 +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 support for (unsafe) Alignof, Offsetof, and Sizeof. - -package types - -func (ctxt *Context) alignof(typ Type) int64 { - if f := ctxt.Alignof; f != nil { - if a := f(typ); a >= 1 { - return a - } - panic("Context.Alignof returned an alignment < 1") - } - return DefaultAlignof(typ) -} - -func (ctxt *Context) offsetsof(s *Struct) []int64 { - offsets := s.offsets - if offsets == nil { - // compute offsets on demand - if f := ctxt.Offsetsof; f != nil { - offsets = f(s.Fields) - // sanity checks - if len(offsets) != len(s.Fields) { - panic("Context.Offsetsof returned the wrong number of offsets") - } - for _, o := range offsets { - if o < 0 { - panic("Context.Offsetsof returned an offset < 0") - } - } - } else { - offsets = DefaultOffsetsof(s.Fields) - } - s.offsets = offsets - } - return offsets -} - -// offsetof returns the offset of the field specified via -// the index sequence relative to typ. It returns a value -// < 0 if the field is in an embedded pointer type. -func (ctxt *Context) offsetof(typ Type, index []int) int64 { - var o int64 - for _, i := range index { - s, _ := underlying(typ).(*Struct) - if s == nil { - return -1 - } - o += ctxt.offsetsof(s)[i] - typ = s.Fields[i].Type - } - return o -} - -func (ctxt *Context) sizeof(typ Type) int64 { - if f := ctxt.Sizeof; f != nil { - if s := f(typ); s >= 0 { - return s - } - panic("Context.Sizeof returned a size < 0") - } - return DefaultSizeof(typ) -} - -// DefaultMaxAlign is the default maximum alignment, in bytes, -// used by DefaultAlignof. -const DefaultMaxAlign = 8 - -// DefaultAlignof implements the default alignment computation -// for unsafe.Alignof. It is used if Context.Alignof == nil. -func DefaultAlignof(typ Type) int64 { - // For arrays and structs, alignment is defined in terms - // of alignment of the elements and fields, respectively. - switch t := underlying(typ).(type) { - case *Array: - // spec: "For a variable x of array type: unsafe.Alignof(x) - // is the same as unsafe.Alignof(x[0]), but at least 1." - return DefaultAlignof(t.Elt) - 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 := DefaultAlignof(f.Type); a > max { - max = a - } - } - return max - } - a := DefaultSizeof(typ) // 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 > DefaultMaxAlign { - return DefaultMaxAlign - } - return a -} - -// 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 -} - -// DefaultOffsetsof implements the default field offset computation -// for unsafe.Offsetof. It is used if Context.Offsetsof == nil. -func DefaultOffsetsof(fields []*Field) []int64 { - offsets := make([]int64, len(fields)) - var o int64 - for i, f := range fields { - a := DefaultAlignof(f.Type) - o = align(o, a) - offsets[i] = o - o += DefaultSizeof(f.Type) - } - return offsets -} - -// DefaultPtrSize is the default size of ints, uint, and pointers, in bytes, -// used by DefaultSizeof. -const DefaultPtrSize = 8 - -// DefaultSizeof implements the default size computation -// for unsafe.Sizeof. It is used if Context.Sizeof == nil. -func DefaultSizeof(typ Type) int64 { - switch t := underlying(typ).(type) { - case *Basic: - if s := t.size; s > 0 { - return s - } - if t.Kind == String { - return DefaultPtrSize * 2 - } - case *Array: - a := DefaultAlignof(t.Elt) - s := DefaultSizeof(t.Elt) - return align(s, a) * t.Len // may be 0 - case *Slice: - return DefaultPtrSize * 3 - case *Struct: - n := len(t.Fields) - if n == 0 { - return 0 - } - offsets := t.offsets - if t.offsets == nil { - // compute offsets on demand - offsets = DefaultOffsetsof(t.Fields) - t.offsets = offsets - } - return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type) - case *Signature: - return DefaultPtrSize * 2 - } - return DefaultPtrSize // catch-all -} diff --git a/src/pkg/go/types/stdlib_test.go b/src/pkg/go/types/stdlib_test.go deleted file mode 100644 index 8b264119d5..0000000000 --- a/src/pkg/go/types/stdlib_test.go +++ /dev/null @@ -1,133 +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. - -package types - -import ( - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/scanner" - "go/token" - "io/ioutil" - "path/filepath" - "runtime" - "testing" - "time" -) - -var verbose = flag.Bool("types.v", false, "verbose mode") - -var ( - pkgCount int // number of packages processed - start = time.Now() -) - -func TestStdlib(t *testing.T) { - walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg")) - if *verbose { - fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) - } -} - -// Package paths of excluded packages. -var excluded = map[string]bool{ - "builtin": true, -} - -// typecheck typechecks the given package files. -func typecheck(t *testing.T, filenames []string) { - fset := token.NewFileSet() - - // parse package files - var files []*ast.File - for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|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 *verbose { - if len(files) == 0 { - fmt.Println("package", file.Name.Name) - } - fmt.Println("\t", filename) - } - - files = append(files, file) - } - - // typecheck package files - ctxt := Context{ - Error: func(err error) { t.Error(err) }, - } - ctxt.Check(fset, files) - pkgCount++ -} - -// pkgfiles returns the list of package files for the given directory. -func pkgfiles(t *testing.T, dir string) []string { - ctxt := build.Default - ctxt.CgoEnabled = false - pkg, err := ctxt.ImportDir(dir, 0) - if err != nil { - if _, nogo := err.(*build.NoGoError); !nogo { - t.Error(err) - } - return nil - } - if excluded[pkg.ImportPath] { - return 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 -} - -// 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 - if files := pkgfiles(t, dir); files != nil { - typecheck(t, 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/src/pkg/go/types/stmt.go b/src/pkg/go/types/stmt.go deleted file mode 100644 index 198cc439a6..0000000000 --- a/src/pkg/go/types/stmt.go +++ /dev/null @@ -1,722 +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 ( - "go/ast" - "go/token" -) - -// assigment reports whether x can be assigned to a variable of type 'to', -// 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. -// TODO(gri) This latter behavior is for historic reasons and complicates -// callers. Needs to be cleaned up. -func (check *checker) assignment(x *operand, to Type) bool { - if x.mode == invalid { - return false - } - - if t, ok := x.typ.(*Result); ok { - // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate) - check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x) - x.mode = invalid - return false - } - - check.convertUntyped(x, to) - - return x.mode != invalid && x.isAssignable(check.ctxt, to) -} - -// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil), or -// lhs = x (if rhs == nil). If decl is set, the lhs expression must be an identifier; -// if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in -// case of an error. -// -func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) { - // Start with rhs so we have an expression type - // for declarations with implicit type. - if x == nil { - x = new(operand) - check.expr(x, rhs, nil, iota) - // don't exit for declarations - we need the lhs first - if x.mode == invalid && !decl { - return - } - } - // x.mode == valid || decl - - // lhs may be an identifier - ident, _ := lhs.(*ast.Ident) - - // regular assignment; we know x is valid - if !decl { - // anything can be assigned to the blank identifier - if ident != nil && ident.Name == "_" { - // the rhs has its final type - check.updateExprType(rhs, x.typ, true) - return - } - - var z operand - check.expr(&z, lhs, nil, -1) - if z.mode == invalid { - return - } - - // TODO(gri) verify that all other z.mode values - // that may appear here are legal - if z.mode == constant || !check.assignment(x, z.typ) { - if x.mode != invalid { - check.errorf(x.pos(), "cannot assign %s to %s", x, &z) - } - } - return - } - - // declaration with initialization; lhs must be an identifier - if ident == nil { - check.errorf(lhs.Pos(), "cannot declare %s", lhs) - return - } - - // Determine typ of lhs: If the object doesn't have a type - // yet, determine it from the type of x; if x is invalid, - // set the object type to Typ[Invalid]. - var typ Type - obj := check.lookup(ident) - switch obj := obj.(type) { - default: - unreachable() - - case nil: - // TODO(gri) is this really unreachable? - unreachable() - - case *Const: - typ = obj.Type // may already be Typ[Invalid] - if typ == nil { - typ = Typ[Invalid] - if x.mode != invalid { - typ = x.typ - } - obj.Type = typ - } - - case *Var: - typ = obj.Type // may already be Typ[Invalid] - if typ == nil { - typ = Typ[Invalid] - if x.mode != invalid { - typ = x.typ - if isUntyped(typ) { - // convert untyped types to default types - if typ == Typ[UntypedNil] { - check.errorf(x.pos(), "use of untyped nil") - typ = Typ[Invalid] - } else { - typ = defaultType(typ) - } - } - } - obj.Type = typ - } - } - - // nothing else to check if we don't have a valid lhs or rhs - if typ == Typ[Invalid] || x.mode == invalid { - return - } - - if !check.assignment(x, typ) { - if x.mode != invalid { - if x.typ != Typ[Invalid] && typ != Typ[Invalid] { - check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", ident.Name, typ, x) - } - } - return - } - - // for constants, set their value - if obj, _ := obj.(*Const); obj != nil { - obj.Val = nil // failure case: we don't know the constant value - if x.mode == constant { - if isConstType(x.typ) { - obj.Val = x.val - } else if x.typ != Typ[Invalid] { - check.errorf(x.pos(), "%s has invalid constant type", x) - } - } else if x.mode != invalid { - check.errorf(x.pos(), "%s is not constant", x) - } - } -} - -// assignNtoM typechecks a general assignment. If decl is set, the lhs expressions -// must be identifiers; if their types are not set, they are deduced from the types -// of the corresponding rhs expressions, or set to Typ[Invalid] in case of an error. -// Precondition: len(lhs) > 0 . -// -func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { - assert(len(lhs) > 0) - - // If the lhs and rhs have corresponding expressions, treat each - // matching pair as an individual pair. - if len(lhs) == len(rhs) { - for i, e := range rhs { - check.assign1to1(lhs[i], e, nil, decl, iota) - } - return - } - - // Otherwise, the rhs must be a single expression (possibly - // a function call returning multiple values, or a comma-ok - // expression). - if len(rhs) == 1 { - // len(lhs) > 1 - // Start with rhs so we have expression types - // for declarations with implicit types. - var x operand - check.expr(&x, rhs[0], nil, iota) - if x.mode == invalid { - goto Error - } - - if t, _ := x.typ.(*Result); t != nil && len(lhs) == len(t.Values) { - // function result - x.mode = value - for i, obj := range t.Values { - x.expr = nil // TODO(gri) should do better here - x.typ = obj.Type - check.assign1to1(lhs[i], nil, &x, decl, iota) - } - return - } - - if x.mode == valueok && len(lhs) == 2 { - // comma-ok expression - x.mode = value - check.assign1to1(lhs[0], nil, &x, decl, iota) - - x.typ = Typ[UntypedBool] - check.assign1to1(lhs[1], nil, &x, decl, iota) - return - } - } - - check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs)) - -Error: - // In case of a declaration, set all lhs types to Typ[Invalid]. - if decl { - for _, e := range lhs { - ident, _ := e.(*ast.Ident) - if ident == nil { - check.errorf(e.Pos(), "cannot declare %s", e) - continue - } - switch obj := check.lookup(ident).(type) { - case *Const: - obj.Type = Typ[Invalid] - case *Var: - obj.Type = Typ[Invalid] - default: - unreachable() - } - } - } -} - -func (check *checker) optionalStmt(s ast.Stmt) { - if s != nil { - check.stmt(s) - } -} - -func (check *checker) stmtList(list []ast.Stmt) { - for _, s := range list { - check.stmt(s) - } -} - -func (check *checker) call(call *ast.CallExpr) { - var x operand - check.rawExpr(&x, call, nil, -1, false) // don't check if value is used - // TODO(gri) If a builtin is called, the builtin must be valid in statement context. -} - -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 - } - } - } -} - -// stmt typechecks statement s. -func (check *checker) stmt(s ast.Stmt) { - switch s := s.(type) { - case *ast.BadStmt, *ast.EmptyStmt: - // ignore - - case *ast.DeclStmt: - d, _ := s.Decl.(*ast.GenDecl) - if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) { - check.invalidAST(token.NoPos, "const, type, or var declaration expected") - return - } - if d.Tok == token.CONST { - check.assocInitvals(d) - } - check.decl(d) - - case *ast.LabeledStmt: - // TODO(gri) anything to do with label itself? - check.stmt(s.Stmt) - - case *ast.ExprStmt: - var x operand - used := false - switch e := unparen(s.X).(type) { - case *ast.CallExpr: - // function calls are permitted - used = true - // but some builtins are excluded - // (Caution: This evaluates e.Fun twice, once here and once - // below as part of s.X. This has consequences for - // check.register. Perhaps this can be avoided.) - check.expr(&x, e.Fun, nil, -1) - if x.mode != invalid { - if b, ok := x.typ.(*builtin); ok && !b.isStatement { - used = false - } - } - case *ast.UnaryExpr: - // receive operations are permitted - if e.Op == token.ARROW { - used = true - } - } - if !used { - check.errorf(s.Pos(), "%s not used", s.X) - // ok to continue - } - check.rawExpr(&x, s.X, nil, -1, false) - if x.mode == typexpr { - check.errorf(x.pos(), "%s is not an expression", &x) - } - - case *ast.SendStmt: - var ch, x operand - check.expr(&ch, s.Chan, nil, -1) - check.expr(&x, s.Value, nil, -1) - if ch.mode == invalid || x.mode == invalid { - return - } - if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) { - if 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, s.X, Y, op, -1) - if x.mode == invalid { - return - } - check.assign1to1(s.X, nil, &x, false, -1) - - 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 - } - check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1) - 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 - } - // TODO(gri) make this conversion more efficient - var op token.Token - switch s.Tok { - case token.ADD_ASSIGN: - op = token.ADD - case token.SUB_ASSIGN: - op = token.SUB - case token.MUL_ASSIGN: - op = token.MUL - case token.QUO_ASSIGN: - op = token.QUO - case token.REM_ASSIGN: - op = token.REM - case token.AND_ASSIGN: - op = token.AND - case token.OR_ASSIGN: - op = token.OR - case token.XOR_ASSIGN: - op = token.XOR - case token.SHL_ASSIGN: - op = token.SHL - case token.SHR_ASSIGN: - op = token.SHR - case token.AND_NOT_ASSIGN: - op = token.AND_NOT - default: - check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok) - return - } - var x operand - check.binary(&x, s.Lhs[0], s.Rhs[0], op, -1) - if x.mode == invalid { - return - } - check.assign1to1(s.Lhs[0], nil, &x, false, -1) - } - - case *ast.GoStmt: - check.call(s.Call) - - case *ast.DeferStmt: - check.call(s.Call) - - case *ast.ReturnStmt: - sig := check.funcsig - if n := len(sig.Results); n > 0 { - // TODO(gri) should not have to compute lhs, named every single time - clean this up - lhs := make([]ast.Expr, n) - named := false // if set, function has named results - for i, res := range sig.Results { - if len(res.Name) > 0 { - // a blank (_) result parameter is a named result - named = true - } - name := ast.NewIdent(res.Name) - name.NamePos = s.Pos() - check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil - lhs[i] = name - } - if len(s.Results) > 0 || !named { - // TODO(gri) assignNtoM should perhaps not require len(lhs) > 0 - check.assignNtoM(lhs, s.Results, false, -1) - } - } else if len(s.Results) > 0 { - check.errorf(s.Pos(), "no result values expected") - } - - case *ast.BranchStmt: - // TODO(gri) implement this - - case *ast.BlockStmt: - check.stmtList(s.List) - - case *ast.IfStmt: - check.optionalStmt(s.Init) - var x operand - check.expr(&x, s.Cond, nil, -1) - if x.mode != invalid && !isBoolean(x.typ) { - check.errorf(s.Cond.Pos(), "non-boolean condition in if statement") - } - check.stmt(s.Body) - check.optionalStmt(s.Else) - - case *ast.SwitchStmt: - check.optionalStmt(s.Init) - var x operand - tag := s.Tag - if tag == nil { - // use fake true tag value and position it at the opening { of the switch - ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"} - check.register(ident, Universe.Lookup("true")) - tag = ident - } - check.expr(&x, tag, nil, -1) - - check.multipleDefaults(s.Body.List) - // TODO(gri) check also correct use of fallthrough - seen := make(map[interface{}]token.Pos) - for _, s := range s.Body.List { - clause, _ := s.(*ast.CaseClause) - if clause == nil { - continue // error reported before - } - if x.mode != invalid { - for _, expr := range clause.List { - x := x // copy of x (don't modify original) - var y operand - check.expr(&y, expr, nil, -1) - if y.mode == invalid { - continue // error reported before - } - // If we have a constant case value, it must appear only - // once in the switch statement. Determine if there is a - // duplicate entry, but only report an error if there are - // no other errors. - var dupl token.Pos - var yy operand - if y.mode == constant { - // TODO(gri) This code doesn't work correctly for - // large integer, floating point, or - // complex values - the respective struct - // comparisons are shallow. Need to use a - // hash function to index the map. - dupl = seen[y.val] - seen[y.val] = y.pos() - yy = y // remember y - } - // 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 { - continue // error reported before - } - check.convertUntyped(&x, y.typ) - if x.mode == invalid { - continue // error reported before - } - check.comparison(&y, &x, token.EQL) - if y.mode != invalid && dupl.IsValid() { - check.errorf(yy.pos(), "%s is duplicate case (previous at %s)", - &yy, check.fset.Position(dupl)) - } - } - } - check.stmtList(clause.Body) - } - - case *ast.TypeSwitchStmt: - check.optionalStmt(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 *Var // lhs variable 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 - } - ident, _ := guard.Lhs[0].(*ast.Ident) - if ident == nil { - check.invalidAST(s.Pos(), "incorrect form of type switch guard") - return - } - lhs = check.lookup(ident).(*Var) - 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, nil, -1) - if x.mode == invalid { - return - } - var T *Interface - if T, _ = underlying(x.typ).(*Interface); T == nil { - check.errorf(x.pos(), "%s is not an interface", &x) - return - } - - check.multipleDefaults(s.Body.List) - for _, s := range s.Body.List { - clause, _ := s.(*ast.CaseClause) - if clause == nil { - continue // error reported before - } - // Check each type in this type switch case. - var typ Type - for _, expr := range clause.List { - typ = check.typOrNil(expr, false) - if typ != nil && typ != Typ[Invalid] { - if method, wrongType := missingMethod(typ, T); method != nil { - var msg string - if wrongType { - msg = "%s cannot have dynamic type %s (wrong type for method %s)" - } else { - msg = "%s cannot have dynamic type %s (missing method %s)" - } - check.errorf(expr.Pos(), msg, &x, typ, method.Name) - // ok to continue - } - } - } - // If lhs exists, set its type for each clause. - if lhs != nil { - // 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 || typ == nil { - typ = x.typ - } - lhs.Type = typ - } - check.stmtList(clause.Body) - } - - // There is only one object (lhs) associated with a lhs identifier, but that object - // assumes different types for different clauses. Set it back to the type of the - // TypeSwitchGuard expression so that that variable always has a valid type. - if lhs != nil { - lhs.Type = x.typ - } - - case *ast.SelectStmt: - check.multipleDefaults(s.Body.List) - for _, s := range s.Body.List { - clause, _ := s.(*ast.CommClause) - if clause == nil { - continue // error reported before - } - check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) - check.stmtList(clause.Body) - } - - case *ast.ForStmt: - check.optionalStmt(s.Init) - if s.Cond != nil { - var x operand - check.expr(&x, s.Cond, nil, -1) - if x.mode != invalid && !isBoolean(x.typ) { - check.errorf(s.Cond.Pos(), "non-boolean condition in for statement") - } - } - check.optionalStmt(s.Post) - check.stmt(s.Body) - - case *ast.RangeStmt: - // check expression to iterate over - decl := s.Tok == token.DEFINE - var x operand - check.expr(&x, s.X, nil, -1) - if x.mode == invalid { - // if we don't have a declaration, we can still check the loop's body - if !decl { - check.stmt(s.Body) - } - return - } - - // determine key/value types - var key, val Type - switch typ := underlying(x.typ).(type) { - case *Basic: - if isString(typ) { - key = Typ[UntypedInt] - val = Typ[UntypedRune] - } - case *Array: - key = Typ[UntypedInt] - val = typ.Elt - case *Slice: - key = Typ[UntypedInt] - val = typ.Elt - case *Pointer: - if typ, _ := underlying(typ.Base).(*Array); typ != nil { - key = Typ[UntypedInt] - val = typ.Elt - } - case *Map: - key = typ.Key - val = typ.Elt - case *Chan: - key = typ.Elt - if typ.Dir&ast.RECV == 0 { - 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) - // if we don't have a declaration, we can still check the loop's body - if !decl { - check.stmt(s.Body) - } - return - } - - // check assignment to/declaration of iteration variables - // TODO(gri) The error messages/positions are not great here, - // they refer to the expression in the range clause. - // Should give better messages w/o too much code - // duplication (assignment checking). - x.mode = value - if s.Key != nil { - x.typ = key - x.expr = s.Key - check.assign1to1(s.Key, nil, &x, decl, -1) - } else { - check.invalidAST(s.Pos(), "range clause requires index iteration variable") - // ok to continue - } - if s.Value != nil { - x.typ = val - x.expr = s.Value - check.assign1to1(s.Value, nil, &x, decl, -1) - } - - check.stmt(s.Body) - - default: - check.errorf(s.Pos(), "invalid statement") - } -} diff --git a/src/pkg/go/types/testdata/builtins.src b/src/pkg/go/types/testdata/builtins.src deleted file mode 100644 index e8b5da149b..0000000000 --- a/src/pkg/go/types/testdata/builtins.src +++ /dev/null @@ -1,426 +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 _append() { - var x int - var s []byte - _0 := append /* ERROR "argument" */ () - _1 := append("foo" /* ERROR "not a typed slice" */) - _2 := append(nil /* ERROR "not a typed slice" */, s) - _3 := append(x /* ERROR "not a typed slice" */, s) - _4 := append(s) - append /* ERROR "not used" */ (s) -} - -func _cap() { - var a [10]bool - var p *[20]int - var s []int - var c chan string - _0 := cap /* ERROR "argument" */ () - _1 := cap /* ERROR "argument" */ (1, 2) - _2 := cap(42 /* ERROR "invalid" */) - const _3 = cap(a) - assert(_3 == 10) - const _4 = cap(p) - assert(_4 == 20) - _5 := cap(c) - cap /* ERROR "not used" */ (c) - - // issue 4744 - type T struct{ a [10]int } - const _ = cap(((*T)(nil)).a) -} - -func _close() { - var c chan int - var r <-chan int - close /* ERROR "argument" */ () - close /* ERROR "argument" */ (1, 2) - close(42 /* ERROR "not a channel" */) - close(r /* ERROR "receive-only channel" */) - close(c) -} - -func _complex() { - var i32 int32 - var f32 float32 - var f64 float64 - var c64 complex64 - var c128 complex128 - _ = complex /* ERROR "argument" */ () - _ = complex /* ERROR "argument" */ (1) - _ = 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) -} - -func _copy() { - copy /* ERROR "not enough arguments" */ () - copy /* ERROR "not enough arguments" */ ("foo") - copy([ /* ERROR "copy expects slice arguments" */ ...]int{}, []int{}) - copy([ /* ERROR "copy expects slice arguments" */ ]int{}, [...]int{}) - copy([ /* ERROR "different element types" */ ]int8{}, "foo") - - // spec examples - var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7} - var s = make([]int, 6) - var b = make([]byte, 5) - n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5} - n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5} - n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello") -} - -func _delete() { - var m map[string]int - var s string - delete /* ERROR "argument" */ () - delete /* ERROR "argument" */ (1) - delete /* ERROR "argument" */ (1, 2, 3) - delete(m, 0 /* ERROR "not assignable" */) - delete(m, s) -} - -func _imag() { - var f32 float32 - var f64 float64 - var c64 complex64 - var c128 complex128 - _ = imag /* ERROR "argument" */ () - _ = imag /* ERROR "argument" */ (1, 2) - _ = imag(10 /* ERROR "must be a complex number" */) - _ = imag(2.7182818 /* ERROR "must be a complex number" */) - _ = imag("foo" /* ERROR "must be a complex number" */) - const _5 = imag(1 + 2i) - assert(_5 == 2) - f32 = _5 - f64 = _5 - const _6 = imag(0i) - assert(_6 == 0) - f32 = imag(c64) - f64 = imag(c128) - f32 = imag /* ERROR "cannot assign" */ (c128) - f64 = imag /* ERROR "cannot assign" */ (c64) - imag /* ERROR "not used" */ (c64) -} - -func _len() { - const c = "foobar" - var a [10]bool - var p *[20]int - var s []int - var m map[string]complex128 - _ = len /* ERROR "argument" */ () - _ = len /* ERROR "argument" */ (1, 2) - _ = len(42 /* ERROR "invalid" */) - const _3 = len(c) - assert(_3 == 6) - const _4 = len(a) - assert(_4 == 10) - const _5 = len(p) - assert(_5 == 20) - _ = len(m) - len /* ERROR "not used" */ (c) - - // esoteric case - var t string - var hash map[interface{}][]*[10]int - const n = len /* ERROR "not constant" */ (hash[recover()][len(t)]) - assert /* ERROR "failed" */ (n == 10) - var ch <-chan int - const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)]) - - // issue 4744 - type T struct{ a [10]int } - const _ = len(((*T)(nil)).a) -} - -func _make() { - var n int - var m float32 - var s uint - - _ = make /* ERROR "argument" */ () - _ = make(1 /* ERROR "not a type" */) - _ = make(int /* ERROR "cannot make" */) - - // slices - _ = make/* ERROR "arguments" */ ([]int) - _ = make/* ERROR "arguments" */ ([]int, 2, 3, 4) - _ = make([]int, int /* ERROR "not an expression" */) - _ = make([]int, 10, float32 /* ERROR "not an expression" */) - _ = make([]int, "foo" /* ERROR "cannot convert" */) - _ = make([]int, 10, 2.3 /* ERROR "overflows" */) - _ = make([]int, 5, 10.0) - _ = make([]int, 0i) - _ = make([]int, 1.0) - _ = make([]int, 1.0<X, T2->X - ) - - var t T3 - _ = t /* ERROR "no single field or method" */ .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 "no single field or method" */ .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 "no single field or method" */ .X -} - -// 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 -} - -// 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 single field or method" */ {}{}.Foo - _ = S0{}.A - _ = S0 /* ERROR "no single field or method" */ {}.D - _ = S1{}.A - _ = S1{}.B - _ = S1{}.S0 - _ = S1{}.C - _ = S2{}.A - _ = S2{}.S1 - _ = S2{}.B - _ = S2{}.C - _ = S2 /* ERROR "no single field or method" */ {}.D - _ = S3 /* ERROR "no single field or method" */ {}.S1 - _ = S3{}.A - _ = S3 /* ERROR "no single field or method" */ {}.B - _ = S3{}.D - _ = S3{}.E - _ = S4{}.A - _ = S4 /* ERROR "no single field or method" */ {}.B - _ = S5 /* ERROR "no single field or method" */ {}.X - _ = S5{}.Y - _ = S10 /* ERROR "no single field or method" */ {}.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 "no single field or method" */ {}.X \ No newline at end of file diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go deleted file mode 100644 index 8ee28b0942..0000000000 --- a/src/pkg/go/types/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/src/pkg/go/types/testdata/expr0.src b/src/pkg/go/types/testdata/expr0.src deleted file mode 100644 index 8d057f63c1..0000000000 --- a/src/pkg/go/types/testdata/expr0.src +++ /dev/null @@ -1,161 +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 - -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" */ - - // 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 /* ERROR not defined */ - 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 /* ERROR not defined */ - 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 /* ERROR "cannot indirect" */ - s6 = &s4 - s7 = *s6 - s8 = <-s7 /* ERROR "cannot receive" */ - - // 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" */ -) - -// 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/src/pkg/go/types/testdata/expr1.src b/src/pkg/go/types/testdata/expr1.src deleted file mode 100644 index 8ef0aed6d2..0000000000 --- a/src/pkg/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/src/pkg/go/types/testdata/expr2.src b/src/pkg/go/types/testdata/expr2.src deleted file mode 100644 index 674be4005d..0000000000 --- a/src/pkg/go/types/testdata/expr2.src +++ /dev/null @@ -1,23 +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 - _ = struct{b bool}{x < y} -} - -// corner cases -var ( - v0 = nil /* ERROR "cannot compare" */ == nil -) \ No newline at end of file diff --git a/src/pkg/go/types/testdata/expr3.src b/src/pkg/go/types/testdata/expr3.src deleted file mode 100644 index 48f6bc770f..0000000000 --- a/src/pkg/go/types/testdata/expr3.src +++ /dev/null @@ -1,338 +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 - -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 "overflows" */ ] - _ = a[1.0] - _ = a[- /* ERROR "negative" */ 1] - _ = a[- /* ERROR "negative" */ 1 :] - _ = a[: - /* ERROR "negative" */ 1] - var a0 int - a0 = a[0] - var a1 int32 - a1 = a /* ERROR "cannot assign" */ [1] - _ = 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] - - 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] - - var b [0]int - _ = b[0 /* ERROR "index .* out of bounds" */ ] - _ = b[:] - _ = b[0:] - _ = b[:0] - _ = b[0:0] - - var s []int - _ = s[- /* ERROR "negative" */ 1] - _ = s[- /* ERROR "negative" */ 1 :] - _ = s[: - /* ERROR "negative" */ 1] - _ = s[0] - _ = s[1 : 2] - _ = s[2 /* ERROR "inverted slice range" */ : 1] - _ = s[2 :] - _ = s[: 1 /* ERROR "overflows" */ <<100] - _ = s[1 /* ERROR "overflows" */ <<100 :] - _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] - - var t string - _ = t[- /* ERROR "negative" */ 1] - _ = t[- /* ERROR "negative" */ 1 :] - _ = t[: - /* ERROR "negative" */ 1] - var t0 byte - t0 = t[0] - var t1 rune - t1 = t /* ERROR "cannot assign" */ [2] - _ = ("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] - var c2 float32 - c2 = c /* ERROR "cannot assign" */ [2] - _ = c[3 /* ERROR "index .* out of bounds" */ ] - _ = ""[0 /* ERROR "index .* out of bounds" */ ] - - _ = 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] -} - -type T struct { - x int -} - -func (*T) m() {} - -func method_expressions() { - _ = T /* ERROR "no single field or method" */ .a - _ = T /* ERROR "has no method" */ .x - _ = T.m - var f func(*T) = (*T).m - var g func(*T) = ( /* ERROR "cannot initialize" */ T).m -} - -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" */ }} - _ = 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 "overflows" */} -} - -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 "overflows" */ } - _ = A1{"foo" /* ERROR "cannot convert" */ } - - 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" */ - - a2 := [...]int{- /* ERROR "negative" */ 1: 0} - - 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) - - // 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 "overflows" */ } - _ = S0{"foo" /* ERROR "cannot convert" */ } - - // indices must be resolved correctly - // (for details, see comment in go/parser/parser.go, method parseElement) - index1 := 1 - _ = S0{index1: 1} - _ = S0{index2: 2} - _ = S0{index3 /* ERROR "undeclared name" */ : 3} -} - -var index2 int = 2 - -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 keys must be resolved correctly - // (for details, see comment in go/parser/parser.go, method parseElement) - key1 := "foo" - _ = M0{key1: 1} - _ = M0{key2: 2} - _ = M0{key3 /* ERROR "undeclared name" */ : 2} - - _ = M1{true: 1, false: 0} - _ = M2{nil: 0, &index2: 1} -} - -var key2 string = "bar" - -type I interface { - m() -} - -type I2 interface { - m(int) -} - -type T1 struct{} -type T2 struct{} - -func (T2) m(int) {} - -func type_asserts() { - var x int - _ = x /* ERROR "not an interface" */ .(int) - - var e interface{} - var ok bool - x, ok = e.(int) - - var t I - _ = t /* ERROR "use of .* outside type switch" */ .(type) - _ = t.(T) - _ = t.(T1 /* ERROR "missing method m" */ ) - _ = t.(T2 /* ERROR "wrong type for method m" */ ) - _ = t.(I2 /* ERROR "wrong type for method m" */ ) -} - -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 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 /* ERROR "too few arguments" */ (3.14) - f2(3.14, "foo") - f2(x /* ERROR "cannot pass" */ , "foo") - f2(g0 /* ERROR "used as value" */ ()) - f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot pass" */ ()) - f2(g2()) - - fs /* ERROR "too few arguments" */ () - fs(g0 /* ERROR "used as value" */ ()) - fs(g1 /* ERROR "cannot pass" */ ()) - // fs(g2()) // TODO(gri) missing position in error message - fs(gs()) - - fv() - fv(1, 2.0, x) - fv(s /* ERROR "cannot pass" */ ) - fv(s...) - fv(1, s /* ERROR "can only use ... with matching parameter" */ ...) - fv(gs /* ERROR "cannot pass" */ ()) - fv(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" */ ()) -} diff --git a/src/pkg/go/types/testdata/shifts.src b/src/pkg/go/types/testdata/shifts.src deleted file mode 100644 index 49496e5b4b..0000000000 --- a/src/pkg/go/types/testdata/shifts.src +++ /dev/null @@ -1,266 +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 shifts1() { - // basics - var ( - i0 int - u0 uint - - v0 = 1<<0 - v1 = 1<> s) -} - -func shifts4() { - // shifts in comparisons w/ untyped operands - var s uint - - _ = 1<= 0 { - return // nothing to do - } - // fix Obj link for named types - if typ, ok := obj.GetType().(*NamedType); ok { - typ.Obj = obj.(*TypeName) - } - // exported identifiers go into package unsafe - scope := Universe - if ast.IsExported(name) { - scope = Unsafe.Scope - // set Pkg field - switch obj := obj.(type) { - case *TypeName: - obj.Pkg = Unsafe - case *Func: - obj.Pkg = Unsafe - default: - unreachable() - } - } - if scope.Insert(obj) != nil { - panic("internal error: double declaration") - } -}