Golang 编译器代码浅析
  • 0. Golang 编译器代码浅析
  • 1. golang 编译器 - 前言
    • 1.1 编译器简介
    • 1.2 Golang 编译器
    • 1.3 Go 语言版本
    • 1.4 项目设置
    • 1.5 约定
    • 1.6 写作目的
  • 2. golang 编译器 - 词法分析
    • 2.1 简介
    • 2.2 代码结构
    • 2.3 处理字符
    • 2.4 扫描Token
    • 2.5 总结
  • 3.a 语法分析理论知识
    • 3A.1 语法分析简介
    • 3A.2 文法
    • 3A.3 语法解析
    • 3A.3.1 自顶向下(Top-Down)
    • 3A.3.2 自顶向下 - 递归下降
    • 3A.3.3 自顶向下 - LL(1)文法
    • 3A.3.4 自底向上(Bottom-Up)
    • 3A.3.5 自底向上 - LR(0)项集及SLR预测表
    • 3A.3.6 自底向上 - LR(1)、LALR
    • 3A.4 语法分析工具
    • 3A.5 总结
  • 3B. golang 编译器 - 语法分析
    • 3B.1 简介
    • 3B.2 代码结构
    • 3B.3 数据结构
    • 3B.4 构造语法树
    • 3B.5 Unit Test及AST可视化
  • 4. Golang 编译器 - 类型检查
    • 4.1 简介
    • 4.2 代码结构
    • 4.3 符号解析
    • 4.4.1 数据结构 - 作用域
    • 4.4.2 数据结构 - Package
    • 4.4.3 数据结构 - Object 对象
    • 4.4.4-1 类型数据结构 - 简介
    • 4.4.4-2 类型接口
    • 4.4.4-3 基础类型
    • 4.4.4-4 内置复合类型
    • 4.4.4-5 Struct 类型
    • 4.4.4-6 Interface 类型
    • 4.4.4-7 Named 类型
    • 4.4.4-8 Tuple 类型
    • 4.4.4-9 Sum 类型
    • 4.4.4-10 Function & Method 类型
    • 4.4.4-11 泛型类型
    • 4.4.4-12 类型的等价规则
    • 4.4.4-13 类型的比较规则
    • 4.4.4-14 总结
    • 4.4.5 类型检查器
    • 4.4.6 总结
    • 4.5.1 类型检查逻辑 - 包加载器
    • 4.5.2 类型检查逻辑 - 初始化
    • 4.5.2-1 全局作用域
    • 4.5.2-2 类型检查器
    • 4.5.3 类型检查逻辑 - 流程分析
    • 4.5.3-1.1 总体流程
    • 4.5.3-1.2 类型检查准备工作
    • 4.5.3-1.3 类型检查核心逻辑
    • 4.5.3-1.3a 总体介绍
    • 4.5.3-1.3b 类型表达式的类型检查
    • 4.5.3-1.3c 求值表达式的类型检查
    • 4.5.3-1.3d 类型兼容性检查
    • 4.5.3-1.3e 处理delayed队列
    • 4.5.3-1.4 构建初始化顺序
    • 4.5.3-1.5 总结
    • 4.5.3-2 特定问题分析
    • 4.5.3-2a 对象循环依赖检查
    • 4.5.3-2b 方法与属性查找
    • 4.5.3-2c Underlying Type
    • 4.6 如何测试
    • 4.7 总结
  • 5. Golang 编译器 - IR Tree
    • 5.1 简介
    • 5.2 代码结构
    • 5.3 数据结构
    • 5.4 处理逻辑
    • 5.5 编译日志
    • 5.6 Unit Test
    • 5.7 总结
  • 6. golang 编译器 - 初始化任务
    • 6.1 简介
    • 6.2 代码结构
    • 6.3 总体逻辑
    • 6.4 赋值语句
    • 6.5 编译日志
    • 6.6 Unit Test
    • 6.7 总结
  • 7. golang 编译器 - 清除无效代码
    • 7.1 简介
    • 7.2 处理逻辑
    • 7.3 Unit Test
  • 8. golang 编译器 - Inline
    • 8.1 简介
    • 8.2 Inline的问题
    • 8.3 代码结构
    • 8.4 处理逻辑
    • 8.4.1 遍历调用链
    • 8.4.2 内联判断
    • 8.4.3 内联操作
    • 8.4.4 编译日志
    • 8.4.5 Unit Test
    • 8.4.6 总结
  • 9. golang 编译器 - 逃逸分析
    • 9.1 什么是逃逸分析
    • 9.2 Go 的逃逸分析
    • 9.3 算法思路
    • 9.4 代码结构
    • 9.5 处理逻辑
    • 9.5.1总体逻辑
    • 9.5.2 数据结构
    • 9.5.3 构建数据流有向图
    • 9.5.4 逃逸分析
    • 9.6 编译日志
    • 9.7 Unit Test
    • 9.8 总结
  • 10. golang 编译器 - 函数编译及导出
    • 10.1 简介
    • 10.2 编译函数
    • 10.2.1 SSA
    • 10.2.2 ABI
    • 10.2.3 并发控制
    • 10.3 导出对象文件
    • 10.4 总结
  • 11. Golang 编译器 - 写在最后
由 GitBook 提供支持
在本页

这有帮助吗?

  1. 4. Golang 编译器 - 类型检查

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进行演示。

  1. base-case 是什么?即何时遇到原子问题终止递归

  2. 问题如何分解?即一个大问题如何被拆分成更小的问题

如果类型表达式的所有子结构都没有问题,那么check.typInternal()最后就会返回对应的类型数据结构,从而成功完成类型检查,该函数是类型检查过程中编译器创建各种类型结构的核心。

上一页4.5.3-1.3a 总体介绍下一页4.5.3-1.3c 求值表达式的类型检查

最后更新于3年前

这有帮助吗?

check.typInternal()采用递归方式对表达式进行类型检查,例如对于syntax.ChanType, 最后调用了check.varType()对 channel 的元素类型进行检查,而后者最终又会回到check.typInternal()中来。递归算法都是基于的思路来解决问题的,对于任何递归函数的分析,都需要牢牢把握两方面的信息:

对于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"}.

Divide-and-Conquer
全局作用域