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

Cannot call macro defined in the same source file error #12498

Closed
mbore opened this issue May 17, 2021 · 13 comments · Fixed by #15529
Closed

Cannot call macro defined in the same source file error #12498

mbore opened this issue May 17, 2021 · 13 comments · Fixed by #15529
Labels
Milestone

Comments

@mbore
Copy link

mbore commented May 17, 2021

Compiler version

3.0.1-RC1

Minimized code

MainMacro.scala

import scala.quoted.*

case class Wrapper[T](t: T) extends MainMacro[T]

trait MainMacro[U] { this: Wrapper[U] => 
  inline def showTypeRepr: String = ${ MainMacro.showTypeReprImpl[U]}
}

object MainMacro {
  def showTypeReprImpl[U: Type](using Quotes): Expr[String] = {
      import quotes.reflect.*
 
      val tpe = TypeRepr.of[U]

      Expr(tpe.toString)
  }
}

Test1.scala

object TestData {
  case class Person(name: String, age: Int)
}

class Test1 {

  import TestData.*

  // Notice that without inline does not work also
  inline def in(fun: => Any): Any = fun

  in {
    Wrapper(Person("a", 1)).showTypeRepr
  }
}

Output

$ mkdir classes
$ scalac -3.0.2-RC1 -d classes MainMacro.scala
$ scalac -3.0.2-RC1 -d classes -cp classes Test1.scala
-- Error: Test1.scala:13:28 ----------------------------------------------------
13 |    Wrapper(Person("a", 1)).showTypeRepr
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |    Cannot call macro class Person defined in the same source file
   | This location contains code that was inlined from Test1.scala:13
1 error found

Expectation

It should compile, I guess.

@nicolasstucki
Copy link
Contributor

I could not reproduce it on master. It might be fixed or it might depend on the way it is compiled. @mbore how did you compile this code?

@mbore
Copy link
Author

mbore commented May 17, 2021

I've tested it with the 3.0.0 version and it compiles, so it's seems to be fixed already. Thank you

@mbore mbore closed this as completed May 17, 2021
@mbore
Copy link
Author

mbore commented May 17, 2021

@nicolasstucki I’ve found one more unexpected behaviour related to this issue.

Compiler version

Scala Version: 3.0.0
Sbt version: 1.5.2

Minimized code

Updated Main.scala

case class Person(name: String, age: Int)

object Main extends App {
  println(Wrapper(Person("a", 1)).showTypeRepr)
}
  • Run sbt clean run -> works as expected
  • Change the name of Main object e.g.

object Main2 extends App {
  println(Wrapper(Person("a", 1)).showTypeRepr)
}
  • Run sbt run -> error

Output

[error] 6 |  println(Wrapper(Person("a", 1)).showTypeRepr)
[error]   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]   |          Cannot call macro class Person defined in the same source file
[error]   | This location contains code that was inlined from Main.scala:6

I'm not sure if this is an issue in dotty or sbt.

@nicolasstucki
Copy link
Contributor

It looks like it is an incremental compilation issue

@adamw
Copy link
Contributor

adamw commented May 17, 2021

@nicolasstucki yes, although the scalatest variant (linked) also is reproducible on a clean build

@cheeseng
Copy link

cheeseng commented May 18, 2021

@mbore @nicolasstucki @adamw Updating from ScalaTest discussion here:

scalatest/scalatest#2032

I made an example project here to reproduce the problem without ScalaTest:

https://github.com/cheeseng/scala3-inline-problem

just run the following to see the error:

> sbt clean test:compile

Note that if this object TestData in https://github.com/cheeseng/scala3-inline-problem/blob/main/src/test/scala/Test1.scala#L1 is moved into a separate file, the error does go away. But if the following:

object TestData {
  case class Person(name: String, age: Int)
}

object Test1 extends App {
  import TestData.*
  
    println(Wrapper(Person("a", 1)).showTypeRepr)
}

is suppose to work, I don't see why the example does not work.

@cheeseng
Copy link

@mbore @nicolasstucki @adamw, Fyi I updated the example in https://github.com/cheeseng/scala3-inline-problem to use scala 3.0.2-RC1 but unfortunately it still encounters the same error:

cheeseng@cheeseng-RAVEN:~/git/scala3-inline-problem$ sbt clean test
[info] welcome to sbt 1.5.2 (GraalVM Community Java 1.8.0_302)
[info] loading global plugins from /home/cheeseng/.sbt/1.0/plugins
[info] loading project definition from /home/cheeseng/git/scala3-inline-problem/project
[info] loading settings for project root from build.sbt ...
[info] set current project to scala3-simple (in build file:/home/cheeseng/git/scala3-inline-problem/)
[success] Total time: 0 s, completed Aug 17, 2021 9:01:21 PM
[info] compiling 1 Scala source to /home/cheeseng/git/scala3-inline-problem/target/scala-3.0.2-RC1/classes ...
[warn] -- Warning: /home/cheeseng/git/scala3-inline-problem/src/main/scala/MainMacro.scala:15:6 
[warn] 15 |      '{${Expr(tpe.toString)}}
[warn]    |      ^^^^^^^^^^^^^^^^^^^^^^^^
[warn]    |Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.
[warn] one warning found
[warn] one warning found
[info] compiling 1 Scala source to /home/cheeseng/git/scala3-inline-problem/target/scala-3.0.2-RC1/test-classes ...
[error] -- Error: /home/cheeseng/git/scala3-inline-problem/src/test/scala/Test1.scala:13:28 
[error] 13 |    Wrapper(Person("a", 1)).showTypeRepr 
[error]    |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |    Cannot call macro class Person defined in the same source file
[error]    | This location contains code that was inlined from Test1.scala:13
[error] one error found
[error] one error found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 4 s, completed Aug 17, 2021 9:01:25 PM

@dwijnand
Copy link
Member

Reproduces cleanly for me:

$ mkdir classes
$ scalac -3.0.2-RC1 -d classes MainMacro.scala
$ scalac -3.0.2-RC1 -d classes -cp classes Test1.scala
-- Error: Test1.scala:13:28 ----------------------------------------------------
13 |    Wrapper(Person("a", 1)).showTypeRepr
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |    Cannot call macro class Person defined in the same source file
   | This location contains code that was inlined from Test1.scala:13
1 error found

@kitlangton
Copy link

kitlangton commented Nov 19, 2021

I'm getting the same issue:

CleanShot 2021-11-19 at 06 26 28@2x

Needless to say, the trait OldLady does not have any macros defined on it. Not quite minimized, but this is from an example file in the ZIO repo. (Here's the corresponding CI failure)

@nicolasstucki nicolasstucki added area:metaprogramming:quotes Issues related to quotes and splices and removed area:metaprogramming labels Jun 2, 2022
@cheeseng
Copy link

@nicolasstucki just to ping that the problem can still the reproduced consistently in https://github.com/cheeseng/scala3-inline-problem , I have updated the scala version to the latest 3.1.3 and it still has the problem.

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Jun 27, 2022

Minimization

tests/pos-macros/Macro_1.scala

import scala.quoted.*

class Wrapper[T](t: T):
  inline def showType: String = ${ Wrapper.showTypeImpl[T]}

object Wrapper:
  def showTypeImpl[U](using Quotes): Expr[String] = Expr("foo")

tests/pos-macros/Test_2.scala

class Person

def test =  Wrapper(new Person).showType

Note: must be compiled separately (if we suspend the second compilation unit, it works)

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jun 27, 2022
The `macroDependencies` method collects all the definitions that are
required to interpret the contents of the splice (using JVM-reflection).
We do not care about type parameters because those are erased and
the interpreter emulates erased semantics.

Fixes scala#12498
@nicolasstucki
Copy link
Contributor

How I fixed it

  • I looked for the Cannot call macro class Person defined in the same source file message in the compiler. Specifically the Cannot call macro substring. I found this error reporting https://github.com/lampepfl/dotty/blob/16ded76a00a993492611faa1e56689aeba0a4aee/compiler/src/dotty/tools/dotc/typer/Inliner.scala#L1856
  • Above this we collect some dependencies that we will check. There are references to compiled code in that we need to be able to interpret the top-level splice. https://github.com/lampepfl/dotty/blob/16ded76a00a993492611faa1e56689aeba0a4aee/compiler/src/dotty/tools/dotc/typer/Inliner.scala#L1851
  • I printed the body and its dependencies and found that Person was listed in it. Person is only used in a type arguments and is therefore erased when compiled. Therfore it should not have been in the dependencies.
  • I checked macroDependencies and saw that we do not handle the TypeApply case. This implies that all symbol references in the type argument will be collected as if they were terms.
  • I added a case TypeApply that ignores the type argument and collects references from the prefix of the application. This worked.
  • Then I wondered if this should happen at all levels or just at level -1. We only interpret code that is at level -1 and therefore it should only be at level -1.
  • I checked the if I needed a guard for the TypeApply case and found that we already have an if (level != 1) ... else making sure that we are at level -1. I noticed that level == -1 was in another guard unnecessarily and removed it.

Note: when adding the case TypeApply(fun, _) => case I used by mistake foldOver(syms, fun) instead of apply(syms, fun). This made the logic ignore Ident prefixes of the application and therefore some dependencies were identified. This made some tests fail because they were expected use suspension of the compilation unit that contained the dependency.

@kitlangton
Copy link

A hero! Thank you @nicolasstucki!

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jun 28, 2022
The `macroDependencies` method collects all the definitions that are
required to interpret the contents of the splice (using JVM-reflection).
We do not care about type parameters because those are erased and
the interpreter emulates erased semantics.

Fixes scala#12498
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jul 4, 2022
The `macroDependencies` method collects all the definitions that are
required to interpret the contents of the splice (using JVM-reflection).
We do not care about type parameters because those are erased and
the interpreter emulates erased semantics.

Fixes scala#12498
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jul 4, 2022
We can have different kinds of type references in the contents of a
splice. We do not care about types because those are erased and the
interpreter emulates erased semantics. These types are in |Tree|s that
extend |TypeTree| or |RefTree| referring to type.

Fixes scala#12498
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Jul 4, 2022
We can have different kinds of type references in the contents of a
splice. We do not care about types because those are erased and the
interpreter emulates erased semantics. These types are in `Tree`s that
extend `TypeTree` or `RefTree` referring to type.

Fixes scala#12498
@Kordyjan Kordyjan added this to the 3.2.1 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants