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.Namesyntax.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()最后就会返回对应的类型数据结构,从而成功完成类型检查,该函数是类型检查过程中编译器创建各种类型结构的核心。

最后更新于