4.4.5 类型检查器
与类型检查器相关的重要数据结构有三个,本节将分析其代码
4.4.5.1. Config
Config
用来配置类型检查器,定义在$GCROOT/compile/internal/types2/api.go
中:
type Config struct {
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
// empty; an empty string indicates the latest language version.
// If the format is invalid, invoking the type checker will cause a
// panic.
GoVersion string
// If IgnoreFuncBodies is set, function bodies are not
// type-checked.
IgnoreFuncBodies bool
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
// declares an empty "C" package and errors are omitted for qualified
// identifiers referring to package C (which won't find an object).
// This feature is intended for the standard library cmd/api tool.
//
// Caution: Effects may be unpredictable due to follow-on errors.
// Do not use casually!
FakeImportC bool
// If IgnoreLabels is set, correct label use is not checked.
// TODO(gri) Consolidate label checking and remove this flag.
IgnoreLabels bool
// If CompilerErrorMessages is set, errors are reported using
// cmd/compile error strings to match $GOROOT/test errors.
// TODO(gri) Consolidate error messages and remove this flag.
CompilerErrorMessages bool
// If go115UsesCgo is set, the type checker expects the
// _cgo_gotypes.go file generated by running cmd/cgo to be
// provided as a package source file. Qualified identifiers
// referring to package C will be resolved to cgo-provided
// declarations within _cgo_gotypes.go.
//
// It is an error to set both FakeImportC and go115UsesCgo.
go115UsesCgo bool
// If Trace is set, a debug trace is printed to stdout.
Trace bool
// If Error != nil, it is called with each error found
// during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types
// involved in an invalid recursive type declaration) have
// error strings that start with a '\t' character.
// If Error == nil, type-checking stops with the first
// error found.
Error func(err error)
// An importer is used to import packages referred to from
// import declarations.
// If the installed importer implements ImporterFrom, the type
// checker calls ImportFrom instead of Import.
// The type checker reports an error if an importer is needed
// but none was installed.
Importer Importer
// If Sizes != nil, it provides the sizing functions for package unsafe.
// Otherwise SizesFor("gc", "amd64") is used instead.
Sizes Sizes
// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
}
其中各个字段的注释写得非常详细,基本都是一些开关属性。比较重要的是Importer
以及Sizes
, 前者是包加载器,而后者负责处理各类型的对齐问题。
4.4.5.2. Info
Info
用来存放类型检查器的检查结果,定义在$GCROOT/compile/internal/types2/api.go
中:
// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
// Types maps expressions to their types, and for constant
// expressions, also their values. Invalid expressions are
// omitted.
//
// For (possibly parenthesized) identifiers denoting built-in
// functions, the recorded signatures are call-site specific:
// if the call result is not a constant, the recorded type is
// an argument-specific signature. Otherwise, the recorded type
// is invalid.
//
// The Types map does not record the type of every identifier,
// only those that appear where an arbitrary expression is
// permitted. For instance, the identifier f in a selector
// expression x.f is found only in the Selections map, the
// identifier z in a variable declaration 'var z int' is found
// only in the Defs map, and identifiers denoting packages in
// qualified identifiers are collected in the Uses map.
Types map[syntax.Expr]TypeAndValue
// Inferred maps calls of parameterized functions that use
// type inference to the inferred type arguments and signature
// of the function called. The recorded "call" expression may be
// an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
Inferred map[syntax.Expr]Inferred
// Defs maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
// For identifiers that do not denote objects (e.g., the package name
// in package clauses, or symbolic variables t in t := x.(type) of
// type switch headers), the corresponding objects are nil.
//
// For an embedded field, Defs returns the field *Var it defines.
//
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
Defs map[*syntax.Name]Object
// Uses maps identifiers to the objects they denote.
//
// For an embedded field, Uses returns the *TypeName it denotes.
//
// Invariant: Uses[id].Pos() != id.Pos()
Uses map[*syntax.Name]Object
// Implicits maps nodes to their implicitly declared objects, if any.
// The following node and object types may appear:
//
// node declared object
//
// *syntax.ImportDecl *PkgName for imports without renames
// *syntax.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *syntax.Field anonymous parameter *Var (incl. unnamed results)
//
Implicits map[syntax.Node]Object
// Selections maps selector expressions (excluding qualified identifiers)
// to their corresponding selections.
Selections map[*syntax.SelectorExpr]*Selection
// Scopes maps syntax.Nodes to the scopes they define. Package scopes are not
// associated with a specific node but with all files belonging to a package.
// Thus, the package scope can be found in the type-checked Package object.
// Scopes nest, with the Universe scope being the outermost scope, enclosing
// the package scope, which contains (one or more) files scopes, which enclose
// function scopes which in turn enclose statement and function literal scopes.
// Note that even though package-level functions are declared in the package
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
// The following node types may appear in Scopes:
//
// *syntax.File
// *syntax.FuncType
// *syntax.BlockStmt
// *syntax.IfStmt
// *syntax.SwitchStmt
// *syntax.CaseClause
// *syntax.CommClause
// *syntax.ForStmt
//
Scopes map[syntax.Node]*Scope
// InitOrder is the list of package-level initializers in the order in which
// they must be executed. Initializers referring to variables related by an
// initialization dependency appear in topological order, the others appear
// in source order. Variables without an initialization expression do not
// appear in this list.
InitOrder []*Initializer
}
Initializer
表示的是当前 package 的初始化顺序,其余的所有属性都是一个以 AST 节点为 key 的 map, 类型检查器会在类型检查的过程中收集对应的对象,Info 包含了所有类型检查的结果,其会在后续生成 IR Tree 时使用。其中涉及到的TypeAndValue
以及Inferred
都在本文件中定义。
4.4.5.3. Checker
类型检查的整体逻辑由Checker
驱动,其定义在文件$GCROOT/compile/internal/types2/check.go
中:
type Checker struct {
conf *Config
pkg *Package // 当前正在编译的包
*Info // 保存类型检查的结果
version version // 支持的语言版本,例如 go1.16
nextId uint64 // 泛型的结构 TypeParam 包含唯一 id, 通过该字段来自增生成
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
posMap map[*Interface][]syntax.Pos // maps interface types to lists of embedded interface positions
typMap map[string]*Named // maps an instantiated named type hash to a *Named type
pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
files []*syntax.File // list of package files
imports []*PkgName // list of imported packages
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
firstErr error // first error encountered
methods map[*TypeName][]*Func // 保存类型到其方法的映射
untyped map[syntax.Expr]exprInfo // map of expressions without final type
delayed []func() // stack of delayed action segments; segments are processed in FIFO order
finals []func() // list of final actions; processed at the end of type-checking the current set of files
objPath []Object // path of object dependencies during type inference (for cycle reporting)
// context within which the current object is type-checked
// (valid only for the duration of type-checking a specific object)
context // 用来存放当前正在进行类型检查对象的上下文
// debugging
indent int // indentation for tracing
}
Checker
定义了非常多的方法,几乎包含了所有的类型检查逻辑,而其属性封装了类型检查需要的各种数据结构,包括用于存放各种中间状态的属性,其中有几个属性还需要单独介绍一下:
declInfo
该结构用来封装 package-level 的顶级申明,与 AST 中的“申明(Declaration)”对应。其定义在文件$GCROOT/compile/internal/types2/resolver.go
中:
type declInfo struct {
file *Scope // scope of file containing this declaration
lhs []*Var // lhs of n:1 variable declarations, or nil
vtyp syntax.Expr // type, or nil (for const and var declarations only)
init syntax.Expr // init/orig expression, or nil (for const and var declarations only)
inherited bool // if set, the init expression is inherited from a previous constant declaration
tdecl *syntax.TypeDecl // type declaration, or nil
fdecl *syntax.FuncDecl // func declaration, or nil
// 用来保存当前 declInfo 所依赖的对象,用来构建初始化依赖图
deps map[Object]bool // lazily initialized
}
其内部封装了一个申明的各个语法树节点,类型检查主要围绕这个结构体展开。其中deps
属性用来保存当前 declInfo 所依赖的对象,用于构建初始化顺序。
delayed & finals
类型检查是分阶段进行的,有些模块的工作需要依赖其他部分的检查结果,这两个属性用来保存该类工作,所以其是两个函数列表。该属性在类型检查逻辑中会有使用。
最后更新于