Skip to content

Compiler plugin between parser and typer combined with macro usage causes duplicate compilation and compile error #18384

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

Open
mrdziuban opened this issue Aug 10, 2023 · 4 comments

Comments

@mrdziuban
Copy link

Compiler version

3.3.0, also the latest 3.3.1-RC5 and 3.4.0-RC1-bin-20230809-c5adafc-NIGHTLY

Minimized code

https://github.com/mrdziuban/dotty-compiler-plugin-issue/tree/duplicated-code

The compiler plugin inserts a phase between the parser and the typer, but the phase doesn't do anything. The code in the tests directory defines a simple macro in Macro.scala and a call to the macro in Test.scala.

I believe both the placement of the compiler plugin and the existence of the macro matter -- if I change either one of them, i.e. run the plugin after the typer or remove the macro, then the issue stops happening.

Output

-- [E161] Naming Error: /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala:3:0
3 |object test {
  |^
  |test is already defined as object test in /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala

When I enable -Xprint:typer, the full sbt output that shows the duplicated object test looks like this:

[info] [[syntax trees at end of                     typer]] // /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Macro.scala
[info] package example {
[info]   import scala.quoted.*
[info]   final lazy module val Macro$package: example.Macro$package =
[info]     new example.Macro$package()
[info]   final module class Macro$package() extends Object() {
[info]     this: example.Macro$package.type =>
[info]     private[example] def idMacroImpl[A >: Nothing <: Any](
[info]       implicit evidence$1: quoted.Type[A], ctx: quoted.Quotes):
[info]       quoted.Expr[A => A] =
[info]       '{
[info]         {
[info]           def $anonfun(a: A): A =
[info]             {
[info]               a
[info]             }
[info]           closure($anonfun)
[info]         }
[info]       }.apply(ctx)
[info]     inline def idMacro[A >: Nothing <: Any]: A => A =
[info]       ${
[info]         {
[info]           def $anonfun(using evidence$2: quoted.Quotes): quoted.Expr[A => A] =
[info]             example.Macro$package.inline$idMacroImpl[A](
[info]               quoted.Type.of[A](evidence$2), evidence$2)
[info]           closure($anonfun)
[info]         }
[info]       }:(A => A)
[info]     def inline$idMacroImpl[A](implicit evidence$1: quoted.Type[A],
[info]       implicit ctx: quoted.Quotes): quoted.Expr[A => A] =
[info]       example.Macro$package.idMacroImpl[A](evidence$1, ctx)
[info]   }
[info] }
[info] [[syntax trees at end of                     typer]] // /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala
[info] package example {
[info]   final lazy module val test: example.test = new example.test()
[info]   final module class test() extends Object() { this: example.test.type =>
[info]     val intId: Int => Int = example.idMacro[Int]
[info]   }
[info] }
[error] -- [E161] Naming Error: /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala:3:0
[error] 3 |object test {
[error]   |^
[error]   |test is already defined as object test in /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala
[info] [[syntax trees at end of                     typer]] // /Users/matt/dotty-compiler-plugin-issue/tests/src/main/scala/Test.scala
[info] package example {
[info]   final lazy module val test$1: example.test = new example.test()
[info]   final module class test$2() extends Object() { this: example.test.type =>
[info]     val intId: Int => Int = example.idMacro[Int]
[info]   }
[info] }

Expectation

Compilation should succeed

@mrdziuban mrdziuban added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 10, 2023
@smarter
Copy link
Member

smarter commented Aug 10, 2023

Err, you're not supposed to be able to make a non-ResearchPlugin that affects typechecking (https://docs.scala-lang.org/scala3/reference/changed-features/compiler-plugins.html#), so a plugin before typer should be rejected.

@smarter smarter added area:tooling and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 10, 2023
@mrdziuban
Copy link
Author

Maybe I should have kept my mouth shut then 😬 , that would unfortunately make a compiler plugin I maintain obsolete -- https://github.com/mblink/nowarn-plugin

I would prefer to create subclasses of annotation.nowarn, but there are issues in both scala 2 and 3

@smarter
Copy link
Member

smarter commented Aug 10, 2023

Hmm I see, this is a bit annoying to support since the parameter passed to the parent class isn't reachable from the symbol of the class, but it's clear that we should either support it or deprecate extending nowarn. This isn't exactly the same, but the following works in Scala 2 and 3 and could be a good alternative I think:

import scala.annotation.nowarn

object Constants {
  final val unused = "msg=(unused|never used)"
}
import Constants._

@nowarn(unused)
object test {
  import scala.collection.immutable.SortedSet
}

@soronpo
Copy link
Contributor

soronpo commented Aug 11, 2023

Could be related to #17378

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants