Skip to content

Parser crash (AssertionError) when line comment hides then in for-generator inside new argument #25248

@haskiindahouse

Description

@haskiindahouse

Compiler version

  • 3.6.4 (latest stable release)
  • 3.8.3-RC1-SNAPSHOT (nightly)

Minimized code

//> using scala 3.6.4

val y = new c(for baz <- if baz // x then y else new A {
  val z = w } do 1)

The line comment // x then y else new A { causes the parser to see if baz without a then clause. During error recovery, the parser constructs an AST where the for-comprehension's span extends one byte beyond its parent new c(...) span.

Output

-- [E040] Syntax Error: crash.scala:2:2 --
2 |  val z = w } do 1)
  |  ^^^
  |  'then' expected, but 'val' found

  exception occurred while parser crash.scala
  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose

     while compiling: crash.scala
        during phase: parser
    compiler version: version 3.6.4

Exception in thread "main" java.lang.AssertionError: assertion failed: position error, parent span does not contain child span
parent      = new c(for baz <- if baz then ??? do ???) # -1,
parent span = <8..76>,
child       = for baz <- if baz then ??? do ??? # -1,
child span  = [14..77]
	at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
	at dotty.tools.dotc.ast.Positioned.check$1(Positioned.scala:177)
	at dotty.tools.dotc.ast.Positioned.check$1$$anonfun$3(Positioned.scala:207)
	at scala.collection.immutable.List.foreach(List.scala:334)
	at dotty.tools.dotc.ast.Positioned.check$1(Positioned.scala:207)
	at dotty.tools.dotc.ast.Positioned.checkPos(Positioned.scala:228)
	at dotty.tools.dotc.ast.Positioned.check$1(Positioned.scala:202)
	at dotty.tools.dotc.ast.Positioned.checkPos(Positioned.scala:228)
	at dotty.tools.dotc.ast.Positioned.check$1(Positioned.scala:202)
	at dotty.tools.dotc.ast.Positioned.check$1$$anonfun$3(Positioned.scala:207)
	at scala.collection.immutable.List.foreach(List.scala:334)
	at dotty.tools.dotc.ast.Positioned.check$1(Positioned.scala:207)
	at dotty.tools.dotc.ast.Positioned.checkPos(Positioned.scala:228)
	at dotty.tools.dotc.parsing.Parser.parse$$anonfun$1(ParserPhase.scala:39)
	at dotty.tools.dotc.core.Phases$Phase.monitor(Phases.scala:510)
	at dotty.tools.dotc.parsing.Parser.parse(ParserPhase.scala:40)
	...

Expectation

The compiler should parse erroneous programs without crashing. Syntax errors should be reported gracefully, and the AST position invariant (parent span contains all child spans) should be maintained even during error recovery.

Instead, the parser crashes with an AssertionError when checking position consistency of the parsed AST.

Analysis

The crash occurs in Positioned.check (Positioned.scala:177) during the parser phase:

  1. The line comment // on line 1 hides then y else new A {, so the parser sees for baz <- if baz — an if without a then clause.
  2. On line 2, the parser encounters val z = w } do 1) and triggers error recovery (expecting then, got val).
  3. During error recovery, the parser reconstructs the AST but assigns the for-comprehension a span of [14..77] while its parent new c(...) gets a span of <8..76> — the child's end position (77) exceeds the parent's end position (76) by 1 byte.
  4. The checkPos validation in Positioned.scala detects this invariant violation and throws AssertionError.

This is a parser-phase bug - the crash happens before type checking, purely during syntax error recovery. The root cause is likely in how the error recovery mechanism computes source positions when an if-expression inside a for-generator is missing its then clause due to a line comment.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions