Skip to content

Macro changes semantics based on how you refer to a type located in an object after a seemingly innocent quote match #16636

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

Closed
arainko opened this issue Jan 8, 2023 · 0 comments · Fixed by #16570
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug
Milestone

Comments

@arainko
Copy link
Contributor

arainko commented Jan 8, 2023

Compiler version

3.2.1, 3.3.0-RC1-bin-20230106-d49c2d9-NIGHTLY

Minimized code

// file: ReproTransformer.scala

import scala.quoted.*

trait ReproTransformer[A, B] {
  def transform(from: A): B
}

object ReproTransformer {
  final class Identity[A, B >: A] extends ReproTransformer[A, B] {
    def transform(from: A): B = from
  }

  given identity[A, B >: A]: Identity[A, B] = Identity[A, B]

  inline def getTransformer[A, B]: ReproTransformer[A, B] = ${ getTransformerMacro[A, B] }

  def getTransformerMacro[A, B](using quotes: Quotes, A: Type[A], B: Type[B]) = {
    import quotes.reflect.*

    val transformer = (A -> B) match {
      case '[a] -> '[b] => 
        val summoned = Expr.summon[ReproTransformer[a, b]].get
// ----------- INTERESTING STUFF STARTS HERE
        summoned match {
          case '{ $t: ReproTransformer[src, dest] } => t
        }
// ----------- INTERESTING STUFF ENDS HERE
    }
    transformer.asExprOf[ReproTransformer[A, B]]
  }
}
// file: usage.scala

object A {
  case class AnotherCaseClass(name: String)

  val errorsOut1 = ReproTransformer.getTransformer[A.AnotherCaseClass, AnotherCaseClass]
  val errorsOu2 = ReproTransformer.getTransformer[AnotherCaseClass, A.AnotherCaseClass]

  val works1 = ReproTransformer.getTransformer[A.AnotherCaseClass, A.AnotherCaseClass]
  val works2 = ReproTransformer.getTransformer[AnotherCaseClass, AnotherCaseClass]
}

Output

[error] -- Error: /home/aleksander/repos/ducktape/ducktape/src/main/scala/io/github/arainko/ducktape/usage.scala:8:50 
[error]  8 |  val errorsOut1 = ReproTransformer.getTransformer[A.AnotherCaseClass, AnotherCaseClass]
[error]    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Exception occurred while executing macro expansion.
[error]    |java.lang.Exception: Expr cast exception: io.github.arainko.ducktape.ReproTransformer.identity[io.github.arainko.ducktape.A.AnotherCaseClass, B]
[error]    |of type: io.github.arainko.ducktape.ReproTransformer.Identity[io.github.arainko.ducktape.A.AnotherCaseClass, B]
[error]    |did not conform to type: io.github.arainko.ducktape.ReproTransformer[io.github.arainko.ducktape.A.AnotherCaseClass, io.github.arainko.ducktape.A.AnotherCaseClass]
[error]    |
[error]    |    at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:73)
[error]    |    at io.github.arainko.ducktape.ReproTransformer$.getTransformerMacro(ReproTransformer.scala:30)
[error]    |
[error]    |----------------------------------------------------------------------------
[error]    |Inline stack trace
[error]    |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]    |This location contains code that was inlined from ReproTransformer.scala:16
[error] 16 |  inline def getTransformer[A, B]: ReproTransformer[A, B] = ${ getTransformerMacro[A, B] }
[error]    |                                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]     ----------------------------------------------------------------------------

Expectation

An additional (irrefutable?) quote match shouldn't change the semantics of the macro and the way you refer to a type located in an object shouldn't matter.

If you change the implementation of getTransformerMacro to not include the additional pattern match:

 def getTransformerMacro[A, B](using quotes: Quotes, A: Type[A], B: Type[B]) = {
    import quotes.reflect.*

    val transformer = (A -> B) match {
      case '[a] -> '[b] => 
        val summoned = Expr.summon[ReproTransformer[a, b]].get
// ----------- INTERESTING STUFF STARTS HERE
        summoned // return the summoned Expr straight away without additional matches
// ----------- INTERESTING ends STARTS HERE
    }
    transformer.asExprOf[ReproTransformer[A, B]]
  }

both of the usages compile properly.

Context

This issue was brought up by one of the users of ducktape here: arainko/ducktape#26, which was subsequently fixed here: arainko/ducktape#27

@arainko arainko added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 8, 2023
@nicolasstucki nicolasstucki added area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 9, 2023
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jan 9, 2023
The issue was that an uninstantiated type variable escaped the implicit
search and was then affected by the quote pattern match.

Fixes scala#16636
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jan 9, 2023
The issue was that an uninstantiated type variable escaped the implicit
search and was then affected by the quote pattern match.

Fixes scala#15779
Fixes scala#16636
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jan 16, 2023
The issue was that an uninstantiated type variable escaped the implicit
search and was then affected by the quote pattern match.

Fixes scala#15779
Fixes scala#16636
smarter added a commit that referenced this issue Jan 16, 2023
Trees are only checked if `-Xcheck-macros` is enabled.

Fixes:
 - Add missing positions to `{ValDef,Bind}.apply`
 - Inline by-name ascribed param
 - Unbound type variables after implicit search
 - Fixes #15779
 - Fixes #16636
little-inferno pushed a commit to little-inferno/dotty that referenced this issue Jan 25, 2023
The issue was that an uninstantiated type variable escaped the implicit
search and was then affected by the quote pattern match.

Fixes scala#15779
Fixes scala#16636
@Kordyjan Kordyjan added this to the 3.3.0 milestone Aug 1, 2023
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 itype:bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants