Skip to content

Commit 03e0900

Browse files
committed
Report failed extension method constructions as addendums to error messages
1 parent f5b6463 commit 03e0900

File tree

5 files changed

+76
-29
lines changed

5 files changed

+76
-29
lines changed

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
524524
case result: AmbiguousImplicits =>
525525
"Ambiguous Implicit: " ~ toText(result.alt1.ref) ~ " and " ~ toText(result.alt2.ref)
526526
case _ =>
527-
"?Unknown Implicit Result?" + result.getClass
527+
"Search Failure: " ~ toText(result.tree)
528528
}
529529
}
530530

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ object messages {
300300
val explanation: String = ""
301301
}
302302

303-
case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context)
303+
case class NotAMember(site: Type, name: Name, selected: String, addendum: String = "")(implicit ctx: Context)
304304
extends Message(NotAMemberID) {
305305
val kind: String = "Member Not Found"
306306

@@ -360,7 +360,7 @@ object messages {
360360
)
361361
}
362362

363-
ex"$selected $name is not a member of ${site.widen}$closeMember"
363+
ex"$selected $name is not a member of ${site.widen}$closeMember$addendum"
364364
}
365365

366366
val explanation: String = ""

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -234,38 +234,46 @@ trait TypeAssigner {
234234
test(tpe, true)
235235
}
236236

237-
/** The type of a selection with `name` of a tree with type `site`.
238-
*/
239-
def selectionType(site: Type, name: Name, pos: SourcePosition)(implicit ctx: Context): Type = {
240-
val mbr = site.member(name)
237+
/** The type of the selection `tree`, where `qual1` is the typed qualifier part. */
238+
def selectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = {
239+
var qualType = qual1.tpe.widenIfUnstable
240+
if (!qualType.hasSimpleKind && tree.name != nme.CONSTRUCTOR)
241+
// constructors are selected on typeconstructor, type arguments are passed afterwards
242+
qualType = errorType(em"$qualType takes type parameters", qual1.sourcePos)
243+
else if (!qualType.isInstanceOf[TermType])
244+
qualType = errorType(em"$qualType is illegal as a selection prefix", qual1.sourcePos)
245+
val name = tree.name
246+
val mbr = qualType.member(name)
241247
if (reallyExists(mbr))
242-
site.select(name, mbr)
243-
else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name))
248+
qualType.select(name, mbr)
249+
else if (qualType.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name))
244250
TryDynamicCallType
245-
else if (site.isErroneous || name.toTermName == nme.ERROR)
251+
else if (qualType.isErroneous || name.toTermName == nme.ERROR)
246252
UnspecifiedErrorType
253+
else if (name == nme.CONSTRUCTOR)
254+
errorType(ex"$qualType does not have a constructor", tree.sourcePos)
247255
else {
248-
def kind = if (name.isTypeName) "type" else "value"
249-
def addendum =
250-
if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?"
251-
else ""
252-
errorType(
253-
if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor"
254-
else NotAMember(site, name, kind),
255-
pos)
256+
val kind = if (name.isTypeName) "type" else "value"
257+
val addendum =
258+
if (qualType.derivesFrom(defn.DynamicClass))
259+
"\npossible cause: maybe a wrong Dynamic method signature?"
260+
else qual1.getAttachment(Typer.HiddenSearchFailure) match {
261+
case Some(failure) if !failure.reason.isInstanceOf[Implicits.NoMatchingImplicits] =>
262+
i""".
263+
|An extension method was tried, but could not be fully constructed:
264+
|
265+
| ${failure.tree.show.replace("\n", "\n ")}"""
266+
case _ => ""
267+
}
268+
errorType(NotAMember(qualType, name, kind, addendum), tree.sourcePos)
256269
}
257270
}
258271

259-
/** The selection type, which is additionally checked for accessibility.
272+
/** The type of the selection in `tree`, where `qual1` is the typed qualifier part.
273+
* The selection type is additionally checked for accessibility.
260274
*/
261275
def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = {
262-
var qualType = qual1.tpe.widenIfUnstable
263-
if (!qualType.hasSimpleKind && tree.name != nme.CONSTRUCTOR)
264-
// constructors are selected on typeconstructor, type arguments are passed afterwards
265-
qualType = errorType(em"$qualType takes type parameters", qual1.sourcePos)
266-
else if (!qualType.isInstanceOf[TermType])
267-
qualType = errorType(em"$qualType is illegal as a selection prefix", qual1.sourcePos)
268-
val ownType = selectionType(qualType, tree.name, tree.sourcePos)
276+
val ownType = selectionType(tree, qual1)
269277
if (tree.getAttachment(desugar.SuppressAccessCheck).isDefined) ownType
270278
else ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.sourcePos)
271279
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import transform.SymUtils._
4040
import transform.TypeUtils._
4141
import reporting.trace
4242

43-
4443
object Typer {
4544

4645
/** The precedence of bindings which determines which of several bindings will be
@@ -75,6 +74,11 @@ object Typer {
7574
* the `()` was dropped by the Typer.
7675
*/
7776
private val DroppedEmptyArgs = new Property.Key[Unit]
77+
78+
/** Am attachment that indicates a failed conversion or extension method
79+
* search was tried on a tree. This will in some cases be reported in error messages
80+
*/
81+
private[typer] val HiddenSearchFailure = new Property.Key[SearchFailure]
7882
}
7983

8084
class Typer extends Namer
@@ -2724,11 +2728,14 @@ class Typer extends Namer
27242728
checkImplicitConversionUseOK(inferred.symbol, tree.posd)
27252729
readapt(inferred)(ctx.retractMode(Mode.ImplicitsEnabled))
27262730
case failure: SearchFailure =>
2727-
if (pt.isInstanceOf[ProtoType] && !failure.isAmbiguous)
2731+
if (pt.isInstanceOf[ProtoType] && !failure.isAmbiguous) {
27282732
// don't report the failure but return the tree unchanged. This
27292733
// will cause a failure at the next level out, which usually gives
2730-
// a better error message.
2734+
// a better error message. To compensate, store the encountered failure
2735+
// as an attachment, so that it can be reported later as an addendum.
2736+
tree.putAttachment(HiddenSearchFailure, failure)
27312737
tree
2738+
}
27322739
else recover(failure.reason)
27332740
}
27342741
else recover(NoMatchingImplicits)

tests/neg/i5773.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
trait Semigroup[T] {
2+
def (lhs: T) append (rhs: T): T
3+
}
4+
5+
object Semigroup {
6+
implicit object stringAppend extends Semigroup[String] {
7+
override def (lhs: String) append (rhs: String): String = lhs + rhs
8+
}
9+
10+
implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new {
11+
override def (lhs: N) append (rhs: N): N = ??? // N.plus(lhs, rhs)
12+
}
13+
14+
implicit class SumSemigroupDeco[N](implicit N: Numeric[N]) extends Semigroup[N] {
15+
override def (lhs: N) append (rhs: N): N = ??? // N.plus(lhs, rhs)
16+
}
17+
}
18+
19+
20+
object Main {
21+
import Semigroup.sumSemigroup // this is not sufficient
22+
def f1 = {
23+
import Semigroup.stringAppend // necessary to make the extension method visible
24+
println("Hi" append " mum") // ok
25+
println(1 append 2) // error: this won't compile
26+
}
27+
28+
def f2 = {
29+
implicit val intSumAppend: Semigroup[Int] = sumSemigroup[Int]
30+
println(3 append 4) // this will
31+
}
32+
}

0 commit comments

Comments
 (0)