mirror of
https://github.com/golang/go
synced 2024-11-20 09:04:44 -07: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:
parent
1b6d4b5c0a
commit
f5483fb801
322
src/pkg/exp/types/staging/check.go
Normal file
322
src/pkg/exp/types/staging/check.go
Normal 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()
|
||||||
|
}
|
646
src/pkg/exp/types/staging/const.go
Normal file
646
src/pkg/exp/types/staging/const.go
Normal 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
|
||||||
|
}
|
298
src/pkg/exp/types/staging/errors.go
Normal file
298
src/pkg/exp/types/staging/errors.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
201
src/pkg/exp/types/staging/operand.go
Normal file
201
src/pkg/exp/types/staging/operand.go
Normal 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
|
||||||
|
}
|
@ -79,23 +79,6 @@ func isComparable(typ Type) bool {
|
|||||||
return false
|
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.
|
// identical returns true if x and y are identical.
|
||||||
func isIdentical(x, y Type) bool {
|
func isIdentical(x, y Type) bool {
|
||||||
if x == y {
|
if x == y {
|
||||||
@ -208,3 +191,46 @@ func identicalTypes(a, b ObjList) bool {
|
|||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
55
src/pkg/exp/types/staging/stubs.go
Normal file
55
src/pkg/exp/types/staging/stubs.go
Normal 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()
|
||||||
|
}
|
@ -29,8 +29,7 @@ import (
|
|||||||
// the expression appears in the AST.
|
// the expression appears in the AST.
|
||||||
//
|
//
|
||||||
func Check(fset *token.FileSet, pkg *ast.Package, types map[ast.Expr]Type) error {
|
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 check(fset, pkg, types)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All types implement the Type interface.
|
// All types implement the Type interface.
|
||||||
@ -101,6 +100,7 @@ type Basic struct {
|
|||||||
implementsType
|
implementsType
|
||||||
Kind BasicKind
|
Kind BasicKind
|
||||||
Info BasicInfo
|
Info BasicInfo
|
||||||
|
Size int64 // > 0 if valid
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,10 +188,11 @@ const (
|
|||||||
// A builtin represents the type of a built-in function.
|
// A builtin represents the type of a built-in function.
|
||||||
type builtin struct {
|
type builtin struct {
|
||||||
implementsType
|
implementsType
|
||||||
id builtinId
|
id builtinId
|
||||||
name string
|
name string
|
||||||
nargs int // number of arguments (minimum if variadic)
|
nargs int // number of arguments (minimum if variadic)
|
||||||
isVariadic bool
|
isVariadic bool
|
||||||
|
isStatement bool // true if the built-in is valid as an expression statement
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Interface represents an interface type interface{...}.
|
// An Interface represents an interface type interface{...}.
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,39 +19,39 @@ var (
|
|||||||
|
|
||||||
// Predeclared types, indexed by BasicKind.
|
// Predeclared types, indexed by BasicKind.
|
||||||
var Typ = [...]*Basic{
|
var Typ = [...]*Basic{
|
||||||
Invalid: {aType, Invalid, 0, "invalid type"},
|
Invalid: {aType, Invalid, 0, 0, "invalid type"},
|
||||||
|
|
||||||
Bool: {aType, Bool, IsBoolean, "bool"},
|
Bool: {aType, Bool, IsBoolean, 1, "bool"},
|
||||||
Int: {aType, Int, IsInteger, "int"},
|
Int: {aType, Int, IsInteger, 0, "int"},
|
||||||
Int8: {aType, Int8, IsInteger, "int8"},
|
Int8: {aType, Int8, IsInteger, 1, "int8"},
|
||||||
Int16: {aType, Int16, IsInteger, "int16"},
|
Int16: {aType, Int16, IsInteger, 2, "int16"},
|
||||||
Int32: {aType, Int32, IsInteger, "int32"},
|
Int32: {aType, Int32, IsInteger, 4, "int32"},
|
||||||
Int64: {aType, Int64, IsInteger, "int64"},
|
Int64: {aType, Int64, IsInteger, 8, "int64"},
|
||||||
Uint: {aType, Uint, IsInteger | IsUnsigned, "uint"},
|
Uint: {aType, Uint, IsInteger | IsUnsigned, 0, "uint"},
|
||||||
Uint8: {aType, Uint8, IsInteger | IsUnsigned, "uint8"},
|
Uint8: {aType, Uint8, IsInteger | IsUnsigned, 1, "uint8"},
|
||||||
Uint16: {aType, Uint16, IsInteger | IsUnsigned, "uint16"},
|
Uint16: {aType, Uint16, IsInteger | IsUnsigned, 2, "uint16"},
|
||||||
Uint32: {aType, Uint32, IsInteger | IsUnsigned, "uint32"},
|
Uint32: {aType, Uint32, IsInteger | IsUnsigned, 4, "uint32"},
|
||||||
Uint64: {aType, Uint64, IsInteger | IsUnsigned, "uint64"},
|
Uint64: {aType, Uint64, IsInteger | IsUnsigned, 8, "uint64"},
|
||||||
Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, "uintptr"},
|
Uintptr: {aType, Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
|
||||||
Float32: {aType, Float32, IsFloat, "float32"},
|
Float32: {aType, Float32, IsFloat, 4, "float32"},
|
||||||
Float64: {aType, Float64, IsFloat, "float64"},
|
Float64: {aType, Float64, IsFloat, 8, "float64"},
|
||||||
Complex64: {aType, Complex64, IsComplex, "complex64"},
|
Complex64: {aType, Complex64, IsComplex, 8, "complex64"},
|
||||||
Complex128: {aType, Complex128, IsComplex, "complex128"},
|
Complex128: {aType, Complex128, IsComplex, 16, "complex128"},
|
||||||
String: {aType, String, IsString, "string"},
|
String: {aType, String, IsString, 0, "string"},
|
||||||
UnsafePointer: {aType, UnsafePointer, 0, "Pointer"},
|
UnsafePointer: {aType, UnsafePointer, 0, 0, "Pointer"},
|
||||||
|
|
||||||
UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, "untyped boolean"},
|
UntypedBool: {aType, UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
|
||||||
UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, "untyped integer"},
|
UntypedInt: {aType, UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
|
||||||
UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, "untyped rune"},
|
UntypedRune: {aType, UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
|
||||||
UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, "untyped float"},
|
UntypedFloat: {aType, UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
|
||||||
UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
|
UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
|
||||||
UntypedString: {aType, UntypedString, IsString | IsUntyped, "untyped string"},
|
UntypedString: {aType, UntypedString, IsString | IsUntyped, 0, "untyped string"},
|
||||||
UntypedNil: {aType, UntypedNil, IsUntyped, "untyped nil"},
|
UntypedNil: {aType, UntypedNil, IsUntyped, 0, "untyped nil"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var aliases = [...]*Basic{
|
var aliases = [...]*Basic{
|
||||||
{aType, Uint8, IsInteger | IsUnsigned, "byte"},
|
{aType, Byte, IsInteger | IsUnsigned, 1, "byte"},
|
||||||
{aType, Rune, IsInteger, "rune"},
|
{aType, Rune, IsInteger, 4, "rune"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var predeclaredConstants = [...]*struct {
|
var predeclaredConstants = [...]*struct {
|
||||||
@ -61,30 +61,30 @@ var predeclaredConstants = [...]*struct {
|
|||||||
}{
|
}{
|
||||||
{UntypedBool, "true", true},
|
{UntypedBool, "true", true},
|
||||||
{UntypedBool, "false", false},
|
{UntypedBool, "false", false},
|
||||||
{UntypedInt, "iota", int64(0)},
|
{UntypedInt, "iota", zeroConst},
|
||||||
{UntypedNil, "nil", nil},
|
{UntypedNil, "nil", nilConst},
|
||||||
}
|
}
|
||||||
|
|
||||||
var predeclaredFunctions = [...]*builtin{
|
var predeclaredFunctions = [...]*builtin{
|
||||||
{aType, _Append, "append", 1, true},
|
{aType, _Append, "append", 1, true, false},
|
||||||
{aType, _Cap, "cap", 1, false},
|
{aType, _Cap, "cap", 1, false, false},
|
||||||
{aType, _Close, "close", 1, false},
|
{aType, _Close, "close", 1, false, true},
|
||||||
{aType, _Complex, "complex", 2, false},
|
{aType, _Complex, "complex", 2, false, false},
|
||||||
{aType, _Copy, "copy", 2, false},
|
{aType, _Copy, "copy", 2, false, true},
|
||||||
{aType, _Delete, "delete", 2, false},
|
{aType, _Delete, "delete", 2, false, true},
|
||||||
{aType, _Imag, "imag", 1, false},
|
{aType, _Imag, "imag", 1, false, false},
|
||||||
{aType, _Len, "len", 1, false},
|
{aType, _Len, "len", 1, false, false},
|
||||||
{aType, _Make, "make", 1, true},
|
{aType, _Make, "make", 1, true, false},
|
||||||
{aType, _New, "new", 1, false},
|
{aType, _New, "new", 1, false, false},
|
||||||
{aType, _Panic, "panic", 1, false},
|
{aType, _Panic, "panic", 1, false, true},
|
||||||
{aType, _Print, "print", 1, true},
|
{aType, _Print, "print", 1, true, true},
|
||||||
{aType, _Println, "println", 1, true},
|
{aType, _Println, "println", 1, true, true},
|
||||||
{aType, _Real, "real", 1, false},
|
{aType, _Real, "real", 1, false, false},
|
||||||
{aType, _Recover, "recover", 0, false},
|
{aType, _Recover, "recover", 0, false, true},
|
||||||
|
|
||||||
{aType, _Alignof, "Alignof", 1, false},
|
{aType, _Alignof, "Alignof", 1, false, false},
|
||||||
{aType, _Offsetof, "Offsetof", 1, false},
|
{aType, _Offsetof, "Offsetof", 1, false, false},
|
||||||
{aType, _Sizeof, "Sizeof", 1, false},
|
{aType, _Sizeof, "Sizeof", 1, false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
// commonly used types
|
// commonly used types
|
||||||
|
Loading…
Reference in New Issue
Block a user