4.5.2-2 类型检查器

该部分对前文中关于类型检查器的几个数据结构以及包加载器进行初始化,代码在$GCROOT/compile/internal/noder/irgen.gocheck2中:

// check2 type checks a Go package using types2, and then generates IR
// using the results.
func check2(noders []*noder) {
	if base.SyntaxErrors() != 0 {
		base.ErrorExit()
	}

	// setup and syntax error reporting
	var m posMap
	files := make([]*syntax.File, len(noders))
	for i, p := range noders {
		m.join(&p.posMap)
		files[i] = p.file
	}

	// 初始化 Config
	conf := types2.Config{
		GoVersion:             base.Flag.Lang,
		IgnoreLabels:          true, // parser already checked via syntax.CheckBranches mode
		CompilerErrorMessages: true, // use error strings matching existing compiler errors
		Error: func(err error) {
			terr := err.(types2.Error)
			base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
		},
		Importer: &gcimports{ // 初始化包加载器
			packages: make(map[string]*types2.Package),
		},
		Sizes: &gcSizes{},
	}
	// 初始化 Info, Info 是用来保存所有类型检查结果的容器,且只有当对应属性不为 nil 时,才保存对应的内容。这里将所有属性都初始化了,所以我们需要记录下所有检查结果
	info := types2.Info{
		Types:      make(map[syntax.Expr]types2.TypeAndValue),
		Defs:       make(map[*syntax.Name]types2.Object),
		Uses:       make(map[*syntax.Name]types2.Object),
		Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
		Implicits:  make(map[syntax.Node]types2.Object),
		Scopes:     make(map[syntax.Node]*types2.Scope),
		Inferred:   make(map[syntax.Expr]types2.Inferred),
		// expand as needed
	}
	// 开始类型检查
	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
	files = nil // 释放内存

	// 如果类型检查发生错误,则编译器退出
	base.ExitIfErrors()
	if err != nil {
		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
	}
	if base.Flag.G < 2 { // 当前编译参数 -G, 如果当前想要完成后续操作,至少需要传入 -G=2
		os.Exit(0)
	}

	// 如果类型检查成功通过,则根据其结果生成 IR(Intermediate Representation), 后续章节会专门介绍
	g := irgen{
		target: typecheck.Target,
		self:   pkg,
		info:   &info, // info 包含了类型检查的结果,继续传递给 irgen 使用
		posMap: m,
		objs:   make(map[types2.Object]*ir.Name),
		typs:   make(map[types2.Type]*types.Type),
	}
	g.generate(noders)

	if base.Flag.G < 3 {
		os.Exit(0)
	}
}

Checker的初始化在文件$GCROOT/compile/internal/types2/api.goCheck方法内:

func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, pkg, info).Files(files)
}

该方法是类型检查的入口,从这里开始,编译器完成类型检查器的初始化,正式开始工作。

最后更新于