# 4.5.3-1.3c 求值表达式的类型检查

求值表达式是指程序中进行求值运算的代码块，例如算术运算、函数调用、访问数组或对象属性、类型转换等。

{% hint style="info" %}
求值表达式并不是一个特别严格、准确的术语，这里使用只是为了区分类型表达式，从最抽象的层面上来看，程序可以分为控制语句与计算单元，控制语句通过关键字来实现，例如 for, if 等用来控制程序的执行流，而 type, func 等用来告诉编译器需要创建新的类型；计算单元则用来达成程序的业务目的，这里所说的求值表达式属于计算单元，但也并不是所有的计算单元都会产生一个明确的返回值，例如一个函数可能没有返回值，调用函数只是为了该函数的副作用（Side effect）。当然有的语言为了使文法更加规范，使用一个特殊的值（void）来表示“没有值”。
{% endhint %}

对于求值表达式，类型检查器除了进行类型检查之外，还会尝试着对其求值，例如表达式`"Hello, " + "Golang"`，运算符号`+`的两个运算符都是常量，所以在类型检查的过程中就会完成字符串的拼接操作，从而得到最终结果`"Hello, Golang"` 。类型检查器会尽可能得对表达式进行求值，以便提升程序在运行时的效率。

对求值表达式进行类型检查的入口是方法`check.expr()`, 核心逻辑封装在方法`check.exprInternal()`内，定义在文件`$GCROOT/compile/internal/types2/expr.go`中。 `check.exprInternal()`是一个 700 行代码的胖方法，但其基本的处理思路与`check.typInternal()`是一样的，都是通过 switch...case... 语句根据表达式的类型进行递归检查，我们贴出部分代码一探究竟：

```go
func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKind {
	switch e := e.(type) {
	case *syntax.Name:
		// 对标识符进行类型检查，如果该标识符已经完成了类型检查，则递归终止，否则递归进行检查
		check.ident(x, e, nil, false)
	case *syntax.BasicLit:
		// 基本类型都是常量表达式，设置常量值。
		x.setConst(e.Kind, e.Value)
	case *syntax.FuncLit:
		// 函数字面量是形如 func() {} 的表达式，先对函数类型进行检查，再将函数体的检查推入 delayed 队列
		if sig, ok := check.typ(e.Type).(*Signature); ok {
			decl := check.decl
			iota := check.iota
			check.later(func() {
				check.funcBody(decl, "<function literal>", sig, e.Body, iota)
			})
			x.mode = value
			x.typ = sig
		}
	}
}
```

这里删除了绝大多数代码，只保留了三个 case 的部分代码来帮助我们理解该方法的处理思路。其中`syntax.Name`与`syntax.BasicLit`是该函数递归的 base-case, 而`syntax.FuncLit`则体现了对符合表达式的分解，并且可以发现对函数体的类型检查被推入了 delayed 队列，这在后续会有介绍。

对求值表达式的检查结果放在结构体`operand`中，其定义如下：

```go
type operand struct {
	mode operandMode
	expr syntax.Expr
	typ  Type
	val  constant.Value
	id   builtinId
}
```

该结构用来保存类型检查的中间值，属性`typ`与`val`分别用来保存推导出来的类型以及计算出来的值。


---

# 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.5.31.3c-qiu-zhi-biao-da-shi-de-lei-xing-jian-cha.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.
