Skip to content

Commit 7c67d7c

Browse files
authored
Fix implicitNotFound message for type aliases (#19343)
Fix #7092
1 parent 96c956a commit 7c67d7c

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
@@ -2821,19 +2821,27 @@ class MissingImplicitArgument(
28212821
val idx = paramNames.indexOf(name)
28222822
if (idx >= 0) Some(i"${args(idx)}") else None
28232823
"""\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match
2824-
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn
2824+
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("?" + v)).nn
28252825
)
28262826

28272827
/** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
28282828
* @param sym Symbol of the annotated type or of the method whose parameter was annotated
2829+
* @param paramNames Names of type parameters to substitute with `args` in the message template
2830+
* @param args Resolved type arguments to substitute for `paramNames` in the message template
28292831
* @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
28302832
*/
2831-
def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type)(using Context): String =
2833+
def formatAnnotationMessage(
2834+
rawMsg: String,
2835+
sym: Symbol,
2836+
paramNames: List[Name],
2837+
args: List[Type],
2838+
substituteType: Type => Type,
2839+
)(using Context): String =
28322840
val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
28332841
userDefinedErrorString(
28342842
rawMsg,
2835-
paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2836-
args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2843+
paramNames = (paramNames ::: substitutableTypesSymbols.map(_.name)).map(_.unexpandedName.toString),
2844+
args = args ::: substitutableTypesSymbols.map(_.typeRef).map(substituteType)
28372845
)
28382846

28392847
/** Extract a user defined error message from a symbol `sym`
@@ -2845,14 +2853,17 @@ class MissingImplicitArgument(
28452853
msg <- ann.argumentConstantString(0)
28462854
yield msg
28472855

2848-
def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol)(using Context): Option[String] =
2849-
for
2850-
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2851-
if Feature.migrateTo3 || sym != defn.Function1
2852-
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2853-
yield
2854-
val substituteType = (_: Type).asSeenFrom(pt, sym)
2855-
formatAnnotationMessage(rawMsg, sym, substituteType)
2856+
def userDefinedImplicitNotFoundTypeMessageFor(
2857+
sym: Symbol,
2858+
params: List[ParamInfo] = Nil,
2859+
args: List[Type] = Nil
2860+
)(using Context): Option[String] = for
2861+
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2862+
if Feature.migrateTo3 || sym != defn.Function1
2863+
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2864+
yield
2865+
val paramNames = params.map(_.paramName)
2866+
formatAnnotationMessage(rawMsg, sym, paramNames, args, _.asSeenFrom(pt, sym))
28562867

28572868
/** Extracting the message from a method parameter, e.g. in
28582869
*
@@ -2867,19 +2878,22 @@ class MissingImplicitArgument(
28672878
val targs = tpd.typeArgss(applTree).flatten
28682879
val methodOwner = fn.symbol.owner
28692880
val methodOwnerType = tpd.qualifier(fn).tpe
2870-
val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2881+
val methodTypeParams = fn.symbol.paramSymss.flatten.withFilter(_.isType).map(_.name)
28712882
val methodTypeArgs = targs.map(_.tpe)
2872-
val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2873-
formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2883+
formatAnnotationMessage(rawMsg, sym.owner, methodTypeParams, methodTypeArgs, _.asSeenFrom(methodOwnerType, methodOwner))
28742884

28752885
def userDefinedImplicitNotFoundTypeMessage(using Context): Option[String] =
2876-
def recur(tp: Type): Option[String] = tp match
2886+
def recur(tp: Type, params: List[ParamInfo] = Nil, args: List[Type] = Nil): Option[String] = tp match
2887+
case tp: AppliedType =>
2888+
val tycon = tp.typeConstructor
2889+
val typeParams = if tycon.isLambdaSub then tycon.hkTypeParams else tycon.typeParams
2890+
recur(tycon, typeParams ::: params, tp.args ::: args)
28772891
case tp: TypeRef =>
2878-
val sym = tp.symbol
2879-
userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2892+
userDefinedImplicitNotFoundTypeMessageFor(tp.symbol, params, args)
2893+
.orElse(recur(tp.info))
28802894
case tp: ClassInfo =>
28812895
tp.baseClasses.iterator
2882-
.map(userDefinedImplicitNotFoundTypeMessageFor)
2896+
.map(userDefinedImplicitNotFoundTypeMessageFor(_))
28832897
.find(_.isDefined).flatten
28842898
case tp: TypeProxy =>
28852899
recur(tp.superType)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
274274
*/
275275
final def diffTest(testSource: TestSource, checkFile: JFile, actual: List[String], reporters: Seq[TestReporter], logger: LoggedRunnable) = {
276276
for (msg <- FileDiff.check(testSource.title, actual, checkFile.getPath)) {
277-
onFailure(testSource, reporters, logger, Some(msg))
278-
279277
if (updateCheckFiles) {
280278
FileDiff.dump(checkFile.toPath.toString, actual)
281279
echo("Updated checkfile: " + checkFile.getPath)
282280
} else {
281+
onFailure(testSource, reporters, logger, Some(msg))
283282
val outFile = checkFile.toPath.resolveSibling(s"${checkFile.toPath.getFileName}.out").toString
284283
FileDiff.dump(outFile, actual)
285284
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)