--- /dev/null
+// 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/scanner"
+ "go/token"
+ "sort"
+)
+
+type checker struct {
+ fset *token.FileSet
+ pkg *ast.Package
+ errors scanner.ErrorList
+ types map[ast.Expr]Type
+}
+
+// declare declares an object of the given kind and name (ident) in scope;
+// decl is the corresponding declaration in the AST. An error is reported
+// if the object was declared before.
+//
+// TODO(gri) This is very similar to the declare function in go/parser; it
+// is only used to associate methods with their respective receiver base types.
+// In a future version, it might be simpler and cleaner do to all the resolution
+// in the type-checking phase. It would simplify the parser, AST, and also
+// reduce some amount of code duplication.
+//
+func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ident, decl ast.Decl) {
+ assert(ident.Obj == nil) // identifier already declared or resolved
+ obj := ast.NewObj(kind, ident.Name)
+ obj.Decl = decl
+ ident.Obj = obj
+ if ident.Name != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ prevDecl := ""
+ if pos := alt.Pos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
+ }
+ check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
+ }
+ }
+}
+
+func (check *checker) decl(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
+ if len(lhs) == 0 {
+ check.invalidAST(pos, "missing lhs in declaration")
+ return
+ }
+
+ var t Type
+ if typ != nil {
+ t = check.typ(typ, false)
+ }
+
+ // len(lhs) >= 1
+ if len(lhs) == len(rhs) {
+ // check only corresponding lhs and rhs
+ var l, r ast.Expr
+ for i, ident := range lhs {
+ if ident.Obj == obj {
+ l = lhs[i]
+ r = rhs[i]
+ break
+ }
+ }
+ assert(l != nil)
+ obj.Type = t
+ // check rhs
+ var x operand
+ check.expr(&x, r, t, iota)
+ // assign to lhs
+ check.assignment(l, &x, true)
+ return
+ }
+
+ if t != nil {
+ for _, name := range lhs {
+ name.Obj.Type = t
+ }
+ }
+
+ // 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)
+ }
+}
+
+// specValues returns the list of initialization expressions
+// for the given part (spec) of a constant declaration.
+// TODO(gri) Make this more efficient by caching results
+// (using a map in checker).
+func (check *checker) specValues(spec *ast.ValueSpec) []ast.Expr {
+ if len(spec.Values) > 0 {
+ return spec.Values
+ }
+
+ // find the corresponding values
+ for _, file := range check.pkg.Files {
+ for _, d := range file.Decls {
+ if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.CONST {
+ var values []ast.Expr
+ for _, s := range d.Specs {
+ if s, ok := s.(*ast.ValueSpec); ok {
+ if len(s.Values) > 0 {
+ values = s.Values
+ }
+ if s == spec {
+ return values
+ }
+ }
+ }
+ }
+ }
+ }
+
+ check.invalidAST(spec.Pos(), "no initialization values provided")
+ return nil
+}
+
+// obj type checks an object.
+func (check *checker) obj(obj *ast.Object, cycleOk bool) {
+ if trace {
+ fmt.Printf("obj(%s)\n", obj.Name)
+ }
+
+ if obj.Type != nil {
+ // object has already been type checked
+ return
+ }
+
+ switch obj.Kind {
+ case ast.Bad, ast.Pkg:
+ // nothing to do
+
+ case ast.Con:
+ if obj.Data == nil {
+ check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
+ return
+ }
+ spec, ok := obj.Decl.(*ast.ValueSpec)
+ assert(ok)
+ // The Data stored with the constant is the value of iota for that
+ // ast.ValueSpec. Use it for the evaluation of the initialization
+ // expressions.
+ iota := obj.Data.(int)
+ obj.Data = nil
+ check.decl(spec.Pos(), obj, spec.Names, spec.Type, check.specValues(spec), iota)
+
+ case ast.Var:
+ // TODO(gri) missing cycle detection
+ spec, ok := obj.Decl.(*ast.ValueSpec)
+ if !ok {
+ // TODO(gri) the assertion fails for "x, y := 1, 2, 3" it seems
+ fmt.Printf("var = %s\n", obj.Name)
+ }
+ assert(ok)
+ check.decl(spec.Pos(), obj, spec.Names, spec.Type, spec.Values, 0)
+
+ case ast.Typ:
+ typ := &NamedType{Obj: obj}
+ obj.Type = typ // "mark" object so recursion terminates
+ typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
+ // collect associated methods, if any
+ if obj.Data != nil {
+ scope := obj.Data.(*ast.Scope)
+ // struct fields must not conflict with methods
+ if t, ok := typ.Underlying.(*Struct); ok {
+ for _, f := range t.Fields {
+ if m := scope.Lookup(f.Name); m != nil {
+ check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
+ }
+ }
+ }
+ // collect methods
+ methods := make(ObjList, len(scope.Objects))
+ i := 0
+ for _, m := range scope.Objects {
+ methods[i] = m
+ i++
+ }
+ methods.Sort()
+ typ.Methods = methods
+ // methods cannot be associated with an interface type
+ // (do this check after sorting for reproducible error positions - needed for testing)
+ if _, ok := typ.Underlying.(*Interface); ok {
+ for _, m := range methods {
+ recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
+ check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
+ }
+ }
+ }
+
+ case ast.Fun:
+ fdecl := obj.Decl.(*ast.FuncDecl)
+ ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
+ obj.Type = ftyp
+ if fdecl.Recv != nil {
+ // TODO(gri) handle method receiver
+ }
+ check.stmt(fdecl.Body)
+
+ default:
+ panic("unreachable")
+ }
+}
+
+func check(fset *token.FileSet, pkg *ast.Package, types map[ast.Expr]Type) error {
+ var check checker
+ check.fset = fset
+ check.pkg = pkg
+ check.types = types
+
+ // Compute sorted list of file names so that
+ // package file iterations are reproducible (needed for testing).
+ filenames := make([]string, len(pkg.Files))
+ {
+ i := 0
+ for filename := range pkg.Files {
+ filenames[i] = filename
+ i++
+ }
+ sort.Strings(filenames)
+ }
+
+ // Associate methods with types
+ // TODO(gri) All other objects are resolved by the parser.
+ // Consider doing this in the parser (and provide the info
+ // in the AST. In the long-term (might require Go 1 API
+ // changes) it's probably easier to do all the resolution
+ // in one place in the type checker. See also comment
+ // with checker.declare.
+ for _, filename := range filenames {
+ file := pkg.Files[filename]
+ for _, decl := range file.Decls {
+ if meth, ok := decl.(*ast.FuncDecl); ok && meth.Recv != nil {
+ // 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 object (or nil if error)
+ var obj *ast.Object
+ if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil {
+ obj = ident.Obj
+ if obj.Kind != ast.Typ {
+ check.errorf(ident.Pos(), "%s is not a type", ident.Name)
+ obj = nil
+ }
+ // TODO(gri) determine if obj was defined in this package
+ /*
+ if check.notLocal(obj) {
+ check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name)
+ obj = nil
+ }
+ */
+ } else {
+ // If it's not an identifier or the identifier wasn't declared/resolved,
+ // the parser/resolver already reported an error. Nothing to do here.
+ }
+ // determine base type scope (or nil if error)
+ var scope *ast.Scope
+ if obj != nil {
+ if obj.Data != nil {
+ scope = obj.Data.(*ast.Scope)
+ } else {
+ scope = ast.NewScope(nil)
+ obj.Data = scope
+ }
+ } else {
+ // use a dummy scope so that meth can be declared in
+ // presence of an error and get an associated object
+ // (always use a new scope so that we don't get double
+ // declaration errors)
+ scope = ast.NewScope(nil)
+ }
+ check.declare(scope, ast.Fun, meth.Name, meth)
+ }
+ }
+ }
+
+ // Sort objects so that we get reproducible error
+ // positions (this is only needed for testing).
+ // TODO(gri): Consider ast.Scope implementation that
+ // provides both a list and a map for fast lookup.
+ // Would permit the use of scopes instead of ObjMaps
+ // elsewhere.
+ list := make(ObjList, len(pkg.Scope.Objects))
+ {
+ i := 0
+ for _, obj := range pkg.Scope.Objects {
+ list[i] = obj
+ i++
+ }
+ list.Sort()
+ }
+
+ // Check global objects.
+ for _, obj := range list {
+ check.obj(obj, false)
+ }
+
+ // TODO(gri) Missing pieces:
+ // - blank (_) objects and init functions are not in scopes but should be type-checked
+
+ // do not remove multiple errors per line - depending on
+ // order or error reporting this may hide the real error
+ return check.errors.Err()
+}
--- /dev/null
+// 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. Consider introducing
+// a Const type and use methods instead of xConst functions.
+
+// Representation of constant values.
+//
+// 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 constants.
+var (
+ zeroConst = int64(0)
+ oneConst = int64(1)
+ minusOneConst = int64(-1)
+ nilConst = new(nilType)
+)
+
+// 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 value,
+// or *big.Rat value.
+//
+func normalizeRatConst(x *big.Rat) interface{} {
+ if x.IsInt() {
+ return normalizeIntConst(x.Num())
+ }
+ return x
+}
+
+// normalizeComplexConst returns the smallest constant representation
+// for the specific value of x; either an int64, *big.Int value, *big.Rat,
+// or complex value.
+//
+func normalizeComplexConst(x complex) interface{} {
+ if x.im.Sign() == 0 {
+ return normalizeRatConst(x.re)
+ }
+ return x
+}
+
+// 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 normalizeComplexConst(complex{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
+}
+
+// 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{}, as BasicKind) bool {
+ const intBits = 32 // TODO(gri) implementation-specific constant
+ const ptrBits = 64 // TODO(gri) implementation-specific constant
+
+ switch x := x.(type) {
+ case bool:
+ return as == Bool || as == UntypedBool
+
+ case int64:
+ switch as {
+ case Int:
+ return -1<<(intBits-1) <= x && x <= 1<<(intBits-1)-1
+ case Int8:
+ return -1<<(8-1) <= x && x <= 1<<(8-1)-1
+ case Int16:
+ return -1<<(16-1) <= x && x <= 1<<(16-1)-1
+ case Int32, UntypedRune:
+ return -1<<(32-1) <= x && x <= 1<<(32-1)-1
+ case Int64:
+ return true
+ case Uint:
+ return 0 <= x && x <= 1<<intBits-1
+ case Uint8:
+ return 0 <= x && x <= 1<<8-1
+ case Uint16:
+ return 0 <= x && x <= 1<<16-1
+ case Uint32:
+ return 0 <= x && x <= 1<<32-1
+ case Uint64:
+ return 0 <= x
+ case Uintptr:
+ assert(ptrBits == 64)
+ return 0 <= x
+ 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.Int:
+ switch as {
+ case Uint:
+ return x.Sign() >= 0 && x.BitLen() <= intBits
+ case Uint64:
+ return x.Sign() >= 0 && x.BitLen() <= 64
+ case Uintptr:
+ return x.Sign() >= 0 && x.BitLen() <= ptrBits
+ 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
+
+ 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
+}
+
+// binaryOpConst returns the result of the constant evaluation x op y;
+// both operands must be of the same "kind" (boolean, numeric, or string).
+// If intDiv is true, division (op == token.QUO) is using integer division
+// (and the result is guaranteed to be integer) rather than floating-point
+// division. Division by zero leads to a run-time panic.
+//
+func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} {
+ x, y = matchConst(x, y)
+
+ switch x := x.(type) {
+ case bool:
+ y := y.(bool)
+ switch op {
+ case token.LAND:
+ return x && y
+ case token.LOR:
+ return x || y
+ default:
+ unreachable()
+ }
+
+ case int64:
+ y := y.(int64)
+ switch op {
+ case token.ADD:
+ // TODO(gri) can do better than this
+ if is63bit(x) && is63bit(y) {
+ return x + y
+ }
+ return normalizeIntConst(new(big.Int).Add(big.NewInt(x), big.NewInt(y)))
+ case token.SUB:
+ // TODO(gri) can do better than this
+ if is63bit(x) && is63bit(y) {
+ return x - y
+ }
+ return normalizeIntConst(new(big.Int).Sub(big.NewInt(x), big.NewInt(y)))
+ case token.MUL:
+ // TODO(gri) can do better than this
+ if is32bit(x) && is32bit(y) {
+ return x * y
+ }
+ return normalizeIntConst(new(big.Int).Mul(big.NewInt(x), big.NewInt(y)))
+ case token.REM:
+ return x % y
+ case token.QUO:
+ if intDiv {
+ return x / y
+ }
+ return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
+ case token.AND:
+ return x & y
+ case token.OR:
+ return x | y
+ case token.XOR:
+ return x ^ y
+ case token.AND_NOT:
+ return x &^ y
+ default:
+ unreachable()
+ }
+
+ case *big.Int:
+ y := y.(*big.Int)
+ var z big.Int
+ switch op {
+ case token.ADD:
+ z.Add(x, y)
+ case token.SUB:
+ z.Sub(x, y)
+ case token.MUL:
+ z.Mul(x, y)
+ case token.REM:
+ z.Rem(x, y)
+ case token.QUO:
+ if intDiv {
+ z.Quo(x, y)
+ } else {
+ return normalizeRatConst(new(big.Rat).SetFrac(x, y))
+ }
+ case token.AND:
+ z.And(x, y)
+ case token.OR:
+ z.Or(x, y)
+ case token.XOR:
+ z.Xor(x, y)
+ case token.AND_NOT:
+ z.AndNot(x, y)
+ default:
+ unreachable()
+ }
+ return normalizeIntConst(&z)
+
+ case *big.Rat:
+ y := y.(*big.Rat)
+ var z big.Rat
+ switch op {
+ case token.ADD:
+ z.Add(x, y)
+ case token.SUB:
+ z.Sub(x, y)
+ case token.MUL:
+ z.Mul(x, y)
+ case token.QUO:
+ z.Quo(x, y)
+ default:
+ unreachable()
+ }
+ return normalizeRatConst(&z)
+
+ case complex:
+ y := y.(complex)
+ a, b := x.re, x.im
+ c, d := y.re, y.im
+ var re, im big.Rat
+ switch op {
+ case token.ADD:
+ // (a+c) + i(b+d)
+ re.Add(a, c)
+ im.Add(b, d)
+ case token.SUB:
+ // (a-c) + i(b-d)
+ re.Sub(a, c)
+ im.Sub(b, d)
+ case token.MUL:
+ // (ac-bd) + i(bc+ad)
+ var ac, bd, bc, ad big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ re.Sub(&ac, &bd)
+ im.Add(&bc, &ad)
+ case token.QUO:
+ // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+ var ac, bd, bc, ad, s big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ s.Add(c.Mul(c, c), d.Mul(d, d))
+ re.Add(&ac, &bd)
+ re.Quo(&re, &s)
+ im.Sub(&bc, &ad)
+ im.Quo(&im, &s)
+ default:
+ unreachable()
+ }
+ return normalizeComplexConst(complex{&re, &im})
+
+ case string:
+ if op == token.ADD {
+ return x + y.(string)
+ }
+ }
+
+ unreachable()
+ return nil
+}
+
+// shiftConst returns the result of the constant evaluation x op s
+// where op is token.SHL or token.SHR (<< or >>). x must be an
+// integer constant.
+//
+func shiftConst(x interface{}, s uint, op token.Token) interface{} {
+ switch x := x.(type) {
+ 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) {
+ 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
+}
--- /dev/null
+// 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"
+)
+
+// debugging flags
+const debug = false
+const trace = false
+
+func assert(p bool) {
+ if !p {
+ panic("assertion failed")
+ }
+}
+
+func unimplemented() {
+ if debug {
+ panic("unimplemented")
+ }
+}
+
+func unreachable() {
+ panic("unreachable")
+}
+
+// dump is only needed for debugging
+func (check *checker) dump(format string, args ...interface{}) {
+ if n := len(format); n > 0 && format[n-1] != '\n' {
+ format += "\n"
+ }
+ check.convertArgs(args)
+ fmt.Printf(format, args...)
+}
+
+func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) {
+ check.convertArgs(args)
+ msg := fmt.Sprintf(format, args...)
+ check.errors.Add(check.fset.Position(pos), msg)
+}
+
+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...)
+}
+
+func (check *checker) convertArgs(args []interface{}) {
+ for i, arg := range args {
+ switch a := arg.(type) {
+ case token.Pos:
+ args[i] = check.fset.Position(a)
+ case ast.Expr:
+ args[i] = exprString(a)
+ case Type:
+ args[i] = typeString(a)
+ case operand:
+ panic("internal error: should always pass *operand")
+ }
+ }
+}
+
+// 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, "<expr %T>", 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 ObjList, 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.(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("<nil>")
+
+ 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 *tuple:
+ buf.WriteByte('(')
+ for i, typ := range t.list {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ writeType(buf, typ)
+ }
+ buf.WriteByte(')')
+
+ case *Signature:
+ buf.WriteString("func")
+ writeSignature(buf, t)
+
+ case *builtin:
+ fmt.Fprintf(buf, "<type of %s>", 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.(*Signature))
+ }
+ 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:
+ buf.WriteString(t.Obj.Name)
+
+ default:
+ fmt.Fprintf(buf, "<type %T>", t)
+ }
+}
+++ /dev/null
-// 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
-
-import (
- "bytes"
- "fmt"
- "go/ast"
-)
-
-// 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, "<expr %T>", x)
- }
-}
--- /dev/null
+// 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 {
+ fmt.Fprintf(&buf, " %v", 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
+ }
+}
+
+// implements reports whether x implements interface T.
+func (x *operand) implements(T *Interface) bool {
+ if x.mode == invalid {
+ return true // avoid spurious errors
+ }
+
+ unimplemented()
+ return true
+}
+
+// isAssignable reports whether x is assignable to a variable of type T.
+func (x *operand) isAssignable(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 && x.implements(Ti) {
+ 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.typ == Typ[UntypedNil] {
+ switch Tu.(type) {
+ case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
+ return true
+ }
+ return false
+ }
+
+ // x is an untyped constant representable by a value of type T
+ // - this is taken care of in the assignment check
+ // TODO(gri) double-check - isAssignable is used elsewhere
+
+ 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, UntypedInt)
+}
+
+// lookupField returns the struct field with the given name in typ.
+// If no such field exists, the result is nil.
+// TODO(gri) should this be a method of Struct?
+//
+func lookupField(typ *Struct, name string) *StructField {
+ // TODO(gri) deal with embedding and conflicts - this is
+ // a very basic version to get going for now.
+ for _, f := range typ.Fields {
+ if f.Name == name {
+ return f
+ }
+ }
+ return nil
+}
return false
}
-// 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
-}
-
// identical returns true if x and y are identical.
func isIdentical(x, y Type) bool {
if x == y {
}
return false
}
+
+// 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 argument typ for all other types.
+func defaultType(typ Type) Type {
+ if t, ok := typ.(*Basic); ok {
+ var k BasicKind
+ switch t.Kind {
+ case UntypedBool:
+ k = Bool
+ case UntypedRune:
+ k = Rune
+ case UntypedInt:
+ k = Int
+ case UntypedFloat:
+ k = Float64
+ case UntypedComplex:
+ k = Complex128
+ case UntypedString:
+ k = String
+ default:
+ unreachable()
+ }
+ typ = Typ[k]
+ }
+ return typ
+}
--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains unimplemented stubs so that the
+// code in exp/types/staging compiles.
+
+package types
+
+import "go/ast"
+
+// expr typechecks expression e and initializes x with the expression
+// value or type. If an error occured, x.mode is set to invalid.
+// A hint != nil is used as operand type for untyped shifted operands;
+// 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) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) {
+ unimplemented()
+}
+
+// expr is like exprOrType but also checks that e represents a value (rather than a type).
+func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
+ unimplemented()
+}
+
+// typ is like exprOrType but also checks that e represents a type (rather than a value).
+// If an error occured, the result is Typ[Invalid].
+//
+func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
+ unimplemented()
+ return nil
+}
+
+// assignNtoM typechecks a general assignment. If decl is set, the lhs operands
+// must be identifiers. If their types are not set, they are deduced from the
+// types of the corresponding rhs expressions. iota >= 0 indicates that the
+// "assignment" is part of a constant declaration.
+//
+func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
+ unimplemented()
+}
+
+// assignment typechecks a single assignment of the form lhs := x. If decl is set,
+// the lhs operand must be an identifier. If its type is not set, it is deduced
+// from the type or value of x.
+//
+func (check *checker) assignment(lhs ast.Expr, x *operand, decl bool) {
+ unimplemented()
+}
+
+// stmt typechecks statement s.
+func (check *checker) stmt(s ast.Stmt) {
+ unimplemented()
+}
// the expression appears in the AST.
//
func Check(fset *token.FileSet, pkg *ast.Package, types map[ast.Expr]Type) error {
- // return check(fset, pkg, types) // commented out for now to make it compile
- return nil
+ return check(fset, pkg, types)
}
// All types implement the Type interface.
implementsType
Kind BasicKind
Info BasicInfo
+ Size int64 // > 0 if valid
Name string
}
// A builtin represents the type of a built-in function.
type builtin struct {
implementsType
- id builtinId
- name string
- nargs int // number of arguments (minimum if variadic)
- isVariadic bool
+ id builtinId
+ name string
+ nargs int // number of arguments (minimum if variadic)
+ isVariadic bool
+ isStatement bool // true if the built-in is valid as an expression statement
}
// An Interface represents an interface type interface{...}.
+++ /dev/null
-// 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 the TypeString function.
-
-package types
-
-import (
- "bytes"
- "fmt"
- "go/ast"
-)
-
-// 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 ObjList, 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.(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("<nil>")
-
- 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 *tuple:
- buf.WriteByte('(')
- for i, typ := range t.list {
- if i > 0 {
- buf.WriteString("; ")
- }
- writeType(buf, typ)
- }
- buf.WriteByte(')')
-
- case *Signature:
- buf.WriteString("func")
- writeSignature(buf, t)
-
- case *builtin:
- fmt.Fprintf(buf, "<type of %s>", 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.(*Signature))
- }
- 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:
- buf.WriteString(t.Obj.Name)
-
- default:
- fmt.Fprintf(buf, "<type %T>", t)
- }
-}
// Predeclared types, indexed by BasicKind.
var Typ = [...]*Basic{
- Invalid: {aType, Invalid, 0, "invalid type"},
-
- Bool: {aType, Bool, IsBoolean, "bool"},
- Int: {aType, Int, IsInteger, "int"},
- Int8: {aType, Int8, IsInteger, "int8"},
- Int16: {aType, Int16, IsInteger, "int16"},
- Int32: {aType, Int32, IsInteger, "int32"},
- Int64: {aType, Int64, IsInteger, "int64"},
- Uint: {aType, Uint, IsInteger | IsUnsigned, "uint"},
- Uint8: {aType, Uint8, IsInteger | IsUnsigned, "uint8"},
- Uint16: {aType, Uint16, IsInteger | IsUnsigned, "uint16"},
- Uint32: {aType, Uint32, IsInteger | IsUnsigned, "uint32"},
- Uint64: {aType, Uint64, IsInteger | IsUnsigned, "uint64"},
- Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, "uintptr"},
- Float32: {aType, Float32, IsFloat, "float32"},
- Float64: {aType, Float64, IsFloat, "float64"},
- Complex64: {aType, Complex64, IsComplex, "complex64"},
- Complex128: {aType, Complex128, IsComplex, "complex128"},
- String: {aType, String, IsString, "string"},
- UnsafePointer: {aType, UnsafePointer, 0, "Pointer"},
-
- UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, "untyped boolean"},
- UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, "untyped integer"},
- UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, "untyped rune"},
- UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, "untyped float"},
- UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
- UntypedString: {aType, UntypedString, IsString | IsUntyped, "untyped string"},
- UntypedNil: {aType, UntypedNil, IsUntyped, "untyped nil"},
+ Invalid: {aType, Invalid, 0, 0, "invalid type"},
+
+ Bool: {aType, Bool, IsBoolean, 1, "bool"},
+ Int: {aType, Int, IsInteger, 0, "int"},
+ Int8: {aType, Int8, IsInteger, 1, "int8"},
+ Int16: {aType, Int16, IsInteger, 2, "int16"},
+ Int32: {aType, Int32, IsInteger, 4, "int32"},
+ Int64: {aType, Int64, IsInteger, 8, "int64"},
+ Uint: {aType, Uint, IsInteger | IsUnsigned, 0, "uint"},
+ Uint8: {aType, Uint8, IsInteger | IsUnsigned, 1, "uint8"},
+ Uint16: {aType, Uint16, IsInteger | IsUnsigned, 2, "uint16"},
+ Uint32: {aType, Uint32, IsInteger | IsUnsigned, 4, "uint32"},
+ Uint64: {aType, Uint64, IsInteger | IsUnsigned, 8, "uint64"},
+ Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
+ Float32: {aType, Float32, IsFloat, 4, "float32"},
+ Float64: {aType, Float64, IsFloat, 8, "float64"},
+ Complex64: {aType, Complex64, IsComplex, 8, "complex64"},
+ Complex128: {aType, Complex128, IsComplex, 16, "complex128"},
+ String: {aType, String, IsString, 0, "string"},
+ UnsafePointer: {aType, UnsafePointer, 0, 0, "Pointer"},
+
+ UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
+ UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
+ UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
+ UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
+ UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
+ UntypedString: {aType, UntypedString, IsString | IsUntyped, 0, "untyped string"},
+ UntypedNil: {aType, UntypedNil, IsUntyped, 0, "untyped nil"},
}
var aliases = [...]*Basic{
- {aType, Uint8, IsInteger | IsUnsigned, "byte"},
- {aType, Rune, IsInteger, "rune"},
+ {aType, Byte, IsInteger | IsUnsigned, 1, "byte"},
+ {aType, Rune, IsInteger, 4, "rune"},
}
var predeclaredConstants = [...]*struct {
}{
{UntypedBool, "true", true},
{UntypedBool, "false", false},
- {UntypedInt, "iota", int64(0)},
- {UntypedNil, "nil", nil},
+ {UntypedInt, "iota", zeroConst},
+ {UntypedNil, "nil", nilConst},
}
var predeclaredFunctions = [...]*builtin{
- {aType, _Append, "append", 1, true},
- {aType, _Cap, "cap", 1, false},
- {aType, _Close, "close", 1, false},
- {aType, _Complex, "complex", 2, false},
- {aType, _Copy, "copy", 2, false},
- {aType, _Delete, "delete", 2, false},
- {aType, _Imag, "imag", 1, false},
- {aType, _Len, "len", 1, false},
- {aType, _Make, "make", 1, true},
- {aType, _New, "new", 1, false},
- {aType, _Panic, "panic", 1, false},
- {aType, _Print, "print", 1, true},
- {aType, _Println, "println", 1, true},
- {aType, _Real, "real", 1, false},
- {aType, _Recover, "recover", 0, false},
-
- {aType, _Alignof, "Alignof", 1, false},
- {aType, _Offsetof, "Offsetof", 1, false},
- {aType, _Sizeof, "Sizeof", 1, false},
+ {aType, _Append, "append", 1, true, false},
+ {aType, _Cap, "cap", 1, false, false},
+ {aType, _Close, "close", 1, false, true},
+ {aType, _Complex, "complex", 2, false, false},
+ {aType, _Copy, "copy", 2, false, true},
+ {aType, _Delete, "delete", 2, false, true},
+ {aType, _Imag, "imag", 1, false, false},
+ {aType, _Len, "len", 1, false, false},
+ {aType, _Make, "make", 1, true, false},
+ {aType, _New, "new", 1, false, false},
+ {aType, _Panic, "panic", 1, false, true},
+ {aType, _Print, "print", 1, true, true},
+ {aType, _Println, "println", 1, true, true},
+ {aType, _Real, "real", 1, false, false},
+ {aType, _Recover, "recover", 0, false, true},
+
+ {aType, _Alignof, "Alignof", 1, false, false},
+ {aType, _Offsetof, "Offsetof", 1, false, false},
+ {aType, _Sizeof, "Sizeof", 1, false, false},
}
// commonly used types