Skip to content

Commit 9953335

Browse files
committed
Fix implicitNotFound message for type aliases (#19343)
Fix #7092
1 parent feeddb7 commit 9953335

File tree

5 files changed

+98
-22
lines changed

5 files changed

+98
-22
lines changed

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

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,19 +2737,27 @@ class MissingImplicitArgument(
27372737
val idx = paramNames.indexOf(name)
27382738
if (idx >= 0) Some(i"${args(idx)}") else None
27392739
"""\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match
2740-
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn
2740+
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("?" + v)).nn
27412741
)
27422742

27432743
/** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
27442744
* @param sym Symbol of the annotated type or of the method whose parameter was annotated
2745+
* @param paramNames Names of type parameters to substitute with `args` in the message template
2746+
* @param args Resolved type arguments to substitute for `paramNames` in the message template
27452747
* @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
27462748
*/
2747-
def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type)(using Context): String =
2749+
def formatAnnotationMessage(
2750+
rawMsg: String,
2751+
sym: Symbol,
2752+
paramNames: List[Name],
2753+
args: List[Type],
2754+
substituteType: Type => Type,
2755+
)(using Context): String =
27482756
val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
27492757
userDefinedErrorString(
27502758
rawMsg,
2751-
paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2752-
args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2759+
paramNames = (paramNames ::: substitutableTypesSymbols.map(_.name)).map(_.unexpandedName.toString),
2760+
args = args ::: substitutableTypesSymbols.map(_.typeRef).map(substituteType)
27532761
)
27542762

27552763
/** Extract a user defined error message from a symbol `sym`
@@ -2761,14 +2769,17 @@ class MissingImplicitArgument(
27612769
msg <- ann.argumentConstantString(0)
27622770
yield msg
27632771

2764-
def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol)(using Context): Option[String] =
2765-
for
2766-
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2767-
if Feature.migrateTo3 || sym != defn.Function1
2768-
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2769-
yield
2770-
val substituteType = (_: Type).asSeenFrom(pt, sym)
2771-
formatAnnotationMessage(rawMsg, sym, substituteType)
2772+
def userDefinedImplicitNotFoundTypeMessageFor(
2773+
sym: Symbol,
2774+
params: List[ParamInfo] = Nil,
2775+
args: List[Type] = Nil
2776+
)(using Context): Option[String] = for
2777+
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2778+
if Feature.migrateTo3 || sym != defn.Function1
2779+
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2780+
yield
2781+
val paramNames = params.map(_.paramName)
2782+
formatAnnotationMessage(rawMsg, sym, paramNames, args, _.asSeenFrom(pt, sym))
27722783

27732784
/** Extracting the message from a method parameter, e.g. in
27742785
*
@@ -2783,19 +2794,22 @@ class MissingImplicitArgument(
27832794
val targs = tpd.typeArgss(applTree).flatten
27842795
val methodOwner = fn.symbol.owner
27852796
val methodOwnerType = tpd.qualifier(fn).tpe
2786-
val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2797+
val methodTypeParams = fn.symbol.paramSymss.flatten.withFilter(_.isType).map(_.name)
27872798
val methodTypeArgs = targs.map(_.tpe)
2788-
val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2789-
formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2799+
formatAnnotationMessage(rawMsg, sym.owner, methodTypeParams, methodTypeArgs, _.asSeenFrom(methodOwnerType, methodOwner))
27902800

27912801
def userDefinedImplicitNotFoundTypeMessage(using Context): Option[String] =
2792-
def recur(tp: Type): Option[String] = tp match
2802+
def recur(tp: Type, params: List[ParamInfo] = Nil, args: List[Type] = Nil): Option[String] = tp match
2803+
case tp: AppliedType =>
2804+
val tycon = tp.typeConstructor
2805+
val typeParams = if tycon.isLambdaSub then tycon.hkTypeParams else tycon.typeParams
2806+
recur(tycon, typeParams ::: params, tp.args ::: args)
27932807
case tp: TypeRef =>
2794-
val sym = tp.symbol
2795-
userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2808+
userDefinedImplicitNotFoundTypeMessageFor(tp.symbol, params, args)
2809+
.orElse(recur(tp.info))
27962810
case tp: ClassInfo =>
27972811
tp.baseClasses.iterator
2798-
.map(userDefinedImplicitNotFoundTypeMessageFor)
2812+
.map(userDefinedImplicitNotFoundTypeMessageFor(_))
27992813
.find(_.isDefined).flatten
28002814
case tp: TypeProxy =>
28012815
recur(tp.superType)

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
266266
*/
267267
final def diffTest(testSource: TestSource, checkFile: JFile, actual: List[String], reporters: Seq[TestReporter], logger: LoggedRunnable) = {
268268
for (msg <- FileDiff.check(testSource.title, actual, checkFile.getPath)) {
269-
onFailure(testSource, reporters, logger, Some(msg))
270-
271269
if (updateCheckFiles) {
272270
FileDiff.dump(checkFile.toPath.toString, actual)
273271
echo("Updated checkfile: " + checkFile.getPath)
274272
} else {
273+
onFailure(testSource, reporters, logger, Some(msg))
275274
val outFile = checkFile.toPath.resolveSibling(s"${checkFile.toPath.getFileName}.out").toString
276275
FileDiff.dump(outFile, actual)
277276
echo(FileDiff.diffMessage(checkFile.getPath, outFile))

tests/neg/i4986c.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@
6161
-- [E172] Type Error: tests/neg/i4986c.scala:62:19 ---------------------------------------------------------------------
6262
62 | i.m[Option[Long]] // error
6363
| ^
64-
| String; List; [A, _] =>> List[Option[?]]; Int; Option[Long];
64+
| String; List; [A, _] =>> List[Option[?]]; Int; Option[Long]; ?XX

tests/neg/i7092.check

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- [E172] Type Error: tests/neg/i7092.scala:24:19 ----------------------------------------------------------------------
2+
24 | summon[F[String]] // error
3+
| ^
4+
| Not found for String
5+
-- [E172] Type Error: tests/neg/i7092.scala:25:19 ----------------------------------------------------------------------
6+
25 | summon[G[String]] // error
7+
| ^
8+
| Not found for String
9+
-- [E172] Type Error: tests/neg/i7092.scala:26:16 ----------------------------------------------------------------------
10+
26 | summon[H[Int]] // error
11+
| ^
12+
| Not found for Int, ?B
13+
-- [E172] Type Error: tests/neg/i7092.scala:27:23 ----------------------------------------------------------------------
14+
27 | summon[H[Int][Float]] // error
15+
| ^
16+
| Not found for Int, Float
17+
-- [E172] Type Error: tests/neg/i7092.scala:28:18 ----------------------------------------------------------------------
18+
28 | summon[AAA[Int]] // error
19+
| ^
20+
| Not found for Int
21+
-- [E172] Type Error: tests/neg/i7092.scala:29:25 ----------------------------------------------------------------------
22+
29 | summon[AAA[Int][Float]] // error
23+
| ^
24+
| Not found for Int
25+
-- [E172] Type Error: tests/neg/i7092.scala:30:19 ----------------------------------------------------------------------
26+
30 | summon[op.F[Int]] // error
27+
| ^
28+
| Could not find Int
29+
-- [E172] Type Error: tests/neg/i7092.scala:31:28 ----------------------------------------------------------------------
30+
31 | summon[String =!:= String] // error
31+
| ^
32+
| Cannot proof type inequality because types are equal: String =:= String

tests/neg/i7092.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import scala.annotation.implicitNotFound
2+
import scala.util.NotGiven
3+
4+
@implicitNotFound("Not found for ${A}")
5+
type F[A]
6+
7+
@implicitNotFound("Not found for ${A}")
8+
trait G[A]
9+
10+
@implicitNotFound("Not found for ${A}, ${B}")
11+
type H = [A] =>> [B] =>> (A, B)
12+
13+
@implicitNotFound("Not found for ${A}")
14+
type AAA = [A] =>> [A] =>> A
15+
16+
object op:
17+
@implicitNotFound("Could not find ${A}")
18+
opaque type F[A] = A
19+
20+
@implicitNotFound("Cannot proof type inequality because types are equal: ${A} =:= ${B}")
21+
type =!:=[A, B] = NotGiven[A =:= B]
22+
23+
object Test:
24+
summon[F[String]] // error
25+
summon[G[String]] // error
26+
summon[H[Int]] // error
27+
summon[H[Int][Float]] // error
28+
summon[AAA[Int]] // error
29+
summon[AAA[Int][Float]] // error
30+
summon[op.F[Int]] // error
31+
summon[String =!:= String] // error

0 commit comments

Comments
 (0)