Commit f5483fb8 authored by Robert Griesemer's avatar Robert Griesemer

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
parent 1b6d4b5c
// 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()
}
This diff is collapsed.
......@@ -2,7 +2,7 @@
// 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.
// This file implements various error reporters.
package types
......@@ -10,8 +10,158 @@ 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
......
// 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)
}
}
// 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
}
......@@ -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
}
// 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()
}
......@@ -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{...}.
......
......@@ -19,39 +19,39 @@ var (
// Predeclared types, indexed by BasicKind.
var Typ = [...]*Basic{
Invalid: {aType, Invalid, 0, "invalid type"},
Bool: {aType, Bool, IsBoolean, "bool"},
Int: {aType, Int, IsInteger, "int"},
Int8: {aType, Int8, IsInteger, "int8"},
Int16: {aType, Int16, IsInteger, "int16"},
Int32: {aType, Int32, IsInteger, "int32"},
Int64: {aType, Int64, IsInteger, "int64"},
Uint: {aType, Uint, IsInteger | IsUnsigned, "uint"},
Uint8: {aType, Uint8, IsInteger | IsUnsigned, "uint8"},
Uint16: {aType, Uint16, IsInteger | IsUnsigned, "uint16"},
Uint32: {aType, Uint32, IsInteger | IsUnsigned, "uint32"},
Uint64: {aType, Uint64, IsInteger | IsUnsigned, "uint64"},
Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, "uintptr"},
Float32: {aType, Float32, IsFloat, "float32"},
Float64: {aType, Float64, IsFloat, "float64"},
Complex64: {aType, Complex64, IsComplex, "complex64"},
Complex128: {aType, Complex128, IsComplex, "complex128"},
String: {aType, String, IsString, "string"},
UnsafePointer: {aType, UnsafePointer, 0, "Pointer"},
UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, "untyped boolean"},
UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, "untyped integer"},
UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, "untyped rune"},
UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, "untyped float"},
UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
UntypedString: {aType, UntypedString, IsString | IsUntyped, "untyped string"},
UntypedNil: {aType, UntypedNil, IsUntyped, "untyped nil"},
Invalid: {aType, Invalid, 0, 0, "invalid type"},
Bool: {aType, Bool, IsBoolean, 1, "bool"},
Int: {aType, Int, IsInteger, 0, "int"},
Int8: {aType, Int8, IsInteger, 1, "int8"},
Int16: {aType, Int16, IsInteger, 2, "int16"},
Int32: {aType, Int32, IsInteger, 4, "int32"},
Int64: {aType, Int64, IsInteger, 8, "int64"},
Uint: {aType, Uint, IsInteger | IsUnsigned, 0, "uint"},
Uint8: {aType, Uint8, IsInteger | IsUnsigned, 1, "uint8"},
Uint16: {aType, Uint16, IsInteger | IsUnsigned, 2, "uint16"},
Uint32: {aType, Uint32, IsInteger | IsUnsigned, 4, "uint32"},
Uint64: {aType, Uint64, IsInteger | IsUnsigned, 8, "uint64"},
Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
Float32: {aType, Float32, IsFloat, 4, "float32"},
Float64: {aType, Float64, IsFloat, 8, "float64"},
Complex64: {aType, Complex64, IsComplex, 8, "complex64"},
Complex128: {aType, Complex128, IsComplex, 16, "complex128"},
String: {aType, String, IsString, 0, "string"},
UnsafePointer: {aType, UnsafePointer, 0, 0, "Pointer"},
UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
UntypedString: {aType, UntypedString, IsString | IsUntyped, 0, "untyped string"},
UntypedNil: {aType, UntypedNil, IsUntyped, 0, "untyped nil"},
}
var aliases = [...]*Basic{
{aType, Uint8, IsInteger | IsUnsigned, "byte"},
{aType, Rune, IsInteger, "rune"},
{aType, Byte, IsInteger | IsUnsigned, 1, "byte"},
{aType, Rune, IsInteger, 4, "rune"},
}
var predeclaredConstants = [...]*struct {
......@@ -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, _Alignof, "Alignof", 1, false},
{aType, _Offsetof, "Offsetof", 1, false},
{aType, _Sizeof, "Sizeof", 1, false},
{aType, _Append, "append", 1, true, false},
{aType, _Cap, "cap", 1, false, false},
{aType, _Close, "close", 1, false, true},
{aType, _Complex, "complex", 2, false, false},
{aType, _Copy, "copy", 2, false, true},
{aType, _Delete, "delete", 2, false, true},
{aType, _Imag, "imag", 1, false, false},
{aType, _Len, "len", 1, false, false},
{aType, _Make, "make", 1, true, false},
{aType, _New, "new", 1, false, false},
{aType, _Panic, "panic", 1, false, true},
{aType, _Print, "print", 1, true, true},
{aType, _Println, "println", 1, true, true},
{aType, _Real, "real", 1, false, false},
{aType, _Recover, "recover", 0, false, true},
{aType, _Alignof, "Alignof", 1, false, false},
{aType, _Offsetof, "Offsetof", 1, false, false},
{aType, _Sizeof, "Sizeof", 1, false, false},
}
// commonly used types
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment