blob: 7428f8f99500a78a03a8e8798237095637e4e9ed [file] [log] [blame]
// Copyright 2014 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 (
"go/ast"
"go/constant"
"go/token"
)
func (check *Checker) reportAltDecl(obj Object) {
if pos := obj.Pos(); pos.IsValid() {
// We use "other" rather than "previous" here because
// the first declaration seen may not be textually
// earlier in the source.
check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
}
}
func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token.Pos) {
// spec: "The blank identifier, represented by the underscore
// character _, may be used in a declaration like any other
// identifier but the declaration does not introduce a new
// binding."
if obj.Name() != "_" {
if alt := scope.Insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
return
}
obj.setScopePos(pos)
}
if id != nil {
check.recordDef(id, obj)
}
}
// objDecl type-checks the declaration of obj in its respective (file) context.
// See check.typ for the details on def and path.
func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
if obj.Type() != nil {
return // already checked - nothing to do
}
if trace {
check.trace(obj.Pos(), "-- declaring %s", obj.Name())
check.indent++
defer func() {
check.indent--
check.trace(obj.Pos(), "=> %s", obj)
}()
}
d := check.objMap[obj]
if d == nil {
check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
unreachable()
}
// save/restore current context and setup object context
defer func(ctxt context) {
check.context = ctxt
}(check.context)
check.context = context{
scope: d.file,
}
// Const and var declarations must not have initialization
// cycles. We track them by remembering the current declaration
// in check.decl. Initialization expressions depending on other
// consts, vars, or functions, add dependencies to the current
// check.decl.
switch obj := obj.(type) {
case *Const:
check.decl = d // new package-level const decl
check.constDecl(obj, d.typ, d.init)
case *Var:
check.decl = d // new package-level var decl
check.varDecl(obj, d.lhs, d.typ, d.init)
case *TypeName:
// invalid recursive types are detected via path
check.typeDecl(obj, d.typ, def, path, d.alias)
case *Func:
// functions may be recursive - no need to track dependencies
check.funcDecl(obj, d)
default:
unreachable()
}
}
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
assert(obj.typ == nil)
if obj.visited {
obj.typ = Typ[Invalid]
return
}
obj.visited = true
// use the correct value of iota
assert(check.iota == nil)
check.iota = obj.val
defer func() { check.iota = nil }()
// provide valid constant value under all circumstances
obj.val = constant.MakeUnknown()
// determine type, if any
if typ != nil {
t := check.typ(typ)
if !isConstType(t) {
check.errorf(typ.Pos(), "invalid constant type %s", t)
obj.typ = Typ[Invalid]
return
}
obj.typ = t
}
// check initialization
var x operand
if init != nil {
check.expr(&x, init)
}
check.initConst(obj, &x)
}
func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
assert(obj.typ == nil)
if obj.visited {
obj.typ = Typ[Invalid]
return
}
obj.visited = true
// var declarations cannot use iota
assert(check.iota == nil)
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ)
// We cannot spread the type to all lhs variables if there
// are more than one since that would mark them as checked
// (see Checker.objDecl) and the assignment of init exprs,
// if any, would not be checked.
//
// TODO(gri) If we have no init expr, we should distribute
// a given type otherwise we need to re-evalate the type
// expr for each lhs variable, leading to duplicate work.
}
// check initialization
if init == nil {
if typ == nil {
// error reported before by arityMatch
obj.typ = Typ[Invalid]
}
return
}
if lhs == nil || len(lhs) == 1 {
assert(lhs == nil || lhs[0] == obj)
var x operand
check.expr(&x, init)
check.initVar(obj, &x, "variable declaration")
return
}
if debug {
// obj must be one of lhs
found := false
for _, lhs := range lhs {
if obj == lhs {
found = true
break
}
}
if !found {
panic("inconsistent lhs")
}
}
// We have multiple variables on the lhs and one init expr.
// Make sure all variables have been given the same type if
// one was specified, otherwise they assume the type of the
// init expression values (was issue #15755).
if typ != nil {
for _, lhs := range lhs {
lhs.typ = obj.typ
}
}
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
}
// underlying returns the underlying type of typ; possibly by following
// forward chains of named types. Such chains only exist while named types
// are incomplete.
func underlying(typ Type) Type {
for {
n, _ := typ.(*Named)
if n == nil {
break
}
typ = n.underlying
}
return typ
}
func (n *Named) setUnderlying(typ Type) {
if n != nil {
n.underlying = typ
}
}
func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName, alias bool) {
assert(obj.typ == nil)
// type declarations cannot use iota
assert(check.iota == nil)
if alias {
obj.typ = Typ[Invalid]
obj.typ = check.typExpr(typ, nil, append(path, obj))
} else {
named := &Named{obj: obj}
def.setUnderlying(named)
obj.typ = named // make sure recursive type declarations terminate
// determine underlying type of named
check.typExpr(typ, named, append(path, obj))
// The underlying type of named may be itself a named type that is
// incomplete:
//
// type (
// A B
// B *C
// C A
// )
//
// The type of C is the (named) type of A which is incomplete,
// and which has as its underlying type the named type B.
// Determine the (final, unnamed) underlying type by resolving
// any forward chain (they always end in an unnamed type).
named.underlying = underlying(named.underlying)
}
// check and add associated methods
// TODO(gri) It's easy to create pathological cases where the
// current approach is incorrect: In general we need to know
// and add all methods _before_ type-checking the type.
// See https://play.golang.org/p/WMpE0q2wK8
check.addMethodDecls(obj)
}
func (check *Checker) addMethodDecls(obj *TypeName) {
// get associated methods
methods := check.methods[obj.name]
if len(methods) == 0 {
return // no methods
}
delete(check.methods, obj.name)
// use an objset to check for name conflicts
var mset objset
// spec: "If the base type is a struct type, the non-blank method
// and field names must be distinct."
base, _ := obj.typ.(*Named) // nil if receiver base type is type alias
if base != nil {
if t, _ := base.underlying.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
assert(mset.insert(fld) == nil)
}
}
}
// Checker.Files may be called multiple times; additional package files
// may add methods to already type-checked types. Add pre-existing methods
// so that we can detect redeclarations.
for _, m := range base.methods {
assert(m.name != "_")
assert(mset.insert(m) == nil)
}
}
// type-check methods
for _, m := range methods {
// spec: "For a base type, the non-blank names of methods bound
// to it must be unique."
if m.name != "_" {
if alt := mset.insert(m); alt != nil {
switch alt.(type) {
case *Var:
check.errorf(m.pos, "field and method with the same name %s", m.name)
case *Func:
check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
default:
unreachable()
}
check.reportAltDecl(alt)
continue
}
}
// type-check
check.objDecl(m, nil, nil)
// methods with blank _ names cannot be found - don't keep them
if base != nil && m.name != "_" {
base.methods = append(base.methods, m)
}
}
}
func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
assert(obj.typ == nil)
// func declarations cannot use iota
assert(check.iota == nil)
sig := new(Signature)
obj.typ = sig // guard against cycles
fdecl := decl.fdecl
check.funcType(sig, fdecl.Recv, fdecl.Type)
if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
// ok to continue
}
// function body must be type-checked after global declarations
// (functions implemented elsewhere have no body)
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
check.later(obj.name, decl, sig, fdecl.Body)
}
}
func (check *Checker) declStmt(decl ast.Decl) {
pkg := check.pkg
switch d := decl.(type) {
case *ast.BadDecl:
// ignore
case *ast.GenDecl:
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
for iota, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ValueSpec:
switch d.Tok {
case token.CONST:
// determine which init exprs to use
switch {
case s.Type != nil || len(s.Values) > 0:
last = s
case last == nil:
last = new(ast.ValueSpec) // make sure last exists
}
// declare all constants
lhs := make([]*Const, len(s.Names))
for i, name := range s.Names {
obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
lhs[i] = obj
var init ast.Expr
if i < len(last.Values) {
init = last.Values[i]
}
check.constDecl(obj, last.Type, init)
}
check.arityMatch(s, last)
// spec: "The scope of a constant or variable identifier declared
// inside a function begins at the end of the ConstSpec or VarSpec
// (ShortVarDecl for short variable declarations) and ends at the
// end of the innermost containing block."
scopePos := s.End()
for i, name := range s.Names {
check.declare(check.scope, name, lhs[i], scopePos)
}
case token.VAR:
lhs0 := make([]*Var, len(s.Names))
for i, name := range s.Names {
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
}
// initialize all variables
for i, obj := range lhs0 {
var lhs []*Var
var init ast.Expr
switch len(s.Values) {
case len(s.Names):
// lhs and rhs match
init = s.Values[i]
case 1:
// rhs is expected to be a multi-valued expression
lhs = lhs0
init = s.Values[0]
default:
if i < len(s.Values) {
init = s.Values[i]
}
}
check.varDecl(obj, lhs, s.Type, init)
if len(s.Values) == 1 {
// If we have a single lhs variable we are done either way.
// If we have a single rhs expression, it must be a multi-
// valued expression, in which case handling the first lhs
// variable will cause all lhs variables to have a type
// assigned, and we are done as well.
if debug {
for _, obj := range lhs0 {
assert(obj.typ != nil)
}
}
break
}
}
check.arityMatch(s, nil)
// declare all variables
// (only at this point are the variable scopes (parents) set)
scopePos := s.End() // see constant declarations
for i, name := range s.Names {
// see constant declarations
check.declare(check.scope, name, lhs0[i], scopePos)
}
default:
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
}
case *ast.TypeSpec:
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
// spec: "The scope of a type identifier declared inside a function
// begins at the identifier in the TypeSpec and ends at the end of
// the innermost containing block."
scopePos := s.Name.Pos()
check.declare(check.scope, s.Name, obj, scopePos)
check.typeDecl(obj, s.Type, nil, nil, s.Assign.IsValid())
default:
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
}
}
default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
}