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

Scala 3 migration warning for implicits found in package prefix #10621

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ trait Contexts { self: Analyzer =>

private def collectImplicits(syms: Scope, pre: Type): List[ImplicitInfo] =
for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported = false))
yield new ImplicitInfo(sym.name, pre, sym)
yield new ImplicitInfo(sym.name, pre, sym, inPackagePrefix = false)

private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = if (isExcludedRootImport(imp)) List() else {
val qual = imp.qual
Expand All @@ -1114,12 +1114,12 @@ trait Contexts { self: Analyzer =>
// Looking up implicit members in the package, rather than package object, here is at least
// consistent with what is done just below for named imports.
for (sym <- qual.tpe.implicitMembers.toList if isQualifyingImplicit(sym.name, sym, pre, imported = true))
yield new ImplicitInfo(sym.name, pre, sym, imp, sel)
yield new ImplicitInfo(sym.name, pre, sym, importInfo = imp, importSelector = sel)
case (sel @ ImportSelector(from, _, to, _)) :: sels1 =>
var impls = collect(sels1).filter(_.name != from)
if (!sel.isMask)
withQualifyingImplicitAlternatives(imp, to, pre) { sym =>
impls = new ImplicitInfo(to, pre, sym, imp, sel) :: impls
impls = new ImplicitInfo(to, pre, sym, importInfo = imp, importSelector = sel) :: impls
}
impls
}
Expand Down
47 changes: 30 additions & 17 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,20 @@ trait Implicits extends splain.SplainData {
if (settings.areStatisticsEnabled) statistics.stopCounter(findMemberImpl, findMemberStart)
if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeImpl, subtypeStart)

if (result.isSuccess && settings.lintImplicitRecursion && result.tree.symbol != null) {
val rts = result.tree.symbol
val s = if (rts.isAccessor) rts.accessed else if (rts.isModule) rts.moduleClass else rts
if (s != NoSymbol && context.owner.hasTransOwner(s))
context.warning(result.tree.pos, s"Implicit resolves to enclosing $rts", WarningCategory.WFlagSelfImplicit)
val rts = result.tree.symbol
if (result.isSuccess && rts != null) {
if (settings.lintImplicitRecursion) {
val s = if (rts.isAccessor) rts.accessed else if (rts.isModule) rts.moduleClass else rts
if (s != NoSymbol && context.owner.hasTransOwner(s))
context.warning(result.tree.pos, s"Implicit resolves to enclosing $rts", WarningCategory.WFlagSelfImplicit)
}

if (result.inPackagePrefix && currentRun.isScala3) {
val msg =
s"""Implicit $rts was found in a package prefix of the required type, which is not part of the implicit scope in Scala 3.
|For migration, add `import ${rts.fullNameString}`.""".stripMargin
context.warning(result.tree.pos, msg, WarningCategory.Scala3Migration)
}
}
implicitSearchContext.emitImplicitDictionary(result)
}
Expand Down Expand Up @@ -194,8 +203,8 @@ trait Implicits extends splain.SplainData {
* that were instantiated by the winning implicit.
* @param undetparams undetermined type parameters
*/
class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter, val undetparams: List[Symbol], val implicitInfo: ImplicitInfo = null) {
override def toString = s"SearchResult($tree, ${if (subst.isEmpty) "" else subst})"
class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter, val undetparams: List[Symbol], val inPackagePrefix: Boolean = false, val implicitInfo: ImplicitInfo = null) {
override def toString = s"SearchResult($tree, ${if (subst.isEmpty) "" else subst}, $inPackagePrefix)"

def isFailure = false
def isAmbiguousFailure = false
Expand All @@ -222,7 +231,7 @@ trait Implicits extends splain.SplainData {
* @param pre The prefix type of the implicit
* @param sym The symbol of the implicit
*/
class ImplicitInfo (val name: Name, val pre: Type, val sym: Symbol, val importInfo: ImportInfo = null, val importSelector: ImportSelector = null) {
class ImplicitInfo(val name: Name, val pre: Type, val sym: Symbol, val inPackagePrefix: Boolean = false, val importInfo: ImportInfo = null, val importSelector: ImportSelector = null) {
private[this] var tpeCache: Type = null
private[this] var depolyCache: Type = null
private[this] var isErroneousCache: TriState = TriState.Unknown
Expand Down Expand Up @@ -291,7 +300,8 @@ trait Implicits extends splain.SplainData {
case that: ImplicitInfo =>
this.name == that.name &&
this.pre =:= that.pre &&
this.sym == that.sym
this.sym == that.sym &&
this.inPackagePrefix == that.inPackagePrefix
case _ => false
}
override def hashCode = {
Expand Down Expand Up @@ -602,7 +612,7 @@ trait Implicits extends splain.SplainData {
} else {
val ref = context.refByNameImplicit(pt)
if(ref != EmptyTree)
new SearchResult(ref, EmptyTreeTypeSubstituter, Nil)
new SearchResult(ref, EmptyTreeTypeSubstituter, Nil, inPackagePrefix = info.inPackagePrefix)
else {
@tailrec
def loop(ois: List[OpenImplicit], isByName: Boolean): Option[OpenImplicit] =
Expand All @@ -617,7 +627,7 @@ trait Implicits extends splain.SplainData {
recursiveImplicit match {
case Some(rec) =>
val ref = atPos(pos.focus)(context.linkByNameImplicit(rec.pt))
new SearchResult(ref, EmptyTreeTypeSubstituter, Nil)
new SearchResult(ref, EmptyTreeTypeSubstituter, Nil, inPackagePrefix = info.inPackagePrefix)
case None =>
try {
context.openImplicits = OpenImplicit(info, pt, tree, isView, isByNamePt) :: context.openImplicits
Expand Down Expand Up @@ -957,7 +967,7 @@ trait Implicits extends splain.SplainData {
splainPushImplicitSearchFailure(itree3, pt, err)
fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg)
case None =>
val result = new SearchResult(unsuppressMacroExpansion(itree3), subst, context.undetparams)
val result = new SearchResult(unsuppressMacroExpansion(itree3), subst, context.undetparams, inPackagePrefix = info.inPackagePrefix)
if (settings.areStatisticsEnabled) statistics.incCounter(foundImplicits)
typingLog("success", s"inferred value of type $ptInstantiated is $result")
result
Expand Down Expand Up @@ -1205,7 +1215,7 @@ trait Implicits extends splain.SplainData {
val res = typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
if (res.isFailure) res
else
new SearchResult(res.tree, res.subst, res.undetparams, firstPending)
new SearchResult(res.tree, res.subst, res.undetparams, res.inPackagePrefix, implicitInfo = firstPending)
lrytz marked this conversation as resolved.
Show resolved Hide resolved
}
else SearchFailure
} finally {
Expand Down Expand Up @@ -1328,10 +1338,13 @@ trait Implicits extends splain.SplainData {
if (symInfos.exists(_.isSearchedPrefix))
infoMap(sym) = SearchedPrefixImplicitInfo(pre) :: symInfos
else if (pre.isStable && !pre.typeSymbol.isExistentiallyBound) {
val pre1 =
if (sym.isPackageClass) sym.packageObject.typeOfThis
else singleType(pre, companionSymbolOf(sym, context))
val preInfos = pre1.implicitMembers.iterator.map(mem => new ImplicitInfo(mem.name, pre1, mem))
val (pre1, inPackagePrefix) =
if (sym.isPackageClass) (sym.packageObject.typeOfThis, true)
else (singleType(pre, companionSymbolOf(sym, context)), false)
lrytz marked this conversation as resolved.
Show resolved Hide resolved
val preInfos = {
if (currentRun.isScala3Cross && inPackagePrefix) Iterator.empty
else pre1.implicitMembers.iterator.map(mem => new ImplicitInfo(mem.name, pre1, mem, inPackagePrefix = inPackagePrefix))
}
val mergedInfos = if (symInfos.isEmpty) preInfos else {
if (shouldLogAtThisPhase && symInfos.exists(!_.dependsOnPrefix)) log {
val nonDepInfos = symInfos.iterator.filterNot(_.dependsOnPrefix).mkString("(", ", ", ")")
Expand Down
4 changes: 4 additions & 0 deletions test/files/neg/t12919-3cross.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
t12919-3cross.scala:24: error: No implicit Ordering defined for a.A.
def f(xs: List[a.A]) = xs.sorted // not found
^
1 error
36 changes: 36 additions & 0 deletions test/files/neg/t12919-3cross.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// scalac: -Xsource:3-cross

package object a {
implicit val aOrd: Ordering[A] = null
implicit val bOrd: Ordering[b.B] = null
}

package a {
class A

package aa {
class U {
// implicit is in an enclosing package of the callsite, not in the path of the implicit's type
def f(xs: List[a.A]) = xs.sorted // ok
def g(xs: List[b.B]) = xs.sorted // ok
}
}
}

package b {
class B

class V {
def f(xs: List[a.A]) = xs.sorted // not found
}
}

package c {
import a._

class W {
def f(xs: List[a.A]) = xs.sorted // ok
def g(xs: List[b.B]) = xs.sorted // ok
}
}

7 changes: 7 additions & 0 deletions test/files/neg/t12919.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
t12919.scala:24: error: Implicit value aOrd was found in a package prefix of the required type, which is not part of the implicit scope in Scala 3.
For migration, add `import a.aOrd`.
Scala 3 migration messages are errors under -Xsource:3. Use -Wconf / @nowarn to filter them or add -Xmigration to demote them to warnings.
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=scala3-migration, site=b.V.f
def f(xs: List[a.A]) = xs.sorted // warn
^
1 error
36 changes: 36 additions & 0 deletions test/files/neg/t12919.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// scalac: -Xsource:3 -Werror

package object a {
implicit val aOrd: Ordering[A] = null
implicit val bOrd: Ordering[b.B] = null
}

package a {
class A

package aa {
class U {
// implicit is in an enclosing package of the callsite, not in the path of the implicit's type
def f(xs: List[a.A]) = xs.sorted // ok
def g(xs: List[b.B]) = xs.sorted // ok
}
}
}

package b {
class B

class V {
def f(xs: List[a.A]) = xs.sorted // warn
}
}

package c {
import a._

class W {
def f(xs: List[a.A]) = xs.sorted // ok
def g(xs: List[b.B]) = xs.sorted // ok
}
}