sbt/main-settings
Rikito Taniguchi e292d05322
[2.x] Fix Scala 3 .previous expansion for unstable path (#9041)
**problem**
.previous was implemented as an inline expansion to wrapInitTask(Previous.runtime(in)(<instance of JsonFormat[A1]))

For example, the .previous on taskKey[String] gets inlined like
this, and then sbt's macro moves `Previous.runtime(...)(using StringJsonFormat)` into task input.

lazy val fingerprints = taskKey[String]("...")
val previousFingerprints = fingerprints.previous

// inlined
val previousFingerprints: Option[String] = {
  InputWrapper.wrapInitTask(
    Previous.runtime(...)(using StringJsonFormat)
  )
}

- 6c8ee6ea37/main-settings/src/main/scala/sbt/Def.scala (L410-L412)
- 6c8ee6ea37/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala (L468)

However, if the return type of task is a bit more complicated like
Seq[String], it doesn't work:

Scala 3 Compiler's inliner creates a synthetic proxy symbol if the
inlining tree is an application, whose arguments are unstable path.

For example,

lazy val fingerprints = taskKey[Seq[String]]("...")
val previousFingerprints = fingerprints.previous

// inline to
val previousFingerprints: Option[Seq[String]] = {
  val x$2$proxy1 = immSeqFormat(StringJsonFormat)
  InputWrapper.wrapInitTask(
    Previous.runtime(...)(using x$2$proxy1)
  )
}

cc7d6db700/compiler/src/dotty/tools/dotc/inlines/Inliner.scala (L324-L329)

However, sbt2's Cont macro captures only `Previous.runtime(...)(using x$2$proxy1)`, while it doesn't capture the proxy definition. Consequently, while sbt macro moves the `Previous.runtime(...)` application as a task input, the proxy definition is left in the task body.

mapN(
  (
    link / fingerprints,
    Previous.runtime(...)(using x$2$proxy1) // here x$2$proxy1 can't be found
  )
) {
  ...
  val x$2$proxy1 = ...
}

Then we get:

-- Error: /.../build.sbt:14:59
14 |  val previousFingerprints = (link / fingerprints).previous
   |                                                           ^
   |While expanding a macro, a reference to value x$2$proxy1 was used outside the scope where it was defined

**How this PR fixed**
This commit fixes the problem by defining a dedicated Scala3 macro for .previous that summon JsonFormat[A1] inside the macro before constructing the wrapped previous.runtime(...)(using ...) by inliner. The macro insert the found given value tree directly into the previous.runtime(...)(using $found).

This way, Cont macro always moves the Previous.runtime tree along with it's given argument, without leaking compiler-generated inline proxies across scopes.
2026-04-07 22:45:01 -04:00
..
src [2.x] Fix Scala 3 .previous expansion for unstable path (#9041) 2026-04-07 22:45:01 -04:00