Skip to content

Quoted API has broken impl for fields (case fields / declared fields / ...) #22584

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
Kalin-Rudnicki opened this issue Feb 12, 2025 · 9 comments · May be fixed by #22603
Open

Quoted API has broken impl for fields (case fields / declared fields / ...) #22584

Kalin-Rudnicki opened this issue Feb 12, 2025 · 9 comments · May be fixed by #22603
Assignees
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API area:private options Issues tied to -Y private/internal compiler settings. itype:bug

Comments

@Kalin-Rudnicki
Copy link

Kalin-Rudnicki commented Feb 12, 2025

Compiler version

3.6.3
note, was not an issue before I bumped from 3.3.0
edit: I reverted to 3.3.0, and apparently it was still an issue, actually

Minimized code

TypeRepr.of[A].typeSymbol.caseFields.map(_.tree)
TypeRepr.of[A].typeSymbol.declaredFields.map(_.tree)

Output

[error]    |Exception occurred while executing macro expansion.
[error]    |scala.MatchError: DefDef(int,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Int)],EmptyTree) (of class dotty.tools.dotc.ast.Trees$DefDef)

Expectation

  1. Expect that compiler wont throw MatchError
  2. In 3.3.0, these were returning as ValDef

Code I was using:

typeSymbol: Symbol = typeRepr.typeSymbol
caseFieldSymbols: List[Symbol] = typeSymbol.caseFields

caseFieldSymbols.traverse { sym =>
            sym.tree match {
              case valDef: ValDef => valDef.asRight
              case _      => s"Somehow not a `ValDef`: ${sym.fullName}".asLeft
            }
@Kalin-Rudnicki Kalin-Rudnicki added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Feb 12, 2025
@Kalin-Rudnicki
Copy link
Author

DefDef(int,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Int)],EmptyTree) (of class dotty.tools.dotc.ast.Trees$DefDef)
    dotty.tools.dotc.quoted.reflect.FromSymbol$.valDefFromSym(FromSymbol.scala:50)
    dotty.tools.dotc.quoted.reflect.FromSymbol$.definitionFromSym(FromSymbol.scala:22)
    scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.tree(QuotesImpl.scala:2716)
    scala.quoted.runtime.impl.QuotesImpl$reflect$SymbolMethods$.tree(QuotesImpl.scala:2716)
    oxygen.meta.K0$ProductGeneric$.$anonfun$3(K0.scala:152)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    scala.collection.immutable.List.foreach(List.scala:334)
    oxygen.meta.K0$ProductGeneric$.attemptOf$$anonfun$2$$anonfun$1$$anonfun$2(K0.scala:148)
    scala.util.Either.map(Either.scala:390)
    oxygen.meta.K0$ProductGeneric$.attemptOf$$anonfun$2$$anonfun$1(K0.scala:124)
    oxygen.meta.K0$ProductGeneric$.attemptOf$$anonfun$2$$anonfun$adapted$1(K0.scala:117)
    scala.util.Either.flatMap(Either.scala:360)
    oxygen.meta.K0$ProductGeneric$.attemptOf$$anonfun$2(K0.scala:117)
    scala.util.Either.flatMap(Either.scala:360)
    oxygen.meta.K0$ProductGeneric$.attemptOf(K0.scala:111)
    oxygen.meta.K0$Generic$.attemptOf(K0.scala:36)
    oxygen.meta.K0$Generic$.of(K0.scala:23)
    oxygen.meta.ExampleTypeClasses$Show$.derivedImpl(ExampleTypeClasses.scala:25)
    oxygen.meta.ExampleTypeClasses$Show$.inline$derivedImpl(ExampleTypeClasses.scala:19)
    java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.base/java.lang.reflect.Method.invoke(Method.java:566)
    dotty.tools.dotc.quoted.Interpreter.interpretedStaticMethodCall$$anonfun$1(Interpreter.scala:174)
    dotty.tools.dotc.quoted.Interpreter.stopIfRuntimeException(Interpreter.scala:231)
    dotty.tools.dotc.quoted.Interpreter.interpretedStaticMethodCall(Interpreter.scala:174)
    dotty.tools.dotc.quoted.Interpreter.interpretTree(Interpreter.scala:80)
    dotty.tools.dotc.transform.Splicer$SpliceInterpreter.interpretTree(Splicer.scala:267)
    dotty.tools.dotc.quoted.Interpreter.interpretTree$$anonfun$2(Interpreter.scala:99)
    dotty.tools.dotc.transform.Splicer$.$anonfun$2(Splicer.scala:62)
    scala.Option.fold(Option.scala:263)
    dotty.tools.dotc.transform.Splicer$.splice(Splicer.scala:62)
    dotty.tools.dotc.inlines.Inliner.dotty$tools$dotc$inlines$Inliner$$expandMacro(Inliner.scala:1088)
    dotty.tools.dotc.inlines.Inliner$InlineTyper.typedSplice(Inliner.scala:835)
    dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3546)
    dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3585)
    dotty.tools.dotc.inlines.Inliner$InlineTyper.typedUnadapted(Inliner.scala:940)
    dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
    dotty.tools.dotc.typer.Typer.typed(Typer.scala:3666)
    dotty.tools.dotc.typer.ReTyper.typedTyped(ReTyper.scala:65)
    dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3505)
    dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3585)
    dotty.tools.dotc.inlines.Inliner$InlineTyper.typedUnadapted(Inliner.scala:940)
    dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
    dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
    dotty.tools.dotc.typer.Typer.typed(Typer.scala:3666)
    dotty.tools.dotc.inlines.Inliner.inlined(Inliner.scala:677)
    dotty.tools.dotc.inlines.Inlines$InlineCall.expand(Inlines.scala:495)
    dotty.tools.dotc.inlines.Inlines$.inlineCall(Inlines.scala:163)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:94)
    dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1600)
    dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
    dotty.tools.dotc.ast.TreeMapWithTrackedStats.transform(TreeMapWithTrackedStats.scala:63)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transformMemberDef(Inlining.scala:135)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:71)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1289)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1289)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1291)
    dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:58)
    dotty.tools.dotc.ast.TreeMapWithTrackedStats.transform(TreeMapWithTrackedStats.scala:63)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:95)
    dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1605)
    dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
    dotty.tools.dotc.ast.TreeMapWithTrackedStats.transform(TreeMapWithTrackedStats.scala:60)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transformMemberDef(Inlining.scala:135)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:71)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1289)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1289)
    dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1291)
    dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1616)
    dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
    dotty.tools.dotc.ast.TreeMapWithTrackedStats.transform(TreeMapWithTrackedStats.scala:48)
    dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:75)
    dotty.tools.dotc.transform.Inlining$$anon$2.transform(Inlining.scala:57)
    dotty.tools.dotc.transform.MacroTransform.run(MacroTransform.scala:20)
    dotty.tools.dotc.transform.Inlining.run(Inlining.scala:39)
    dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:380)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    scala.collection.immutable.List.foreach(List.scala:334)
    dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:373)
    dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:343)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
    dotty.tools.dotc.Run.runPhases$1(Run.scala:336)
    dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:383)
    dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:395)
    dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
    dotty.tools.dotc.Run.compileUnits(Run.scala:395)
    dotty.tools.dotc.Run.compileUnits(Run.scala:288)
    dotty.tools.dotc.Run.compileSuspendedUnits(Run.scala:409)
    dotty.tools.dotc.Driver.finish(Driver.scala:63)
    dotty.tools.dotc.Driver.doCompile(Driver.scala:38)
    dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:141)
    dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)
    sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
    sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$7(MixedAnalyzingCompiler.scala:194)
    scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
    sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:249)
    sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4(MixedAnalyzingCompiler.scala:184)
    sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4$adapted(MixedAnalyzingCompiler.scala:164)
    sbt.internal.inc.JarUtils$.withPreviousJar(JarUtils.scala:239)
    sbt.internal.inc.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:164)
    sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:212)
    sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:534)
    sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:534)
    sbt.internal.inc.Incremental$.$anonfun$apply$3(Incremental.scala:178)
    sbt.internal.inc.Incremental$.$anonfun$apply$3$adapted(Incremental.scala:176)
    sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:454)
    sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:117)
    sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
    sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
    sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:265)
    sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:409)
    sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:496)
    sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:396)
    sbt.internal.inc.Incremental$.apply(Incremental.scala:170)
    sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:534)
    sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:488)
    sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
    sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:425)
    sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
    sbt.Defaults$.compileIncrementalTaskImpl(Defaults.scala:2427)
    sbt.Defaults$.$anonfun$compileIncrementalTask$2(Defaults.scala:2377)
    sbt.internal.server.BspCompileTask$.$anonfun$compute$1(BspCompileTask.scala:41)
    sbt.internal.io.Retry$.apply(Retry.scala:47)
    sbt.internal.io.Retry$.apply(Retry.scala:29)
    sbt.internal.io.Retry$.apply(Retry.scala:24)
    sbt.internal.server.BspCompileTask$.compute(BspCompileTask.scala:41)
    sbt.Defaults$.$anonfun$compileIncrementalTask$1(Defaults.scala:2375)
    scala.Function1.$anonfun$compose$1(Function1.scala:49)
    sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:63)
    sbt.std.Transform$$anon$4.work(Transform.scala:69)
    sbt.Execute.$anonfun$submit$2(Execute.scala:283)
    sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
    sbt.Execute.work(Execute.scala:292)
    sbt.Execute.$anonfun$submit$1(Execute.scala:283)
    sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
    sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
    java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    java.base/java.lang.Thread.run(Thread.java:829)

@Kalin-Rudnicki
Copy link
Author

          println(s"sym.raw: ${sym.raw}")
          println(s"sym.raw.isValDef: ${sym.raw.isValDef}")
          println(s"sym.raw.isDefDef: ${sym.raw.isDefDef}")
          println(s"sym.raw.tree: ${sym.raw.tree}")
sym.raw: val int
sym.raw.isValDef: true
sym.raw.isDefDef: false
[error] -- Error: /home/kalin/dev/current/oxygen/modules/meta/src/test/scala/oxygen/meta/DeriveShowSpec.scala:17:27 
[error] 17 |  given Show[CaseClass1] = Show.derived
[error]    |                           ^^^^^^^^^^^^
[error]    |Exception occurred while executing macro expansion.
[error]    |scala.MatchError: DefDef(int,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Int)],EmptyTree) (of class dotty.tools.dotc.ast.Trees$DefDef)

@Kalin-Rudnicki
Copy link
Author

🤔 I just tried reverting back to 3.3.0, and its actually failing there as well

@soronpo
Copy link
Contributor

soronpo commented Feb 12, 2025

Please provide a fully reproducible minimization

@Kalin-Rudnicki
Copy link
Author

Kalin-Rudnicki commented Feb 12, 2025

oxygen.meta.tmp.Caller.scala

package oxygen.meta.tmp

import Types.*

object Caller extends scala.App {

  Macros.myMacro[MyClass1]

}

oxygen.meta.tmp.Types.scala

package oxygen.meta.tmp

object Types {

  final case class MyClass1(
      int: Int,
      string: String,
      boolean: Boolean,
  )

}

oxygen.meta.tmp.Macros.scala

package oxygen.meta.tmp

import scala.quoted.*

object Macros {

  inline def myMacro[A]: Unit = ${ myMacroImpl[A] }

  private def myMacroImpl[A](using quotes: Quotes, tpe: Type[A]): Expr[Unit] = {
    import quotes.reflect.*

    val typeRepr = TypeRepr.of[A]
    val typeSymbol = typeRepr.typeSymbol

    val caseFieldSymbols: List[Symbol] = typeSymbol.caseFields
    val caseFieldValDefs: List[ValDef] =
      caseFieldSymbols.map {
        _.tree match {
          case valDef: ValDef => valDef
          case _              => report.errorAndAbort("???")
        }
      }

    println(caseFieldValDefs)

    '{ () }
  }

}

@Kalin-Rudnicki
Copy link
Author

I think I have isolated the source as a part of reducing this...
the single line that makes the error appear/disappear is:

scalacOptions ++= Seq("-Yretain-trees"),

@prolativ
Copy link
Contributor

This is not a bug: https://dotty.epfl.ch/api/scala/quoted/Quotes$reflectModule$SymbolMethods.html#tree-d26
The -Yretain-trees flag is required if you want to use symbol.tree in a reliable way

@prolativ prolativ closed this as not planned Won't fix, can't repro, duplicate, stale Feb 12, 2025
@Kalin-Rudnicki
Copy link
Author

@prolativ this only works when retain trees is not added

  • retain trees flag added -> error
  • retain trees flag not added -> works

@prolativ
Copy link
Contributor

Sorry, you're right. That's really strange

@prolativ prolativ reopened this Feb 12, 2025
@prolativ prolativ added area:metaprogramming:reflection Issues related to the quotes reflection API and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Feb 12, 2025
@Gedochao Gedochao added the area:private options Issues tied to -Y private/internal compiler settings. label Feb 13, 2025
@jchyb jchyb self-assigned this Feb 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API area:private options Issues tied to -Y private/internal compiler settings. itype:bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants