v2.26.0 - Control-flow bodies no longer require braces
Control-flow bodies no longer require braces
for (var e in l) print('- $e'); is ordinary Dart, and it did not parse. Only
the plain if accepted an unbraced single-statement body — every loop, every
if/else and every else if demanded { }.
All seven remaining rules now accept either form, in Dart, Java 11, Kotlin,
C#, JavaScript and TypeScript: for, for-in/for-each, while,
do/while, if/else, the else if chain, and the final else. A braced
body is still tried first, so nothing about existing sources changes.
singleLineStatement was also far too narrow — return …; or an expression
statement, nothing else — so even the if that already supported it rejected
if (x) break;, if (x) throw e; and a nested if. It is now each language's
full statement set minus declarations and bare blocks.
A dangling else binds to the nearest if, matching every target
language. Python gains the equivalent construct, the inline suite
(if x: return 1, def f(): pass, while c: i += 1; j += 1), at every
suite() position.
Go is deliberately excluded — its spec defines
Block = "{" StatementList "}" — and Lua has no such form. A single-statement
body translated to either is emitted braced / do…end.
Not supported, on purpose: the empty statement as a body (while (c) ;),
for (;;), and ;-separated statements on an ordinary (non-suite) Python line.
A latent else-prefix miscompile, fixed first
BaseGrammarLexer.token() is a prefix matcher with no word-boundary guard, so
string('else') matches the start of an identifier like elseCount. That was
unreachable while every else arm followed a mandatory braced block, and would
have become a silent miscompile the moment bodies could be unbraced:
if (a) x();
elseCount = 1; // `else` matches; `Count = 1;` becomes the else armThe branch and loop rules of all six C-style grammars now use whole-word
keyword tokens. Also covers Kotlin's when entry labels and if expression.
New Dart syntax
- Interpolation inside triple-quoted strings —
'''Hello $name'''yielded
the literal text$name. Wrong output, not an error. assert(c)/assert(c, m)as a real statement (it parsed as a call to a
user function namedassert). Each target emits its own idiom: Java
assert c : m;, Pythonassert c, m, Kotlinassert(c) { m }, C#
Debug.Assert, Lua's built-in, JS/TS/Go lowered to an explicit check. Wasm
refuses to compile it rather than mis-compile it.requirednamed parameters on plain, constructor-typed andthis.
forms.({required int a})was a hard parse failure.- Annotations (
@override,@Deprecated('x'),@pragma(...),@a.B(1))
at every position Dart allows. There was no@in any grammar in the repo,
so a single@overridebroke the whole class body — which matters beyond
hand-written sources, sincelib/src/publoads real pub packages. Parsed and
discarded for now. lateon locals and fields (accepted and dropped).constat use sites:const Foo(),const [],const {}.- Arrow and
asyncbodies on local and anonymous functions. catch (e, s)now binds the stack trace — it was parsed and thrown away,
so any handler referencingsfailed.- Compound assignment
%= &= |= ^= <<= >>=. - Untyped getters (
get twice => …) now parse.
Other fixes
- Java and C#:
if (a) {} else if (b) {}with no trailingelsefailed to
parse — both made the finalelsenon-optional, unlike every other language. - Python: a
do/whiletranslated to Python emitted literal
do { … } while (c);. Now lowers towhile True: … if not (c): break. - Lua: compound assignment was emitted verbatim (
a += 1), which Lua does
not have. Now lowers toa = a + 1. const [1]silently misparsed as an index read on a variable namedconst.set value(int v) {}silently became a method namedvaluereturning a
type namedset. Now a clean parse error at theset. Full setter support
remains unimplemented.
Full changelog: https://github.com/ApolloVM/apollovm_dart/blob/v2.26.0/CHANGELOG.md