diff --git a/src/pkg/exp/types/staging/check.go b/src/pkg/exp/types/staging/check.go new file mode 100644 index 0000000000..c54acc65aa --- /dev/null +++ b/src/pkg/exp/types/staging/check.go @@ -0,0 +1,322 @@ +// 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() +} diff --git a/src/pkg/exp/types/staging/const.go b/src/pkg/exp/types/staging/const.go new file mode 100644 index 0000000000..79b89e1915 --- /dev/null +++ b/src/pkg/exp/types/staging/const.go @@ -0,0 +1,646 @@ +// 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<= 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 +} diff --git a/src/pkg/exp/types/staging/errors.go b/src/pkg/exp/types/staging/errors.go new file mode 100644 index 0000000000..39799d0479 --- /dev/null +++ b/src/pkg/exp/types/staging/errors.go @@ -0,0 +1,298 @@ +// 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, "", 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("") + + 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, "", 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, "", t) + } +} diff --git a/src/pkg/exp/types/staging/exprstring.go b/src/pkg/exp/types/staging/exprstring.go deleted file mode 100644 index d3638d83f4..0000000000 --- a/src/pkg/exp/types/staging/exprstring.go +++ /dev/null @@ -1,98 +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 - -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, "", x) - } -} diff --git a/src/pkg/exp/types/staging/operand.go b/src/pkg/exp/types/staging/operand.go new file mode 100644 index 0000000000..f2440c2999 --- /dev/null +++ b/src/pkg/exp/types/staging/operand.go @@ -0,0 +1,201 @@ +// 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 +} diff --git a/src/pkg/exp/types/staging/predicates.go b/src/pkg/exp/types/staging/predicates.go index 7f0c2da68c..35fcf858b6 100644 --- a/src/pkg/exp/types/staging/predicates.go +++ b/src/pkg/exp/types/staging/predicates.go @@ -79,23 +79,6 @@ func isComparable(typ Type) bool { 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 { @@ -208,3 +191,46 @@ func identicalTypes(a, b ObjList) bool { } 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 +} diff --git a/src/pkg/exp/types/staging/stubs.go b/src/pkg/exp/types/staging/stubs.go new file mode 100644 index 0000000000..224d71afb9 --- /dev/null +++ b/src/pkg/exp/types/staging/stubs.go @@ -0,0 +1,55 @@ +// 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() +} diff --git a/src/pkg/exp/types/staging/types.go b/src/pkg/exp/types/staging/types.go index 2185d0c464..28c20be0bd 100644 --- a/src/pkg/exp/types/staging/types.go +++ b/src/pkg/exp/types/staging/types.go @@ -29,8 +29,7 @@ import ( // 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. @@ -101,6 +100,7 @@ type Basic struct { implementsType Kind BasicKind Info BasicInfo + Size int64 // > 0 if valid Name string } @@ -188,10 +188,11 @@ const ( // 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{...}. diff --git a/src/pkg/exp/types/staging/typestring.go b/src/pkg/exp/types/staging/typestring.go deleted file mode 100644 index 6a79165aa9..0000000000 --- a/src/pkg/exp/types/staging/typestring.go +++ /dev/null @@ -1,148 +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 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("") - - 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, "", 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, "", t) - } -} diff --git a/src/pkg/exp/types/staging/universe.go b/src/pkg/exp/types/staging/universe.go index 64ccfefe6b..bb8b6a2bda 100644 --- a/src/pkg/exp/types/staging/universe.go +++ b/src/pkg/exp/types/staging/universe.go @@ -19,39 +19,39 @@ var ( // Predeclared types, indexed by BasicKind. var Typ = [...]*Basic{ - Invalid: {aType, Invalid, 0, "invalid type"}, + Invalid: {aType, Invalid, 0, 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"}, + 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, "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"}, + 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 { @@ -61,30 +61,30 @@ 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, _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}, - {aType, _Offsetof, "Offsetof", 1, false}, - {aType, _Sizeof, "Sizeof", 1, false}, + {aType, _Alignof, "Alignof", 1, false, false}, + {aType, _Offsetof, "Offsetof", 1, false, false}, + {aType, _Sizeof, "Sizeof", 1, false, false}, } // commonly used types