这样的表达式结果为 nil,但应该无法编译
(let () do)
这样的表达式结果为 nil,但应该产生一个值
(let (do 1) do)
这适用于所有具有“隐式 do”的特殊形式:try/catch/finally,letfn、let,以及 fn**/reify/deftype 系列形式。
对于这些特殊形式,隐式 do 的实现方式是相关解析器显式地委派到 BodyExpr.Parser 来解析其体表达式。例如,
(let (x 1) (println x) x)
调用 BodyExpr.Parser.parse() 并传递列表 '((println x) x)'。然而,BodyExpr.Parser 在其他上下文中也用于解析 '(do (println x) x)' 这样的列表。为了同时处理这两种情况,其 parse() 方法会跳过其第一个形式,如果该形式是符号 'do'。因此,如果一个具有“隐式 do”的特殊形式将其第一个表达式作为空白的 'do' 符号,那么该符号会被错误地丢弃。
附加的补丁通过使每个此类特殊形式实际在委派之前插入显式 'do' 来修复此问题,这样 BodyExpr.Parser 就可以无条件地假设其第一个形式是 'do' 并跳过它。BodyExpr 现在抛出异常,如果没有这种情况,这表明编译器中存在错误——用户代码无法创建这种情况。
附加的补丁对编译时间的影响很小:它为具有隐式 do 的每个表达式分析引入了一个额外的函数调用和一个额外的 cons 单元分配。它不会对生成代码产生影响(除了它会修复在票据开头指示的错误)。
包含三个测试,这些测试在补丁之前失败但在补丁之后成功。