语句的返回类型
语句的返回类型的检查在类型检查中完成,因为它的规则比较复杂,这里单独列出来讲一下。这里只涉及到了框架中已经存在的部分,即判断语句的返回内容是否为空,而没有完整地计算语句的返回类型。
每个语句(Stmt
)都有自己的返回类型,规则列举如下:
Assign
:返回类型为空(Ty::void()
,下同)LocalVarDef
:返回类型为空ExprEval
:返回类型为空Skip
:返回类型为空If
:定义on_false
分支的返回类型为:如果on_false
分支存在,则就是它的返回类型,否则为空;If
的返回类型不为空,当且仅当为两个分支的返回类型都不为空While
:返回类型为空For
:返回类型为空Return
:若return
语句无返回值则返回类型为空,否则返回类型为返回值值的类型Print
:返回类型为空Break
:返回类型为空Block
:如果它至少包含一条语句,则它的返回类型等同于它的最后一条语句的返回类型;否则,它的返回类型为空显然这里有一些不精确的地方,例如一个
Block
中可能包含多条return
语句,则有可能最后一条语句实际上不可达。为了简单起见,我们不考虑这种情况,测例中也不会在这里为难大家。作为一点科普,这样的不可达语句在大多数语言中都会被汇报一个警告,但在java中这是一种编译错误,可参考section 14.21 of the JLS
如果一个函数的返回类型不为空,然而它的body
语句块的返回类型为空,需要汇报一个对应的错误,这在基础框架中已经实现了。
大家可能会发现,对于while
和for
循环的检测粒度似乎太粗了一点:如果是while (true)
或者for (...; true; ...)
,且循环中没有break
的情况,这个循环本身并不会结束,唯一的跳出循环的方式是通过其中的return
语句,所以它们的返回类型也就不一定为空了。
java对这个问题的处理方式是,javac会汇报返回类型不为空的函数可能不返回值这个错误,但如果while
或者for
的条件是常量表达式且求值结果为true
,并且其中没有作用于它的break
语句,则认为它一定有返回值,不会产生编译错误。
rust对这个问题的处理方式是,rustc也强制要求保证返回类型不为空的函数一定返回值,但是它不会特殊处理while true
,而是额外引入了loop
关键字,运行时效果等价于while true
,但在其中没有作用于它的break
语句时认为它一定有返回值。
如果这样的循环中同时也没有return
语句呢?这意味着这个循环一定是一个死循环,一旦程序执行到这个位置,则这个函数就不可能返回了,但是这并不与函数的返回类型要求不为空相矛盾。在rust中这种情况下认为函数的实际返回类型为!
,即never type,而never type是其他所有类型的子类型,所以不会产生编译错误。java虽然语法上没有never type的概念,但是可以认为编译器内部有这个概念,类似的情况下也不会产生编译错误。这里给出两个简单的例子,它们都是合法的:
目前我们为了简单起见,这两种比较合理的方案都没有采用(第一种方案需要定义常量表达式,并且需要在类型检查阶段做常量表达式求值;第二种方案需要增加新的关键字,也许可以作为未来的实验中的某个新语法),而是直接粗暴地认为循环语句的返回值为空。
Last updated