blob: acc098ee1e85afd52393477fa9ec5896b218d0cd [file] [log] [blame]
// Copyright 2013 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 type-checking of identifiers and type expressions.
package types
import (
"go/ast"
"go/token"
"strconv"
"code.google.com/p/go.tools/go/exact"
)
// ident type-checks identifier e and initializes x with the value or type of e.
// If an error occurred, x.mode is set to invalid.
// For the meaning of def and cycleOk, see check.typ, below.
//
func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) {
x.mode = invalid
x.expr = e
obj := check.topScope.LookupParent(e.Name)
if obj == nil {
if e.Name == "_" {
check.errorf(e.Pos(), "cannot use _ as value or type")
} else {
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
}
return
}
check.recordObject(e, obj)
typ := obj.Type()
if typ == nil {
// object not yet declared
if check.objMap == nil {
check.dump("%s: %s should have been declared (we are inside a function)", e.Pos(), e)
unreachable()
}
check.objDecl(obj, def, cycleOk)
typ = obj.Type()
}
assert(typ != nil)
switch obj := obj.(type) {
case *PkgName:
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
return
case *Const:
// The constant may be dot-imported. Mark it as used so that
// later we can determine if the corresponding dot-imported
// packages was used. Same applies for other objects, below.
// (This code is only used for dot-imports. Without them, we
// would only have to mark Vars.)
obj.used = true
if typ == Typ[Invalid] {
return
}
if obj == universeIota {
if check.iota == nil {
check.errorf(e.Pos(), "cannot use iota outside constant declaration")
return
}
x.val = check.iota
} else {
x.val = obj.val // may be nil if we don't know the constant value
}
x.mode = constant
case *TypeName:
obj.used = true
x.mode = typexpr
named, _ := typ.(*Named)
if !cycleOk && named != nil && !named.complete {
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
// maintain x.mode == typexpr despite error
typ = Typ[Invalid]
}
if def != nil {
def.underlying = typ
}
case *Var:
obj.used = true
x.mode = variable
case *Func:
obj.used = true
x.mode = value
case *Builtin:
obj.used = true // for built-ins defined by package unsafe
x.mode = builtin
x.id = obj.id
case *Nil:
// no need to "use" the nil object
x.mode = value
default:
unreachable()
}
x.typ = typ
}
// typ type-checks the type expression e and returns its type, or Typ[Invalid].
// If def != nil, e is the type specification for the named type def, declared
// in a type declaration, and def.underlying will be set to the type of e before
// any components of e are type-checked.
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
// yet completely set up.
//
func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type {
if trace {
check.trace(e.Pos(), "%s", e)
check.indent++
}
t := check.typ0(e, def, cycleOk)
assert(e != nil && t != nil && isTyped(t))
check.recordTypeAndValue(e, t, nil)
if trace {
check.indent--
check.trace(e.Pos(), "=> %s", t)
}
return t
}
// funcType type-checks a function or method type and returns its signature.
func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Named) *Signature {
sig := new(Signature)
if def != nil {
def.underlying = sig
}
scope := NewScope(check.topScope)
check.recordScope(ftyp, scope)
recv_, _ := check.collectParams(scope, recv, false)
params, isVariadic := check.collectParams(scope, ftyp.Params, true)
results, _ := check.collectParams(scope, ftyp.Results, false)
if len(recv_) > 0 {
// There must be exactly one receiver.
if len(recv_) > 1 {
check.invalidAST(recv_[1].Pos(), "method must have exactly one receiver")
// ok to continue
}
recv := recv_[0]
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
if t, _ := deref(recv.typ); t != Typ[Invalid] {
var err string
if T, _ := t.(*Named); T != nil {
// spec: "The type denoted by T is called the receiver base type; it must not
// be a pointer or interface type and it must be declared in the same package
// as the method."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
} else {
switch u := T.underlying.(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
err = "unsafe.Pointer"
}
case *Pointer, *Interface:
err = "pointer or interface type"
}
}
} else {
err = "basic or unnamed type"
}
if err != "" {
check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
// ok to continue
}
}
sig.recv = recv
}
sig.scope = scope
sig.params = NewTuple(params...)
sig.results = NewTuple(results...)
sig.isVariadic = isVariadic
return sig
}
// typ0 contains the core of type checking of types.
// Must only be called by typ.
//
func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type {
switch e := e.(type) {
case *ast.BadExpr:
// ignore - error reported before
case *ast.Ident:
var x operand
check.ident(&x, e, def, cycleOk)
switch x.mode {
case typexpr:
return x.typ
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
case *ast.SelectorExpr:
var x operand
check.selector(&x, e)
switch x.mode {
case typexpr:
return x.typ
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
case *ast.ParenExpr:
return check.typ(e.X, def, cycleOk)
case *ast.ArrayType:
if e.Len != nil {
var x operand
check.expr(&x, e.Len)
if x.mode != constant {
if x.mode != invalid {
check.errorf(x.pos(), "array length %s must be constant", &x)
}
break
}
if !x.isInteger() {
check.errorf(x.pos(), "array length %s must be integer", &x)
break
}
n, ok := exact.Int64Val(x.val)
if !ok || n < 0 {
check.errorf(x.pos(), "invalid array length %s", &x)
break
}
typ := new(Array)
if def != nil {
def.underlying = typ
}
typ.len = n
typ.elt = check.typ(e.Elt, nil, cycleOk)
return typ
} else {
typ := new(Slice)
if def != nil {
def.underlying = typ
}
typ.elt = check.typ(e.Elt, nil, true)
return typ
}
case *ast.StructType:
typ := new(Struct)
if def != nil {
def.underlying = typ
}
typ.fields, typ.tags = check.collectFields(e.Fields, cycleOk)
return typ
case *ast.StarExpr:
typ := new(Pointer)
if def != nil {
def.underlying = typ
}
typ.base = check.typ(e.X, nil, true)
return typ
case *ast.FuncType:
return check.funcType(nil, e, def)
case *ast.InterfaceType:
typ := new(Interface)
var recv Type = typ
if def != nil {
def.underlying = typ
recv = def // use named receiver type if available
}
typ.methods = check.collectMethods(recv, e.Methods, cycleOk)
return typ
case *ast.MapType:
typ := new(Map)
if def != nil {
def.underlying = typ
}
typ.key = check.typ(e.Key, nil, true)
typ.elt = check.typ(e.Value, nil, true)
// spec: "The comparison operators == and != must be fully defined
// for operands of the key type; thus the key type must not be a
// function, map, or slice."
if !isComparable(typ.key) {
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
// ok to continue
}
return typ
case *ast.ChanType:
typ := new(Chan)
if def != nil {
def.underlying = typ
}
typ.dir = e.Dir
typ.elt = check.typ(e.Value, nil, true)
return typ
default:
check.errorf(e.Pos(), "%s is not a type", e)
}
return Typ[Invalid]
}
// typeOrNil type-checks the type expression (or nil value) e
// and returns the typ of e, or nil.
// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
//
func (check *checker) typOrNil(e ast.Expr) Type {
var x operand
check.rawExpr(&x, e, nil)
switch x.mode {
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
case typexpr:
return x.typ
case value:
if x.isNil() {
return nil
}
fallthrough
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
return Typ[Invalid]
}
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
if list == nil {
return
}
for i, field := range list.List {
ftype := field.Type
if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt
if variadicOk && i == len(list.List)-1 {
isVariadic = true
} else {
check.invalidAST(field.Pos(), "... not permitted")
// ignore ... and continue
}
}
typ := check.typ(ftype, nil, true)
// the parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag
if len(field.Names) > 0 {
// named parameter
for _, name := range field.Names {
par := NewParam(name.Pos(), check.pkg, name.Name, typ)
check.declareObj(scope, name, par)
params = append(params, par)
}
} else {
// anonymous parameter
par := NewParam(ftype.Pos(), check.pkg, "", typ)
check.recordImplicit(field, par)
params = append(params, par)
}
}
// For a variadic function, change the last parameter's type from T to []T.
if isVariadic && len(params) > 0 {
last := params[len(params)-1]
last.typ = &Slice{elt: last.typ}
}
return
}
func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk bool) (methods []*Func) {
if list == nil {
return nil
}
var mset objset
for _, f := range list.List {
typ := check.typ(f.Type, nil, cycleOk)
// the parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag
if len(f.Names) > 0 {
// methods (the parser ensures that there's only one
// and we don't care if a constructed AST has more)
sig, _ := typ.(*Signature)
if sig == nil {
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
continue
}
sig.recv = NewVar(token.NoPos, check.pkg, "", recv)
for _, name := range f.Names {
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.declareFld(&mset, name, m)
methods = append(methods, m)
}
} else {
// embedded interface
switch t := typ.Underlying().(type) {
case nil:
// The underlying type is in the process of being defined
// but we need it in order to complete this type. For now
// complain with an "unimplemented" error. This requires
// a bit more work.
// TODO(gri) finish this.
check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type)
case *Interface:
for _, m := range t.methods {
check.declareFld(&mset, nil, m)
methods = append(methods, m)
}
default:
if t != Typ[Invalid] {
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
}
}
}
}
return
}
func (check *checker) tag(t *ast.BasicLit) string {
if t != nil {
if t.Kind == token.STRING {
if val, err := strconv.Unquote(t.Value); err == nil {
return val
}
}
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
}
return ""
}
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Var, tags []string) {
if list == nil {
return
}
var fset objset
var typ Type // current field typ
var tag string // current field tag
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
if tag != "" && tags == nil {
tags = make([]string, len(fields))
}
if tags != nil {
tags = append(tags, tag)
}
fld := NewField(pos, check.pkg, name, typ, anonymous)
check.declareFld(&fset, ident, fld)
fields = append(fields, fld)
}
for _, f := range list.List {
typ = check.typ(f.Type, nil, cycleOk)
tag = check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields
for _, name := range f.Names {
add(f, name, name.Name, false, name.Pos())
}
} else {
// anonymous field
pos := f.Type.Pos()
t, isPtr := deref(typ)
switch t := t.(type) {
case *Basic:
if t == Typ[Invalid] {
// error was reported before
continue
}
// unsafe.Pointer is treated like a regular pointer
if t.kind == UnsafePointer {
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
continue
}
add(f, nil, t.name, true, pos)
case *Named:
// spec: "An embedded type must be specified as a type name
// T or as a pointer to a non-interface type name *T, and T
// itself may not be a pointer type."
switch u := t.Underlying().(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
continue
}
case *Pointer:
check.errorf(pos, "anonymous field type cannot be a pointer")
continue
case *Interface:
if isPtr {
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
continue
}
}
add(f, nil, t.obj.name, true, pos)
default:
check.invalidAST(pos, "anonymous field type %s must be named", typ)
}
}
}
return
}