scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.89k stars 1.06k forks source link

Multiple inlines make position data on reflect module trees inconsistent #21910

Open felher opened 3 weeks ago

felher commented 3 weeks ago

Compiler version

Tested on both 3.5.2 and 3.6.1

I tested on 3.6.1 as well because it already contains #21793

Main.scala

@main def hello(): Unit =
  Outer.outer:
    println("hi")

Outer.scala

object Outer:
  inline def outer(inline x: Any): Unit =
    Inner.pos(x)

Inner.scala

import scala.quoted.*

object Inner:
  inline def pos(inline x: Any): Unit =
    ${ posImpl('x) }

  def posImpl(x: Expr[Any])(using Quotes): Expr[Unit] =
    import quotes.reflect.*
    def printPos(p: Position): Unit =
      println(s"${p.start}..${p.end} of ${p.sourceFile.path} with length ${p.sourceFile.content.get.length}")

    printPos(x.asTerm.pos)
    printPos(x.asTerm.asInstanceOf[Inlined].body.pos)
    '{}

Output

70..71 of /tmp/down/inline-pos/src/main/scala/Main.scala with length 59
70..71 of /tmp/down/inline-pos/src/main/scala/Outer.scala with length 73

Expectation

Something different. The range for both cases is the x in Inner.pos(x) in Outer.scala, but the source file for the first one is Main.scala.

I thought maybe using inlining means that they are allowed to get inconsistent, but the docs on .start, .end clearly state that they are offsets in the source file and apply on the PositionModule states that "The range must be contained in the file."

som-snytt commented 3 weeks ago

I don't know the API but printPos(Position.ofMacroExpansion) is correct.

Gedochao commented 2 weeks ago

Note, for the given examples I'm getting:

70..71 of /tmp//position-bug/Main.scala with length 58
70..71 of /tmp/position-bug/Outer.scala with length 72

likely an extra line was added somewhere to get the +1 result.

Anyway, tagging @jchyb & @bishabosha for opinions on this.

som-snytt commented 2 weeks ago

On "terminal newline" at EOF, which Scala 2 adds to source text for ease of use, really does simplify, as a sentinel. "Trim to end of line" instead of "EOL or EOF". (That was my recent use case.)