Skip to content

Commit 9f4179a

Browse files
committed
Don't approximate a type using Nothing as prefix
Approximate range(Nothing, hi) to Nothing & hi instead of Nothing. This avoids creating TypeRefs with a Nothing prefix which manifest themselves down the line with an error like: Cannot resolve reference to type path.type.AbsMember. The classfile defining the type might be missing from the classpath. val z1 = transition(di).ext(1) // error ^ Due to a MissingType thrown from TypeErasure#sigName. This change required tweaking Type#findMember to handle WildcardTypes instead of returning NoDenotation. This was only required for two tests (tests/pos/i7414.scala and tests/pos/i13842.scala) where `wildApprox` ends up creating a TermRef whose underlying type is a WildcardType, because of a type parameter being substituted by a wildcard, which seems legitimate when we're using `wildApprox`. The presentation compiler also had a special case for Nothing appearing in completion which had to be adapted. Fixes #23530
1 parent 03a54a7 commit 9f4179a

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,8 @@ object Types extends TypeUtils {
849849
goOr(tp)
850850
case tp: JavaArrayType =>
851851
defn.ObjectType.findMember(name, pre, required, excluded)
852+
case tp: WildcardType =>
853+
go(tp.bounds)
852854
case err: ErrorType =>
853855
newErrorSymbol(pre.classSymbol.orElse(defn.RootClass), name, err.msg)
854856
case _ =>
@@ -6482,9 +6484,24 @@ object Types extends TypeUtils {
64826484
abstract class ApproximatingTypeMap(using Context) extends TypeMap { thisMap =>
64836485

64846486
protected def range(lo: Type, hi: Type): Type =
6485-
if (variance > 0) hi
6486-
else if (variance < 0) lo
6487-
else if (lo `eq` hi) lo
6487+
if variance > 0 then hi
6488+
else if variance < 0 then
6489+
if (lo eq defn.NothingType) && hi.hasSimpleKind then
6490+
// Approximate by Nothing & hi instead of just Nothing, in case the
6491+
// approximated type is used as the prefix of another type (this would
6492+
// lead to a type with a `NoDenotation` denot and a possible
6493+
// MissingType in `TypeErasure#sigName`).
6494+
//
6495+
// Note that we cannot simply check for a `Nothing` prefix in
6496+
// `derivedSelect`, because the substitution might be done lazily (for
6497+
// example if Nothing is the type of a parameter being depended on in
6498+
// a MethodType)
6499+
//
6500+
// Test case in tests/pos/i23530.scala
6501+
AndType(lo, hi)
6502+
else
6503+
lo
6504+
else if lo `eq` hi then lo
64886505
else Range(lower(lo), upper(hi))
64896506

64906507
protected def emptyRange = range(defn.NothingType, defn.AnyType)

presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ import dotty.tools.dotc.core.Names
1717
import dotty.tools.dotc.core.Names.Name
1818
import dotty.tools.dotc.core.Names.NameOrdering
1919
import dotty.tools.dotc.core.StdNames
20-
import dotty.tools.dotc.core.Symbols.NoSymbol
21-
import dotty.tools.dotc.core.Symbols.Symbol
20+
import dotty.tools.dotc.core.Symbols.*
2221
import dotty.tools.dotc.core.Types.*
2322
import dotty.tools.dotc.core.Types.Type
2423
import dotty.tools.dotc.printing.RefinedPrinter
@@ -256,7 +255,6 @@ class ShortenedTypePrinter(
256255
end hoverSymbol
257256

258257
def isImportedByDefault(sym: Symbol): Boolean =
259-
import dotty.tools.dotc.core.Symbols.defn
260258
lazy val effectiveOwner = sym.effectiveOwner
261259
sym.isType && (effectiveOwner == defn.ScalaPackageClass || effectiveOwner == defn.ScalaPredefModuleClass)
262260

@@ -498,9 +496,9 @@ class ShortenedTypePrinter(
498496
val info = nameToInfo
499497
.get(param.name)
500498
.flatMap { info =>
501-
// In some cases, paramInfo becomes Nothing (e.g. CompletionOverrideSuite#cake)
499+
// In some cases, paramInfo becomes `... & Nothing` (e.g. CompletionOverrideSuite#cake)
502500
// which is meaningless, in that case, fallback to param.info
503-
if info.isNothingType then None
501+
if info <:< defn.NothingType then None
504502
else Some(info)
505503
}
506504
.getOrElse(param.info)

tests/pos/i23530.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait TestContainer:
2+
trait TestPath:
3+
type AbsMember
4+
5+
extension (path: TestPath)
6+
infix def ext(color: path.AbsMember): Unit = ???
7+
infix def ext(other: Int): Unit = ???
8+
9+
object Repro:
10+
val dc2: TestContainer = ???
11+
import dc2.TestPath
12+
13+
def transition(path: TestPath)(using DummyImplicit): TestPath = ???
14+
15+
def test: Unit =
16+
val di: TestPath = ???
17+
// error
18+
val z1 = transition(di).ext(1)

0 commit comments

Comments
 (0)