4.3 符号解析

类型检查最重要的一件事情就是做符号解析,所谓符号解析,就是将程序中通过“名字”所引用的真实对象找出来。这里所说的“名字”,在词法分析阶段对应的 Token 类型是_Name, 而在语法阶段对应的表达式是Name,其定义如下:

// file: $GCROOT/compile/internal/syntax/nodes.go
type Name struct {
	Value string
	expr
}

名字所引用的对象就是语言中某个类型的具体实例,例如函数、变量等。例如下列代码涉及到的名字包括:A, main, a, name, fmt, Printf.

import "fmt"

type A struct{}

func main() {
	var a A
	name := "Golang"

	fmt.Printf("%v-%s\n", a, name)
}

go 的编译单位是 package, 即每次编译的源文件必须属于同一个包,所以涉及到的符号来源可以简单归为如下几类:

  1. 预定义符号,即内置符号,包括语言的内置类型、内置函数、泛型的内置 constraints 等

  2. 当前源文件所依赖的包,即通过 ~import~ 语句引用的各个 package

  3. 当前源文件自定义的符号,即定义的类型、变量等

为了完成符号解析,编译器需要构建一个符号表,其在编译的初始化阶段会注册第一部分的符号,在类型检查时导入第二部分的符号,并在实际类型检查过程中对第三部分的符号进行处理。除了符号注册,编译器还需要对符号的作用域进行管理,即处理好哪些符号是 public 的,哪些是 package local 的,哪些是 function local 的。为了管理好这些逻辑,编译器需要定义各种数据结构来抽象对应的概念,我们将在下一节详细分析类型检查时所涉及到的重要数据结构。

最后更新于