Skip to content

Commit 9bc6103

Browse files
Backport "Fix implicitNotFound message for type aliases" (#20275)
Backports #19343 to 3.3 Fix #7092
2 parents d260b21 + 9953335 commit 9bc6103

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

28022802
/** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
28032803
* @param sym Symbol of the annotated type or of the method whose parameter was annotated
2804+
* @param paramNames Names of type parameters to substitute with `args` in the message template
2805+
* @param args Resolved type arguments to substitute for `paramNames` in the message template
28042806
* @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
28052807
*/
2806-
def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type)(using Context): String =
2808+
def formatAnnotationMessage(
2809+
rawMsg: String,
2810+
sym: Symbol,
2811+
paramNames: List[Name],
2812+
args: List[Type],
2813+
substituteType: Type => Type,
2814+
)(using Context): String =
28072815
val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
28082816
userDefinedErrorString(
28092817
rawMsg,
2810-
paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
2811-
args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
2818+
paramNames = (paramNames ::: substitutableTypesSymbols.map(_.name)).map(_.unexpandedName.toString),
2819+
args = args ::: substitutableTypesSymbols.map(_.typeRef).map(substituteType)
28122820
)
28132821

28142822
/** Extract a user defined error message from a symbol `sym`
@@ -2820,14 +2828,17 @@ class MissingImplicitArgument(
28202828
msg <- ann.argumentConstantString(0)
28212829
yield msg
28222830

2823-
def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol)(using Context): Option[String] =
2824-
for
2825-
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2826-
if Feature.migrateTo3 || sym != defn.Function1
2827-
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2828-
yield
2829-
val substituteType = (_: Type).asSeenFrom(pt, sym)
2830-
formatAnnotationMessage(rawMsg, sym, substituteType)
2831+
def userDefinedImplicitNotFoundTypeMessageFor(
2832+
sym: Symbol,
2833+
params: List[ParamInfo] = Nil,
2834+
args: List[Type] = Nil
2835+
)(using Context): Option[String] = for
2836+
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
2837+
if Feature.migrateTo3 || sym != defn.Function1
2838+
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
2839+
yield
2840+
val paramNames = params.map(_.paramName)
2841+
formatAnnotationMessage(rawMsg, sym, paramNames, args, _.asSeenFrom(pt, sym))
28312842

28322843
/** Extracting the message from a method parameter, e.g. in
28332844
*
@@ -2842,19 +2853,22 @@ class MissingImplicitArgument(
28422853
val targs = tpd.typeArgss(applTree).flatten
28432854
val methodOwner = fn.symbol.owner
28442855
val methodOwnerType = tpd.qualifier(fn).tpe
2845-
val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
2856+
val methodTypeParams = fn.symbol.paramSymss.flatten.withFilter(_.isType).map(_.name)
28462857
val methodTypeArgs = targs.map(_.tpe)
2847-
val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
2848-
formatAnnotationMessage(rawMsg, sym.owner, substituteType)
2858+
formatAnnotationMessage(rawMsg, sym.owner, methodTypeParams, methodTypeArgs, _.asSeenFrom(methodOwnerType, methodOwner))
28492859

28502860
def userDefinedImplicitNotFoundTypeMessage(using Context): Option[String] =
2851-
def recur(tp: Type): Option[String] = tp match
2861+
def recur(tp: Type, params: List[ParamInfo] = Nil, args: List[Type] = Nil): Option[String] = tp match
2862+
case tp: AppliedType =>
2863+
val tycon = tp.typeConstructor
2864+
val typeParams = if tycon.isLambdaSub then tycon.hkTypeParams else tycon.typeParams
2865+
recur(tycon, typeParams ::: params, tp.args ::: args)
28522866
case tp: TypeRef =>
2853-
val sym = tp.symbol
2854-
userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
2867+
userDefinedImplicitNotFoundTypeMessageFor(tp.symbol, params, args)
2868+
.orElse(recur(tp.info))
28552869
case tp: ClassInfo =>
28562870
tp.baseClasses.iterator
2857-
.map(userDefinedImplicitNotFoundTypeMessageFor)
2871+
.map(userDefinedImplicitNotFoundTypeMessageFor(_))
28582872
.find(_.isDefined).flatten
28592873
case tp: TypeProxy =>
28602874
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)