Skip to content

Commit d1b51dc

Browse files
authored
Explain unresolvable references better (#20477)
We run into problems when referring to a member of a self type of a class that it not also a member of the class from outside via an asSeenFrom. One example is in 11226.scala where we see: ```scala trait ManagedActorClassification { this: ActorEventBus => def unsubscribe(subscriber: Subscriber): Unit } class Unsubscriber(bus: ManagedActorClassification) { def test(a: ActorRef): Unit = bus.unsubscribe(a) // error } ``` The problem is that `unsubscribe` refers to the type `Subscriber` which is not resolvable as a member of `bus`. one idea could be to rule out type signatures like `unsubscribe`, similar how we rule out public signatures referring to private members. But this could rule out existing valid programs. For instance, the `unsubscribe` signature is unproblematic if it gets only called with prefixes that inherit `ActorEventBus`. You could say that the problem was instead that the type of `bus` was not specific enough. In the long term, maybe restricting the signature is the right move. But for now, we just try to give better error messages in the case of existing failures. Fixes #11226
2 parents a21d2af + e1ce6b9 commit d1b51dc

File tree

9 files changed

+95
-15
lines changed

9 files changed

+95
-15
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/TypeErrors.scala

+21-8
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,30 @@ end TypeError
5757
class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError:
5858
def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}"
5959

60-
class MissingType(pre: Type, name: Name)(using Context) extends TypeError:
61-
private def otherReason(pre: Type)(using Context): String = pre match {
62-
case pre: ThisType if pre.cls.givenSelfType.exists =>
63-
i"\nor the self type of $pre might not contain all transitive dependencies"
64-
case _ => ""
65-
}
60+
class MissingType(val pre: Type, val name: Name)(using Context) extends TypeError:
61+
62+
def reason(using Context): String =
63+
def missingClassFile =
64+
"The classfile defining the type might be missing from the classpath"
65+
val cls = pre.classSymbol
66+
val givenSelf = cls match
67+
case cls: ClassSymbol => cls.givenSelfType
68+
case _ => NoType
69+
pre match
70+
case pre: ThisType if pre.cls.givenSelfType.exists =>
71+
i"""$missingClassFile
72+
|or the self type of $pre might not contain all transitive dependencies"""
73+
case _ if givenSelf.exists && givenSelf.member(name).exists =>
74+
i"""$name exists as a member of the self type $givenSelf of $cls
75+
|but it cannot be called on a receiver whose type does not extend $cls"""
76+
case _ =>
77+
missingClassFile
78+
6679

6780
override def toMessage(using Context): Message =
6881
if ctx.debug then printStackTrace()
69-
em"""cannot resolve reference to type $pre.$name
70-
|the classfile defining the type might be missing from the classpath${otherReason(pre)}"""
82+
em"""Cannot resolve reference to type $pre.$name.
83+
|$reason."""
7184
end MissingType
7285

7386
class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int)(using Context)

Diff for: compiler/src/dotty/tools/dotc/reporting/messages.scala

+11-1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre
301301
// these are usually easier to analyze. We exclude F-bounds since these would
302302
// lead to a recursive infinite expansion.
303303
object reported extends TypeMap, IdentityCaptRefMap:
304+
var notes: String = ""
304305
def setVariance(v: Int) = variance = v
305306
val constraint = mapCtx.typerState.constraint
306307
var fbounded = false
@@ -318,6 +319,15 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre
318319
case tp: LazyRef =>
319320
fbounded = true
320321
tp
322+
case tp @ TypeRef(pre, _) =>
323+
if pre != NoPrefix && !pre.member(tp.name).exists then
324+
notes ++=
325+
i"""
326+
|
327+
|Note that I could not resolve reference $tp.
328+
|${MissingType(pre, tp.name).reason}
329+
"""
330+
mapOver(tp)
321331
case _ =>
322332
mapOver(tp)
323333

@@ -329,7 +339,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre
329339
else (found1, expected1)
330340
val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)
331341
i"""|Found: $foundStr
332-
|Required: $expectedStr"""
342+
|Required: $expectedStr${reported.notes}"""
333343
end msg
334344

335345
override def msgPostscript(using Context) =

Diff for: compiler/test/dotc/neg-best-effort-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ i17121.scala
1414
illegal-match-types.scala
1515
i13780-1.scala
1616
i20317a.scala
17+
i11226.scala
1718

1819
# semantic db generation fails in the first compilation
1920
i1642.scala

Diff for: tests/neg/i11226.check

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Error: tests/neg/i11226.scala:13:36 ---------------------------------------------------------------------------------
2+
13 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error
3+
| ^
4+
| Cannot resolve reference to type (Unsubscriber.this.bus : ManagedActorClassification).Subscriber.
5+
| Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification
6+
| but it cannot be called on a receiver whose type does not extend trait ManagedActorClassification.

Diff for: tests/neg/i11226.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait ActorRef
2+
3+
trait ActorEventBus {
4+
type Subscriber = ActorRef
5+
}
6+
7+
trait ManagedActorClassification { this: ActorEventBus =>
8+
def unsubscribe(subscriber: Subscriber, from: Any): Unit
9+
def unsubscribe(subscriber: Subscriber): Unit
10+
}
11+
12+
class Unsubscriber(bus: ManagedActorClassification) {
13+
def test(a: ActorRef): Unit = bus.unsubscribe(a) // error
14+
}

Diff for: tests/neg/i11226a.check

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i11226a.scala:12:48 -----------------------------------------------------------
2+
12 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error
3+
| ^
4+
| Found: (a : ActorRef)
5+
| Required: Unsubscriber.this.bus.Subscriber
6+
|
7+
| Note that I could not resolve reference Unsubscriber.this.bus.Subscriber.
8+
| Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification
9+
| but it cannot be called on a receiver whose type does not extend trait ManagedActorClassification
10+
|
11+
|
12+
| longer explanation available when compiling with `-explain`

Diff for: tests/neg/i11226a.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait ActorRef
2+
3+
trait ActorEventBus {
4+
type Subscriber = ActorRef
5+
}
6+
7+
trait ManagedActorClassification { this: ActorEventBus =>
8+
def unsubscribe(subscriber: Subscriber): Unit
9+
}
10+
11+
class Unsubscriber(bus: ManagedActorClassification) {
12+
def test(a: ActorRef): Unit = bus.unsubscribe(a) // error
13+
}

Diff for: tests/neg/i16407.check

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
-- Error: tests/neg/i16407.scala:2:2 -----------------------------------------------------------------------------------
22
2 | f(g()) // error // error
33
| ^
4-
| cannot resolve reference to type (X.this : Y & X).A
5-
| the classfile defining the type might be missing from the classpath
6-
| or the self type of (X.this : Y & X) might not contain all transitive dependencies
4+
| Cannot resolve reference to type (X.this : Y & X).A.
5+
| The classfile defining the type might be missing from the classpath
6+
| or the self type of (X.this : Y & X) might not contain all transitive dependencies.
77
-- Error: tests/neg/i16407.scala:2:4 -----------------------------------------------------------------------------------
88
2 | f(g()) // error // error
99
| ^
10-
| cannot resolve reference to type (X.this : Y & X).A
11-
| the classfile defining the type might be missing from the classpath
12-
| or the self type of (X.this : Y & X) might not contain all transitive dependencies
10+
| Cannot resolve reference to type (X.this : Y & X).A.
11+
| The classfile defining the type might be missing from the classpath
12+
| or the self type of (X.this : Y & X) might not contain all transitive dependencies.

Diff for: tests/pos/i11226b.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait A {
2+
class T()
3+
}
4+
trait B {
5+
this: A =>
6+
def f(a: Int = 0): Any
7+
}
8+
trait C extends B {
9+
this: A =>
10+
def f(t: T): Any
11+
}

0 commit comments

Comments
 (0)