From 7d8a77e0a4442ddf01084e21775fa946728643c0 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 27 May 2024 13:09:29 +0200 Subject: [PATCH 1/4] 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 21261c07bb4bdf8aeb493a7fc1893e75c1085344] --- .../dotty/tools/dotc/core/TypeErrors.scala | 29 ++++++++++++++----- .../dotty/tools/dotc/reporting/messages.scala | 12 +++++++- tests/neg/i11226.check | 6 ++++ tests/neg/i11226.scala | 14 +++++++++ tests/neg/i11226a.check | 12 ++++++++ tests/neg/i11226a.scala | 13 +++++++++ tests/neg/i16407.check | 12 ++++---- tests/pos/i11226b.scala | 11 +++++++ 8 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 tests/neg/i11226.check create mode 100644 tests/neg/i11226.scala create mode 100644 tests/neg/i11226a.check create mode 100644 tests/neg/i11226a.scala create mode 100644 tests/pos/i11226b.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index ae017a936818..a6247454b59d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -57,17 +57,30 @@ end TypeError class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: 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(", ")}" -class MissingType(pre: Type, name: Name)(using Context) extends TypeError: - private def otherReason(pre: Type)(using Context): String = pre match { - case pre: ThisType if pre.cls.givenSelfType.exists => - i"\nor the self type of $pre might not contain all transitive dependencies" - case _ => "" - } +class MissingType(val pre: Type, val name: Name)(using Context) extends TypeError: + + def reason(using Context): String = + def missingClassFile = + "The classfile defining the type might be missing from the classpath" + val cls = pre.classSymbol + val givenSelf = cls match + case cls: ClassSymbol => cls.givenSelfType + case _ => NoType + pre match + case pre: ThisType if pre.cls.givenSelfType.exists => + i"""$missingClassFile + |or the self type of $pre might not contain all transitive dependencies""" + case _ if givenSelf.exists && givenSelf.member(name).exists => + i"""$name exists as a member of the self type $givenSelf of $cls + |but it cannot be referenced from a scope that does not extend that ${ctx.printer.kindString(cls)}""" + case _ => + missingClassFile + override def toMessage(using Context): Message = if ctx.debug then printStackTrace() - em"""cannot resolve reference to type $pre.$name - |the classfile defining the type might be missing from the classpath${otherReason(pre)}""" + em"""Cannot resolve reference to type $pre.$name. + |$reason.""" end MissingType class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int)(using Context) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 5874712f83e6..fdac3ac897fc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -298,6 +298,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre // these are usually easier to analyze. We exclude F-bounds since these would // lead to a recursive infinite expansion. object reported extends TypeMap, IdentityCaptRefMap: + var notes: String = "" def setVariance(v: Int) = variance = v val constraint = mapCtx.typerState.constraint var fbounded = false @@ -315,6 +316,15 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre case tp: LazyRef => fbounded = true tp + case tp @ TypeRef(pre, _) => + if pre != NoPrefix && !pre.member(tp.name).exists then + notes ++= + i""" + | + |Note that I could not resolve reference $tp. + |${MissingType(pre, tp.name).reason} + """ + mapOver(tp) case _ => mapOver(tp) @@ -326,7 +336,7 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre else (found1, expected1) val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2) i"""|Found: $foundStr - |Required: $expectedStr""" + |Required: $expectedStr${reported.notes}""" end msg override def msgPostscript(using Context) = diff --git a/tests/neg/i11226.check b/tests/neg/i11226.check new file mode 100644 index 000000000000..90ed2d6a8ebe --- /dev/null +++ b/tests/neg/i11226.check @@ -0,0 +1,6 @@ +-- Error: tests/neg/i11226.scala:13:36 --------------------------------------------------------------------------------- +13 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error + | ^ + | Cannot resolve reference to type (Unsubscriber.this.bus : ManagedActorClassification).Subscriber. + | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification + | but it cannot be referenced from a scope that does not extend that trait. diff --git a/tests/neg/i11226.scala b/tests/neg/i11226.scala new file mode 100644 index 000000000000..34c6eb78fd2d --- /dev/null +++ b/tests/neg/i11226.scala @@ -0,0 +1,14 @@ +trait ActorRef + +trait ActorEventBus { + type Subscriber = ActorRef +} + +trait ManagedActorClassification { this: ActorEventBus => + def unsubscribe(subscriber: Subscriber, from: Any): Unit + def unsubscribe(subscriber: Subscriber): Unit +} + +class Unsubscriber(bus: ManagedActorClassification) { + def test(a: ActorRef): Unit = bus.unsubscribe(a) // error +} \ No newline at end of file diff --git a/tests/neg/i11226a.check b/tests/neg/i11226a.check new file mode 100644 index 000000000000..871973264677 --- /dev/null +++ b/tests/neg/i11226a.check @@ -0,0 +1,12 @@ +-- [E007] Type Mismatch Error: tests/neg/i11226a.scala:12:48 ----------------------------------------------------------- +12 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error + | ^ + | Found: (a : ActorRef) + | Required: Unsubscriber.this.bus.Subscriber + | + | Note that I could not resolve reference Unsubscriber.this.bus.Subscriber. + | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification + | but it cannot be referenced from a scope that does not extend that trait + | + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i11226a.scala b/tests/neg/i11226a.scala new file mode 100644 index 000000000000..f30530c5a58e --- /dev/null +++ b/tests/neg/i11226a.scala @@ -0,0 +1,13 @@ +trait ActorRef + +trait ActorEventBus { + type Subscriber = ActorRef +} + +trait ManagedActorClassification { this: ActorEventBus => + def unsubscribe(subscriber: Subscriber): Unit +} + +class Unsubscriber(bus: ManagedActorClassification) { + def test(a: ActorRef): Unit = bus.unsubscribe(a) // error +} \ No newline at end of file diff --git a/tests/neg/i16407.check b/tests/neg/i16407.check index 5c6bd19ca8c1..481d70e83ce3 100644 --- a/tests/neg/i16407.check +++ b/tests/neg/i16407.check @@ -1,12 +1,12 @@ -- Error: tests/neg/i16407.scala:2:2 ----------------------------------------------------------------------------------- 2 | f(g()) // error // error | ^ - | cannot resolve reference to type (X.this : Y & X).A - | the classfile defining the type might be missing from the classpath - | or the self type of (X.this : Y & X) might not contain all transitive dependencies + | Cannot resolve reference to type (X.this : Y & X).A. + | The classfile defining the type might be missing from the classpath + | or the self type of (X.this : Y & X) might not contain all transitive dependencies. -- Error: tests/neg/i16407.scala:2:4 ----------------------------------------------------------------------------------- 2 | f(g()) // error // error | ^ - | cannot resolve reference to type (X.this : Y & X).A - | the classfile defining the type might be missing from the classpath - | or the self type of (X.this : Y & X) might not contain all transitive dependencies + | Cannot resolve reference to type (X.this : Y & X).A. + | The classfile defining the type might be missing from the classpath + | or the self type of (X.this : Y & X) might not contain all transitive dependencies. diff --git a/tests/pos/i11226b.scala b/tests/pos/i11226b.scala new file mode 100644 index 000000000000..4074cbfc4b2e --- /dev/null +++ b/tests/pos/i11226b.scala @@ -0,0 +1,11 @@ +trait A { + class T() +} +trait B { + this: A => + def f(a: Int = 0): Any +} +trait C extends B { + this: A => + def f(t: T): Any +} \ No newline at end of file From 7c095c583b09a7ab8172ce8971ed2b04d0a4ba31 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 8 Jul 2024 20:11:34 +0200 Subject: [PATCH 2/4] Add test to bestEffortCompilation blacklist [Cherry-picked 0b9ee335fa488458966fcbe8e2ce1e7601f49546][modified] From c0e36c6d657f81541186e24ff6052837c4e5f42a Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 May 2024 16:25:31 +0200 Subject: [PATCH 3/4] Update compiler/src/dotty/tools/dotc/core/TypeErrors.scala Co-authored-by: Guillaume Martres [Cherry-picked 66bba46f143efa3bb44202c956292ba82ce4b4a1] --- compiler/src/dotty/tools/dotc/core/TypeErrors.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index a6247454b59d..80dd412d0dbe 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -72,7 +72,7 @@ class MissingType(val pre: Type, val name: Name)(using Context) extends TypeErro |or the self type of $pre might not contain all transitive dependencies""" case _ if givenSelf.exists && givenSelf.member(name).exists => i"""$name exists as a member of the self type $givenSelf of $cls - |but it cannot be referenced from a scope that does not extend that ${ctx.printer.kindString(cls)}""" + |but it cannot be called on a receiver whose type does not extend $cls""" case _ => missingClassFile From b838ea1cda7d6163939d63b8b0e83e284ed2d6bf Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 May 2024 18:29:49 +0200 Subject: [PATCH 4/4] Update check files [Cherry-picked e1ce6b99781ee6daeb363ee4b14b91dc5ba1ea69] --- tests/neg/i11226.check | 6 +++--- tests/neg/i11226a.check | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/neg/i11226.check b/tests/neg/i11226.check index 90ed2d6a8ebe..571f54326808 100644 --- a/tests/neg/i11226.check +++ b/tests/neg/i11226.check @@ -1,6 +1,6 @@ -- Error: tests/neg/i11226.scala:13:36 --------------------------------------------------------------------------------- 13 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error | ^ - | Cannot resolve reference to type (Unsubscriber.this.bus : ManagedActorClassification).Subscriber. - | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification - | but it cannot be referenced from a scope that does not extend that trait. + | Cannot resolve reference to type (Unsubscriber.this.bus : ManagedActorClassification).Subscriber. + | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification + | but it cannot be called on a receiver whose type does not extend trait ManagedActorClassification. diff --git a/tests/neg/i11226a.check b/tests/neg/i11226a.check index 871973264677..ecb0760dd01c 100644 --- a/tests/neg/i11226a.check +++ b/tests/neg/i11226a.check @@ -1,12 +1,12 @@ -- [E007] Type Mismatch Error: tests/neg/i11226a.scala:12:48 ----------------------------------------------------------- 12 | def test(a: ActorRef): Unit = bus.unsubscribe(a) // error | ^ - | Found: (a : ActorRef) - | Required: Unsubscriber.this.bus.Subscriber + | Found: (a : ActorRef) + | Required: Unsubscriber.this.bus.Subscriber | - | Note that I could not resolve reference Unsubscriber.this.bus.Subscriber. - | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification - | but it cannot be referenced from a scope that does not extend that trait - | + | Note that I could not resolve reference Unsubscriber.this.bus.Subscriber. + | Subscriber exists as a member of the self type ActorEventBus of trait ManagedActorClassification + | but it cannot be called on a receiver whose type does not extend trait ManagedActorClassification + | | | longer explanation available when compiling with `-explain`