Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

名字解析

复习

  • 作用域:名字可见的范围,且可以嵌套
  • 符号表:名字到属性的映射
  • 树:作用域构成的层次结构

TL;DR

  • 名字解析确定每个名字的使用指向哪个定义
  • 它沿作用域从内向外查找
  • 找到最近的同名定义,就是答案
  • 一个都找不到,就是“未定义”错误

正文

  有了作用域和符号表,就可以回答语义分析里的一个基本问题:程序里出现的每个名字,到底指的是哪个定义?

  这个“把名字的使用和它的定义对应起来”的过程,叫名字解析(name resolution),也叫名字绑定。

从里往外找

  解析的规则,正是上一章作用域结构的直接结果:从当前最内层的作用域开始,一层一层向外找,遇到第一个同名定义就停下。

  为什么从内向外?因为内层的定义“更近、更具体”。回想遮蔽规则:内层同名者会遮住外层。从内向外找,自然就先命中内层的那个,符合“最近优先”。查找顺序,其实就是作用域的嵌套顺序。

  找到的那个定义,就是这个名字的“绑定目标”。编译器据此知道:这个 x 是哪个变量、它的类型是什么、它存在哪儿。

找不到、或找到多个

  名字解析也会处理两类典型错误:

  • 找不到任何定义:使用了从未定义过的名字,报“未定义”
  • 同层重复定义:同一个作用域里定义了两次同名,通常也是错误(除非语言特别允许)

  分析器在扫描时,一边维护符号表(进作用域就加、出作用域就删),一边对每个名字做上面的查找。两件事互相配合,缺一不可。

它是后面一切的入口

  名字解析的结果,是后续所有语义判断的前提:

  • 要知道变量的类型,才能做类型检查
  • 要知道函数的签名,才能检查调用
  • 要知道名字存在哪里,才能生成代码

  所以,名字解析虽然听起来朴素,却是语义分析真正开始的地方。先把“每个名字指什么”弄清楚,才谈得上“用得对不对”。

思考题 1

  名字解析为什么要“从内到外”查找?

思考题 2

  如果某个名字在任何一个作用域里都找不到定义,应该怎样处理?

小结

知识点

  • 名字解析把名字的使用与定义对应起来
  • 沿作用域从内向外查找,取最近的定义
  • 找不到即为“未定义”错误
  • 它是后续类型检查与代码生成的前提

参考资料

  1. Wikipedia(zh):名字解析:将名字与定义关联起来
  2. Wikipedia(zh):作用域:名字解析依据的可见性规则

思考题答案(仅供参考)

思考题 1

  因为内层作用域的定义更近、更具体,且会遮蔽外层同名定义。从内向外查找,恰好能优先命中最近的、也是实际生效的那个定义,符合“最近优先”的语义。

思考题 2

  应报告一个“未定义名字”的错误。因为这个名字在程序的任何作用域里都没有对应的定义,编译无法确定它指的是什么,也就无法继续正确的语义分析和代码生成。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪