1
0
mirror of https://github.com/golang/go synced 2024-10-03 15:21:22 -06:00

exp/types/staging: operands, constants, and error handling

More pieces of the typechecker code:

- Operands are temporary objects representing an expressions's
type and value (for constants). An operand is the equivalent of
an "attribute" in attribute grammars except that it's not stored
but only passed around during type checking.

- Constant operations are implemented in const.go. Constants are
represented as bool (booleans), int64 and *big.Int (integers),
*big.Rat (floats), complex (complex numbers), and string (strings).

- Error reporting is consolidated in errors.go. Only the first
dozen of lines is new code, the rest of the file contains the
exprString and typeString functions formerly in two separate
files (which have been removed).

This is a replacement CL for 6492101 (which was created without
proper use of hg).

R=rsc, r
CC=golang-dev
https://golang.org/cl/6500114
This commit is contained in:
Robert Griesemer 2012-09-25 17:38:22 -07:00
parent 1b6d4b5c0a
commit f5483fb801
10 changed files with 1620 additions and 317 deletions

View File

@ -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()
}

View File

@ -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<<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
}

View File

@ -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, "<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)
}
}

View File

@ -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, "<expr %T>", x)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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{...}.

View File

@ -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("<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)
}
}

View File

@ -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