名字解析
复习
- 作用域:名字可见的范围,且可以嵌套
- 符号表:名字到属性的映射
- 树:作用域构成的层次结构
TL;DR
- 名字解析确定每个名字的使用指向哪个定义
- 它沿作用域从内向外查找
- 找到最近的同名定义,就是答案
- 一个都找不到,就是“未定义”错误
正文
有了作用域和符号表,就可以回答语义分析里的一个基本问题:程序里出现的每个名字,到底指的是哪个定义?
这个“把名字的使用和它的定义对应起来”的过程,叫名字解析(name resolution),也叫名字绑定。
从里往外找
解析的规则,正是上一章作用域结构的直接结果:从当前最内层的作用域开始,一层一层向外找,遇到第一个同名定义就停下。
为什么从内向外?因为内层的定义“更近、更具体”。回想遮蔽规则:内层同名者会遮住外层。从内向外找,自然就先命中内层的那个,符合“最近优先”。查找顺序,其实就是作用域的嵌套顺序。
找到的那个定义,就是这个名字的“绑定目标”。编译器据此知道:这个 x 是哪个变量、它的类型是什么、它存在哪儿。
找不到、或找到多个
名字解析也会处理两类典型错误:
- 找不到任何定义:使用了从未定义过的名字,报“未定义”
- 同层重复定义:同一个作用域里定义了两次同名,通常也是错误(除非语言特别允许)
分析器在扫描时,一边维护符号表(进作用域就加、出作用域就删),一边对每个名字做上面的查找。两件事互相配合,缺一不可。
它是后面一切的入口
名字解析的结果,是后续所有语义判断的前提:
- 要知道变量的类型,才能做类型检查
- 要知道函数的签名,才能检查调用
- 要知道名字存在哪里,才能生成代码
所以,名字解析虽然听起来朴素,却是语义分析真正开始的地方。先把“每个名字指什么”弄清楚,才谈得上“用得对不对”。
思考题 1
名字解析为什么要“从内到外”查找?
思考题 2
如果某个名字在任何一个作用域里都找不到定义,应该怎样处理?
小结
知识点
- 名字解析把名字的使用与定义对应起来
- 沿作用域从内向外查找,取最近的定义
- 找不到即为“未定义”错误
- 它是后续类型检查与代码生成的前提
参考资料
- Wikipedia(zh):名字解析:将名字与定义关联起来
- Wikipedia(zh):作用域:名字解析依据的可见性规则
思考题答案(仅供参考)
思考题 1
因为内层作用域的定义更近、更具体,且会遮蔽外层同名定义。从内向外查找,恰好能优先命中最近的、也是实际生效的那个定义,符合“最近优先”的语义。
思考题 2
应报告一个“未定义名字”的错误。因为这个名字在程序的任何作用域里都没有对应的定义,编译无法确定它指的是什么,也就无法继续正确的语义分析和代码生成。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪