Skip to content

Commit 89f9091

Browse files
committed
Support implicitNotFound annotation
1 parent 320ca51 commit 89f9091

File tree

4 files changed

+30
-4
lines changed

4 files changed

+30
-4
lines changed

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ class Definitions {
449449
def ContravariantBetweenAnnot(implicit ctx: Context) = ContravariantBetweenAnnotType.symbol.asClass
450450
lazy val DeprecatedAnnotType = ctx.requiredClassRef("scala.deprecated")
451451
def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass
452+
lazy val ImplicitNotFoundAnnotType = ctx.requiredClassRef("scala.annotation.implicitNotFound")
453+
def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass
452454
lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InvariantBetween")
453455
def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass
454456
lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration")

src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ object ErrorReporting {
116116
| found : $found
117117
| required: $expected""".stripMargin + whyNoMatchStr(found, expected)
118118
}
119+
120+
/** Format `raw` implicitNotFound argument, replacing all
121+
* occurrences of `${X}` where `X` is in `paramNames` with the
122+
* corresponding shown type in `args`.
123+
*/
124+
def implicitNotFoundString(raw: String, paramNames: List[String], args: List[Type]): String = {
125+
def translate(name: String): Option[String] = {
126+
val idx = paramNames.indexOf(name)
127+
if (idx >= 0) Some(args(idx).show) else None
128+
}
129+
"""\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init))
130+
}
119131
}
120132

121133
def err(implicit ctx: Context): Errors = new Errors

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15411541
case failure: SearchFailure =>
15421542
val arg = synthesizedClassTag(formal)
15431543
if (!arg.isEmpty) arg
1544-
else implicitArgError(d"no implicit argument of type $formal found for $where" + failure.postscript)
1544+
else {
1545+
var msg = d"no implicit argument of type $formal found for $where" + failure.postscript
1546+
for (notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot);
1547+
Literal(Constant(raw: String)) <- notFound.argument(0))
1548+
msg = err.implicitNotFoundString(
1549+
raw,
1550+
formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
1551+
formal.argInfos)
1552+
implicitArgError(msg)
1553+
}
15451554
}
15461555
}
15471556
if (errors.nonEmpty) {

tests/neg/EqualityStrawman1.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
package strawman.equality
2+
import annotation.implicitNotFound
23

34
object EqualityStrawman1 {
45

56
trait Eq[-T]
6-
trait Impossible
7+
8+
@implicitNotFound("cannot compare value of type ${T} with a value outside its equality class")
9+
trait Impossible[T]
710

811
object Eq extends Eq[Any]
912

1013
trait Base {
1114
def === (other: Any): Boolean = this.equals(other)
12-
def === (other: CondEquals)(implicit ce: Impossible): Boolean = ???
15+
def === [T <: CondEquals](other: T)(implicit ce: Impossible[T]): Boolean = ???
1316
}
1417

1518
trait CondEquals extends Base {
1619
def === [T >: this.type <: CondEquals](other: T)(implicit ce: Eq[T]): Boolean = this.equals(other)
17-
def === (other: Any)(implicit ce: Impossible): Boolean = ???
20+
def === [T](other: T)(implicit ce: Impossible[T]): Boolean = ???
1821
}
1922

2023
trait Equals[-T] extends CondEquals

0 commit comments

Comments
 (0)