From 3bbd9e3d97c37b8d239446a24e71b1a96ef587b0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 10 Nov 2022 11:36:49 -0800 Subject: [PATCH] Mark import used after implicit ranking chooses it --- .../src/dotty/tools/dotc/core/Types.scala | 4 ++-- .../dotty/tools/dotc/typer/Implicits.scala | 20 +++++++++++++++---- .../dotty/tools/dotc/typer/ImportInfo.scala | 2 ++ tests/neg/t12690a.scala | 16 +++++++++++++++ tests/neg/t12690b.scala | 16 +++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/neg/t12690a.scala create mode 100644 tests/neg/t12690b.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9a8e2a051ad4..3eda3ad7f690 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2736,8 +2736,8 @@ object Types { override def eql(that: Type): Boolean = this eq that // safe because named types are hash-consed separately } - /** A reference to an implicit definition. This can be either a TermRef or a - * Implicits.RenamedImplicitRef. + /** A reference to an implicit definition. This can be either a TermRef or + * an Implicits.{ImportedImplicitRef, RenamedImplicitRef}. */ trait ImplicitRef { def implicitName(using Context): TermName diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 9218d69a9ac4..73f6fdf6283b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -44,6 +44,7 @@ object Implicits: */ class ImportedImplicitRef(val underlyingRef: TermRef, val importInfo: ImportInfo, val selector: Int) extends ImplicitRef: def implicitName(using Context): TermName = underlyingRef.implicitName + override def toString = s"ImportedImplicitRef($underlyingRef, $importInfo, selector=$selector)" /** An implicit definition `ImplicitRef` that is visible under a different name, `alias`. * Gets generated if an implicit ref is imported via a renaming import. @@ -51,6 +52,7 @@ object Implicits: class RenamedImplicitRef(underlyingRef: TermRef, importInfo: ImportInfo, selector: Int, val alias: TermName) extends ImportedImplicitRef(underlyingRef, importInfo, selector): override def implicitName(using Context): TermName = alias + override def toString = s"ImportedImplicitRef($underlyingRef, $importInfo, selector=$selector, alias=$alias)" /** Both search candidates and successes are references with a specific nesting level. */ sealed trait RefAndLevel { @@ -1157,9 +1159,6 @@ trait Implicits: SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } else - cand match - case Candidate(k: ImportedImplicitRef, _, _) => ctx.usages.use(k.importInfo, k.importInfo.selectors(k.selector)) - case _ => SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) } end typedImplicit @@ -1485,7 +1484,20 @@ trait Implicits: validateOrdering(ord) throw ex - rank(sort(eligible), NoMatchingImplicitsFailure, Nil) + @tailrec + def markSelector(allEligible: List[Candidate], foundRef: ImplicitRef): Unit = + allEligible match + case Candidate(k: ImportedImplicitRef, _, _) :: _ if k.underlyingRef == foundRef => + ctx.usages.use(k.importInfo, k.importInfo.selectors(k.selector)) + case _ :: rest => markSelector(rest, foundRef) + case nil => () + + rank(sort(eligible), NoMatchingImplicitsFailure, Nil) match { + case res: SearchSuccess if !ctx.mode.is(Mode.ImplicitExploration) => + markSelector(eligible, res.ref) + res + case res => res + } end searchImplicit def isUnderSpecifiedArgument(tp: Type): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 5815e9e6890a..a222009205d7 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -59,6 +59,8 @@ class ImportInfo(symf: Context ?=> Symbol, val enclosingSpan: Span, val isRootImport: Boolean = false) extends Showable { + override def toString = selectors.mkString(s"ImportInfo#$hashCode(", ",", s") at $enclosingSpan") + private def symNameOpt = qualifier match { case ref: untpd.RefTree => Some(ref.name.asTermName) case _ => None diff --git a/tests/neg/t12690a.scala b/tests/neg/t12690a.scala new file mode 100644 index 000000000000..1e41727d3333 --- /dev/null +++ b/tests/neg/t12690a.scala @@ -0,0 +1,16 @@ + +// scalac: -Werror -Wunused:imports + +class X +class Y extends X +object A { implicit val x: X = new X } +object B { implicit val y: Y = new Y } +class C { + import B._ + import A._ // error: unused + def t = implicitly[X] +} + +object Test extends App { + println(new C().t) +} diff --git a/tests/neg/t12690b.scala b/tests/neg/t12690b.scala new file mode 100644 index 000000000000..8ab16c56539b --- /dev/null +++ b/tests/neg/t12690b.scala @@ -0,0 +1,16 @@ + +// scalac: -Werror -Wunused:imports + +class X +class Y extends X +object A { implicit val x: X = new X } +object B { implicit val y: Y = new Y } +class C { + import A._ // error: unused + import B._ + def t = implicitly[X] +} + +object Test extends App { + println(new C().t) +}