From af2004f4f2c2558d84e2256585a5342cb99eebf3 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 3 Dec 2024 10:13:54 +1000 Subject: [PATCH 1/3] Exclude anon classes from dependency registration These can show in edge cases like: ``` package demo class SRC[_] class Filter class Refined { def select(cols: SRC[_]*) = new { def where(filter: Filter => Filter)(implicit d: DummyImplicit) = { new { def using(opt: Box[SRC[_]] => Some[SRC[_]])(implicit d: DummyImplicit) = opt } } } } ``` ``` package demo class Client { def temp = new Refined().select(null).where(null).using(null) } ``` This can leak an existential type owned by an anon-class into `Client`, at least prior to this compiler fix: https://github.com/scala/scala/pull/10940 --- .../compiler-bridge/src/main/scala/xsbt/Dependency.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala index e7425ca1b..08a13f355 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala @@ -291,9 +291,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with assert(fromClass.isClass, Feedback.expectedClassSymbol(fromClass)) val depClass = enclOrModuleClass(dep) val dependency = ClassDependency(fromClass, depClass) + // An anonymous class be the enclosing class of an existential type symbol inferred from refinements, + // prior to https://github.com/scala/scala/pull/10940. Allowing this here leads to a dependency on class name + // that does not exist. Guard against it here to avoid the issue with legacy compiler versions. if ( !cache.contains(dependency) && - !depClass.isRefinementClass + !depClass.isAnonOrRefinementClass ) { process(dependency) cache.add(dependency) @@ -484,7 +487,8 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => // TODO: typesTouchedDuringMacroExpansion can be provided by compiler - // in the form of tree attachment + // in the form + // of tree attachment val typesTouchedDuringMacroExpansion = original match { case Apply(TypeApply(_, args), _) => args.map(_.tpe) case TypeApply(_, args) => args.map(_.tpe) From f1547d0ea8b71e5aa3e28415c9881d14c4af99d1 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 3 Dec 2024 10:14:24 +1000 Subject: [PATCH 2/3] Add some notes to the saga of absent associatedFile --- .../src/main/scala/xsbt/Dependency.scala | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala index 08a13f355..e49e47865 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala @@ -189,6 +189,27 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with * but when it does we must ensure the incremental compiler tries its best no to lose * any dependency. Therefore, we do a last-time effort to get the origin of the symbol * by inspecting the classpath manually. + * + * UPDATE: This can also happen without compiler bugs if the symbol is simply uninitialized. + * Example, `class Client { def foo = Server.foo }`. When compiling client, the type `Foo` returned + * by `Server.foo` does not need to be initialized as we do not select from it or check its + * conformance to another type. + * + * Initializing `targetSymbol` before calling `assosicatedFile` would work but is problematic + * see zinc/zinc#949 + * + * Perhaps consider this? + * val file = targetSymbol.associatedFile match { + * case NoAbstractFile => sym.rawInfo match { + * case cfl: global.loaders.ClassfileLoader => + * val f = cfl.associatedFile(sym) // Gets the file from the loader + * if (f.exists) f else NoAbstractFile + * case f => f + * } + * } + * + * Or the status quo might just be perfectly fine -- if compilation doesn't need to force `Foo`, + * then there isn't a real dependency. */ val fqn = fullName(targetSymbol, '.', targetSymbol.moduleSuffix, false) global.findAssociatedFile(fqn) match { From c752dae13e3c3527e0d709165dccd888557868c5 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 4 Dec 2024 09:28:55 +1000 Subject: [PATCH 3/3] Update internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala Co-authored-by: Jiahui (Jerry) Tan <66892505+Friendseeker@users.noreply.github.com> --- internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala index e49e47865..6b1c2ec15 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala @@ -508,8 +508,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => // TODO: typesTouchedDuringMacroExpansion can be provided by compiler - // in the form - // of tree attachment + // in the form of tree attachment val typesTouchedDuringMacroExpansion = original match { case Apply(TypeApply(_, args), _) => args.map(_.tpe) case TypeApply(_, args) => args.map(_.tpe)