4.5.3-1.3b 类型表达式的类型检查
Go 在语法分析中使用表达式(Expr)表示类型值,例如对于如下类型申明:
type T struct {
name string
}
编译器会为
struct {}
这块代码创建表达式syntax.StructType
. 而对于下列申明: var pipe chan int
代表类型的
chan int
对应的是表达式syntax.ChanType
.对类型表达式进行类型检查的入口函数是
check.typ()
, 核心逻辑封装在方法check.typInternal()
中,定义在文件GCROOT/compile/internal/types2/typexpr.go
内,其代码结构如下: func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
switch e := e0.(type) {
case *syntax.BadExpr:
// ignore - error reported before
case *syntax.Name:
var x operand
check.ident(&x, e, def, true)
switch x.mode {
case typexpr:
typ := x.typ
def.setUnderlying(typ)
return typ
case invalid:
// ignore - error reported before
case novalue:
check.errorf(&x, "%s used as type", &x)
default:
check.errorf(&x, "%s is not a type", &x)
}
case *syntax.SelectorExpr:
case *syntax.IndexExpr:
case *syntax.ParenExpr:
case *syntax.ArrayType:
case *syntax.SliceType:
case *syntax.DotsType:
case *syntax.StructType:
case *syntax.Operation:
case *syntax.FuncType:
case *syntax.InterfaceType:
case *syntax.MapType:
case *syntax.ChanType:
typ := new(Chan)
def.setUnderlying(typ)
dir := SendRecv
switch e.Dir {
case 0:
// nothing to do
case syntax.SendOnly:
dir = SendOnly
case syntax.RecvOnly:
dir = RecvOnly
default:
check.errorf(e, invalidAST+"unknown channel direction %d", e.Dir)
// ok to continue
}
typ.dir = dir
typ.elem = check.varType(e.Elem)
return typ
default:
check.errorf(e0, "%s is not a type", e0)
check.use(e0)
}
typ := Typ[Invalid]
def.setUnderlying(typ)
return typ
}
通过方法签名可知:该方法通过一个表达式推导出一个类型。其中每个 case 都对应一个类型表达式,这里只留下
syntax.Name
与syntax.ChanType
进行演示。check.typInternal()
采用递归方式对表达式进行类型检查,例如对于syntax.ChanType
, 最后调用了check.varType()
对 channel 的元素类型进行检查,而后者最终又会回到check.typInternal()
中来。递归算法都是基于Divide-and-Conquer的思路来解决问题的,对于任何递归函数的分析,都需要牢牢把握两方面的信息: - 1.base-case 是什么?即何时遇到原子问题终止递归
- 2.问题如何分解?即一个大问题如何被拆分成更小的问题
对于
check.typInternal()
而言,问题分解就是上面方法中每个 case 的处理逻辑;而 base-case 对应的就是语言的基础类型,当类型检查遇到int32
, bool
, string
等类型时,此时对应的是syntax.Name
这个 case, 接下来编译器会通过check.ident()
方法对该标识符(identifier)进行类型检查,而在全局作用域的初始化过程中,编译器已经将所有基础类型注册好了,所以check.ident()
方法会成功完成符号解析并拿到对应的类型结构,如果是int32
, 则会得到一个TypeName
的 Object 对象,其类型是Basic{kind: Int32, info: IsInteger, name: "int32"}
.如果类型表达式的所有子结构都没有问题,那么
check.typInternal()
最后就会返回对应的类型数据结构,从而成功完成类型检查,该函数是类型检查过程中编译器创建各种类型结构的核心。