Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

函数与控制流的语义检查

复习

  • 表达式的类型检查:沿语法树检查类型
  • 作用域:名字可见的范围
  • 函数:传入参数并获得结果

TL;DR

  • 函数调用要检查参数个数与类型是否匹配
  • 要检查返回值的类型是否与声明一致
  • 控制流要检查条件类型、跳转是否合法
  • 还要检查所有路径是否都有返回值

正文

  类型检查不只针对表达式。函数调用、返回值、控制流,同样需要一整套语义检查。

检查函数调用

  调用一个函数时,编译器要核对:

  • 参数个数对不对(该给两个,不能给一个或三个)
  • 参数类型对不对(该传整数,不能传字符串)
  • 函数是否存在、是否可见(这就用到了名字解析)

  参数类型不匹配,是最常见的语义错误之一。比如一个函数声明它要一个整数,你却传了个字符串,编译器应当立刻指出。

检查返回值

  函数声明“我会返回一个整数”,那它的返回语句就必须返回整数:

  • 返回类型不匹配,报错
  • 有些路径没有返回值,也要报错

  最后一条很容易被忽略。设想一个函数,如果条件成立就返回,不成立就什么都不返回——那调用者在另一种情况下就拿不到值。有返回类型声明的函数,必须保证每条执行路径都以恰当的返回结束。 这需要在编译期顺着控制流仔细检查。

检查控制流

  控制结构本身也有规则:

  • ifwhile 的条件,应该是布尔类型(而不是随便一个数或字符串)
  • breakcontinue 只能出现在循环里
  • return 只能在函数里

  这些“位置是否合法、类型是否恰当”的检查,都属于语义分析的一部分。

都是在问同一个问题

  把这些检查放在一起看,会发现它们都在回答同一类问题:这段程序,在语义上“讲得通”吗?

  名字有没有定义、类型对不对、控制流合不合理——每一层都在排除“虽然语法合法、但毫无意义”的程序。语法只管形状对不对,语义才管意思通不通。

思考题 1

  检查函数调用时,主要需要核对哪些信息?

思考题 2

  为什么“函数可能有些路径没有返回值”,需要在编译期检查?

小结

知识点

  • 函数调用需核对参数个数、类型与可见性
  • 返回值类型必须与声明一致
  • 控制流要检查条件类型与跳转位置
  • 有返回类型的函数必须保证所有路径都返回

参考资料

  1. Wikipedia(zh):类型检查:包含函数与控制流的检查
  2. Wikipedia(zh):控制流:程序执行路径的组织

思考题答案(仅供参考)

思考题 1

  主要核对:函数是否存在且可见、传入实参的个数是否与形参一致、实参类型是否与形参类型匹配。这些信息来自符号表与类型系统。

思考题 2

  因为有些路径不返回,会让调用者在那种情况下拿不到应有的结果,行为不可预期。若不在编译期检查,这类问题可能要等到运行时才暴露。所以需要在编译期顺着控制流确认所有路径都以恰当方式返回。

协议

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

封面图

设计师 | 南国微雪