Skip to content

Commit 7d8a77e

Browse files
oderskyWojciechMazur
authored andcommitted
Explain unresolvable references better
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 restructing 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 [Cherry-picked 21261c0]
1 parent a88da44 commit 7d8a77e

File tree

8 files changed

+94
-15
lines changed

8 files changed

+94
-15
lines changed

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

Lines changed: 21 additions & 8 deletions
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 referenced from a scope that does not extend that ${ctx.printer.kindString(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)

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre
298298
// these are usually easier to analyze. We exclude F-bounds since these would
299299
// lead to a recursive infinite expansion.
300300
object reported extends TypeMap, IdentityCaptRefMap:
301+
var notes: String = ""
301302
def setVariance(v: Int) = variance = v
302303
val constraint = mapCtx.typerState.constraint
303304
var fbounded = false
@@ -315,6 +316,15 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre
315316
case tp: LazyRef =>
316317
fbounded = true
317318
tp
319+
case tp @ TypeRef(pre, _) =>
320+
if pre != NoPrefix && !pre.member(tp.name).exists then
321+
notes ++=
322+
i"""
323+
|
324+
|Note that I could not resolve reference $tp.
325+
|${MissingType(pre, tp.name).reason}
326+
"""
327+
mapOver(tp)
318328
case _ =>
319329
mapOver(tp)
320330

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

332342
override def msgPostscript(using Context) =

tests/neg/i11226.check

Lines changed: 6 additions & 0 deletions
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 referenced from a scope that does not extend that trait.

tests/neg/i11226.scala

Lines changed: 14 additions & 0 deletions
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+
}

tests/neg/i11226a.check

Lines changed: 12 additions & 0 deletions
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 referenced from a scope that does not extend that trait
10+
|
11+
|
12+
| longer explanation available when compiling with `-explain`

tests/neg/i11226a.scala

Lines changed: 13 additions & 0 deletions
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+
}

tests/neg/i16407.check

Lines changed: 6 additions & 6 deletions
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.

tests/pos/i11226b.scala

Lines changed: 11 additions & 0 deletions
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)