Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression in thoughtworksinc / binding.scala #19191

Closed
WojciechMazur opened this issue Dec 4, 2023 · 1 comment · Fixed by #19409
Closed

Regression in thoughtworksinc / binding.scala #19191

WojciechMazur opened this issue Dec 4, 2023 · 1 comment · Fixed by #19409
Assignees
Labels
area:metaprogramming:quotes Issues related to quotes and splices area:typer itype:bug regression This worked in a previous version but doesn't anymore
Milestone

Comments

@WojciechMazur
Copy link
Contributor

Regression found in Open Community Build for thoughtworksinc/binding.scala build logs

Compiler version

Last good release: 3.4.0-RC1-bin-20230726-97677cc-NIGHTLY
First bad release: 3.4.0-RC1-bin-20230727-ac69c66-NIGHTLY
Works in 3.3.1
Affects Scala 3.3.2 LTS backports

Bisect was not accurate enough, the commit of bisect result failed with other errors when building compiler
Regression candidate commits:
ac69c66 (bad) Ondřej Lhoták : add test for issue #17997 affecting the global object initialization checker (#18141)
d903d35 Nicolas Stucki: Add regression test (#18287)
cb73f0b EnzeXing: Add test for modifying this to AndType
c04d2db Jędrzej Rochala: Update scala3-presentation-compiler to 39e349e (#18296)
989c55c (bad) Jan Chyb : Fix regression #17245: Overloaded methods with ClassTags (#18286)
4b67b34 (bad, other error) Nicolas Stucki : Set missing expansion span for copied inlined node (#18229)
182331b (good) Nicolas Stucki : Support polymorphic functions with erased parameters (#18293)
5625107 Nicolas Stucki: Support polymorphic functions with erased parameters
f22f9db (bad, other error) Julien Richard-Foy : Set missing expansion span for copied inlined node
101ce3a Jan Chyb: Fix regression with Overloaded methods returning Functions
f5313ae EnzeXing: add fix for issue #17997 affecting the global object initialization checker
96ca0b6 Ondrej Lhotak: add test for issue #17997 affecting the global object initialization checker

Minimized code

// main.scala
trait Binding[+A]:
  def value: A = ???
object Binding:
  inline def apply[A](inline a: A): Binding[A] = ???
  final case class Constant[+A](override val value: A) extends Binding[A]

extension [A](inline binding: Binding[A])
    transparent inline def bind: A = null.asInstanceOf[A]

trait Vars[A] extends BindingSeq[A]
trait BindingSeq[+A]:
  def foreachBinding[U](f: A => Binding[U]): Binding[Unit] = ???

extension [A](inline bindingSeq: BindingSeq[A])
  transparent inline def foreach[U](inline f: A => U): Unit = ${ Macros.foreach('bindingSeq, 'f) }

@main def Test = {
  val vars: Vars[Int] = ???

  val works = vars.foreach { v => () }
  val fails = for (v <- vars) ()
}
// macros.scala
import scala.compiletime._
import scala.deriving._
import scala.quoted._
import scala.annotation.meta.field

object Macros{
    private def bindingFunctionBody[A: quoted.Type, B: quoted.Type](f: quoted.Expr[A => B])(using Quotes) =
      import quoted.quotes.reflect.*
      f.asTerm match
        case inlined @ Inlined(
              call,
              bindings,
              block @ Block(
                List(
                  defDef @ DefDef(
                    name,
                    paramss @ List(TermParamClause(List(param @ ValDef(paramName, paramTpt, _)))),
                    tpt,
                    Some(rhs)
                  )
                ),
                closureIdent
              )
            ) =>
          Inlined
            .copy(inlined)(
              call,
              bindings,
              '{ (a: A) =>
                ${
                  Block(
                    List(
                      ValDef
                        .copy(param)(paramName, paramTpt, Some('a.asTerm))
                        .changeOwner(Symbol.spliceOwner)
                    ),
                    '{
                      Binding(${ rhs.changeOwner(Symbol.spliceOwner).asExprOf[B] })
                    }.asTerm.changeOwner(Symbol.spliceOwner)
                  )
                    .asExprOf[Binding[B]]
                }: Binding[B]
              }.asTerm
            )
            .asExprOf[A => Binding[B]]
        case _ =>
          '{ (a: A) => Binding.Constant($f(a)): Binding[B] }
      end match
    end bindingFunctionBody

    def foreach[A: quoted.Type, U: quoted.Type](self: quoted.Expr[BindingSeq[A]], f: quoted.Expr[A => U])(using
        qctx: Quotes
    ): quoted.Expr[Unit] = '{ $self.foreachBinding(${ bindingFunctionBody(f) }).bind }
}

Output

-- Error: /Users/wmazur/projects/dotty/bisect/main.scala:21:14 -----------------
21 |  val fails = for (v <- vars) ()
   |              ^^^^^^^^^^^^^^^^^^
   |Exception occurred while executing macro expansion.
   |java.lang.Exception: Expr cast exception: ((a: scala.Int) => ({
   |  val v: scala.Int = a
   |  Binding.apply[scala.Unit](())
   |}: Binding[scala.Unit]))
   |of type: scala.Function1[scala.Int, scala.Unit]
   |did not conform to type: scala.Function1[scala.Int, Binding[scala.Unit]]
   |
   |    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:76)
   |    at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.asExprOf(QuotesImpl.scala:119)
   |    at scala.quoted.runtime.impl.QuotesImpl$reflect$TreeMethods$.asExprOf(QuotesImpl.scala:118)
   |    at Macros$.bindingFunctionBody(macros.scala:45)
   |    at Macros$.foreach$$anonfun$1(macros.scala:53)
   |    at Macros$.foreach$$anonfun$adapted$1(macros.scala:53)
   |    at dotty.tools.dotc.quoted.PickledQuotes$$anon$1.transform(PickledQuotes.scala:110)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1563)
   |    at dotty.tools.dotc.quoted.PickledQuotes$$anon$1.transform(PickledQuotes.scala:135)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform$$anonfun$1(Trees.scala:1632)
   |    at scala.collection.immutable.List.mapConserve(List.scala:472)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1632)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1529)
   |    at dotty.tools.dotc.quoted.PickledQuotes$$anon$1.transform(PickledQuotes.scala:135)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform$$anonfun$1(Trees.scala:1632)
   |    at scala.collection.immutable.List.mapConserve(List.scala:472)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1632)
   |    at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1529)
   |    at dotty.tools.dotc.quoted.PickledQuotes$$anon$1.transform(PickledQuotes.scala:135)
   |    at dotty.tools.dotc.quoted.PickledQuotes$.spliceTerms(PickledQuotes.scala:152)
   |    at dotty.tools.dotc.quoted.PickledQuotes$.unpickleTerm(PickledQuotes.scala:88)
   |    at scala.quoted.runtime.impl.QuotesImpl.unpickleExprV2(QuotesImpl.scala:3170)
   |    at Macros$.foreach(macros.scala:53)
   |
   |----------------------------------------------------------------------------
   |Inline stack trace
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   |This location contains code that was inlined from main.scala:15
15 |  transparent inline def foreach[U](inline f: A => U): Unit = ${ Macros.foreach('bindingSeq, 'f) }
   |                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ----------------------------------------------------------------------------

Expectation

Should compile

@WojciechMazur WojciechMazur added itype:bug area:typer regression This worked in a previous version but doesn't anymore area:metaprogramming:quotes Issues related to quotes and splices labels Dec 4, 2023
WojciechMazur pushed a commit that referenced this issue Dec 5, 2023
The intention of this change is to make it simpler to read the error
message.

List the expected type and actual type before the expression. This is
usually the most important information and simpler to parse as the
expression can get quite long. For the expected type, the actual type
and the expression, we print the value in the same line if its String
representation has only one line. Otherwise we print it in the next line
with an indentation and extra new line at the end.

Before:
```
java.lang.Exception: Expr cast exception: ((a: scala.Int) => ({
  val v: scala.Int = a
  Binding.apply[scala.Unit](())
}: Binding[scala.Unit]))
of type: scala.Function1[scala.Int, scala.Unit]
did not conform to type: scala.Function1[scala.Int, Binding[scala.Unit]]

    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:76)
    ...
```

Example from #19191

After:
```
scala.quoted.runtime.impl.ExprCastException:
  Expected type: scala.Function1[scala.Int, scala.Unit]
  Actual type: scala.Function1[scala.Int, Binding[scala.Unit]]
  Expression:
    ((a: scala.Int) => ({
      val v: scala.Int = a
      Binding.apply[scala.Unit](())
    }: Binding[scala.Unit]))

    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:...)
    ...
```
@Kordyjan
Copy link
Contributor

Kordyjan commented Dec 8, 2023

I withdrew the backport for #18229 from the 3.3.2 queue and it fixed the problem.
Reference

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jan 10, 2024
@Kordyjan Kordyjan added this to the 3.4.1 milestone Feb 14, 2024
WojciechMazur pushed a commit that referenced this issue Jun 25, 2024
The intention of this change is to make it simpler to read the error
message.

List the expected type and actual type before the expression. This is
usually the most important information and simpler to parse as the
expression can get quite long. For the expected type, the actual type
and the expression, we print the value in the same line if its String
representation has only one line. Otherwise we print it in the next line
with an indentation and extra new line at the end.

Before:
```
java.lang.Exception: Expr cast exception: ((a: scala.Int) => ({
  val v: scala.Int = a
  Binding.apply[scala.Unit](())
}: Binding[scala.Unit]))
of type: scala.Function1[scala.Int, scala.Unit]
did not conform to type: scala.Function1[scala.Int, Binding[scala.Unit]]

    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:76)
    ...
```

Example from #19191

After:
```
scala.quoted.runtime.impl.ExprCastException:
  Expected type: scala.Function1[scala.Int, scala.Unit]
  Actual type: scala.Function1[scala.Int, Binding[scala.Unit]]
  Expression:
    ((a: scala.Int) => ({
      val v: scala.Int = a
      Binding.apply[scala.Unit](())
    }: Binding[scala.Unit]))

    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:...)
    ...
```
[Cherry-picked 7480582]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:quotes Issues related to quotes and splices area:typer itype:bug regression This worked in a previous version but doesn't anymore
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants