The 'do symbol is skipped in some contexts where it shouldn't be

Description

Expressions like this result in nil, but should fail to compile:

(let [] do)

Expressions like this result in nil, but should produce a value:

(let [do 1] do)

This is true for all special forms with an "implicit do": try/catch/finally, letfn*, let*, and the fn*/reify/deftype family of forms.

The way that "implicit do" is implemented for these special forms is that the relevant Parser delegates to BodyExpr.Parser explicitly for its body expression. For example,

(let [x 1] (println x) x)

calls BodyExpr.Parser.parse() with the list '((println x) x). However, BodyExpr.Parser is also used to parse lists like '(do (println x) x), in other contexts. To handle both cases, its parse() method skips its first form if that form is the symbol 'do. So, if a special form with an "implicit do" has as its first expression the bare symbol 'do, that symbol is incorrectly discarded.

The attached patch fixes this by making each such special form actually insert an explicit 'do before delegating, so that BodyExpr.Parser can unconditionally assume that its first form is 'do, and skip it. BodyExpr now throws an exception if this is not the case, as this would indicate an error in the compiler - user code cannot create this situation.

The attached patch will have minimal impact on compilation time: it introduces one extra method call and one extra cons cell allocation for each analysis of an expression with an implicit do. It will have no impact on generated code (except that it will fix the errors indicated in the beginning of this ticket).

Three tests are included which fail before the patch but succeed after it.

Environment

None

Assignee

Unassigned

Reporter

Alan Malloy

Labels

Approval

Triaged

Patch

Code and Test

Affects versions

Priority

Major
Configure