# 4.4.5 类型检查器

## 4.4.5.1. Config

`Config`用来配置类型检查器，定义在`$GCROOT/compile/internal/types2/api.go`中：

```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`, 前者是[包加载器](/golang-bian-yi-qi-lei-xing-jian-cha/lei-xing-jian-cha-luo-ji.md)，而后者负责处理各类型的对齐问题。

## 4.4.5.2. Info

`Info`用来存放类型检查器的检查结果，定义在`$GCROOT/compile/internal/types2/api.go`中：

```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`中：

```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&#x20;

该结构用来封装 package-level 的顶级申明，与 AST 中的“申明（Declaration）”对应。其定义在文件`$GCROOT/compile/internal/types2/resolver.go`中：

```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 所依赖的对象，用于[构建初始化顺序](/golang-bian-yi-qi-lei-xing-jian-cha/4.5.31.4-gou-jian-chu-shi-hua-shun-xu.md)。

* delayed & finals

类型检查是分阶段进行的，有些模块的工作需要依赖其他部分的检查结果，这两个属性用来保存该类工作，所以其是两个函数列表。该属性在[类型检查逻辑](/golang-bian-yi-qi-lei-xing-jian-cha/4.5.31.3e-chu-li-delayed-dui-lie.md)中会有使用。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.4.5-lei-xing-jian-cha-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
