Skip to content

Commit

Permalink
Fix implicitNotFound message for type aliases
Browse files Browse the repository at this point in the history
Fix #7092
  • Loading branch information
joroKr21 committed Dec 29, 2023
1 parent 938d405 commit c550fdc
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 22 deletions.
50 changes: 31 additions & 19 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2784,19 +2784,25 @@ class MissingImplicitArgument(
val idx = paramNames.indexOf(name)
if (idx >= 0) Some(i"${args(idx)}") else None
"""\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse(v)).nn
)

/** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
* @param sym Symbol of the annotated type or of the method whose parameter was annotated
* @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
*/
def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type)(using Context): String =
def formatAnnotationMessage(
rawMsg: String,
sym: Symbol,
paramNames: List[Name],
args: List[Type],
substituteType: Type => Type,
)(using Context): String =
val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym)
userDefinedErrorString(
rawMsg,
paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
paramNames = (paramNames ::: substitutableTypesSymbols.map(_.name)).map(_.unexpandedName.toString),
args = args ::: substitutableTypesSymbols.map(_.typeRef).map(substituteType)
)

/** Extract a user defined error message from a symbol `sym`
Expand All @@ -2808,14 +2814,17 @@ class MissingImplicitArgument(
msg <- ann.argumentConstantString(0)
yield msg

def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol)(using Context): Option[String] =
for
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
if Feature.migrateTo3 || sym != defn.Function1
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
yield
val substituteType = (_: Type).asSeenFrom(pt, sym)
formatAnnotationMessage(rawMsg, sym, substituteType)
def userDefinedImplicitNotFoundTypeMessageFor(
sym: Symbol,
params: List[ParamInfo] = Nil,
args: List[Type] = Nil
)(using Context): Option[String] = for
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
if Feature.migrateTo3 || sym != defn.Function1
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
yield
val paramNames = params.map(_.paramName)
formatAnnotationMessage(rawMsg, sym, paramNames, args, _.asSeenFrom(pt, sym))

/** Extracting the message from a method parameter, e.g. in
*
Expand All @@ -2830,19 +2839,22 @@ class MissingImplicitArgument(
val targs = tpd.typeArgss(applTree).flatten
val methodOwner = fn.symbol.owner
val methodOwnerType = tpd.qualifier(fn).tpe
val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
val methodTypeParams = fn.symbol.paramSymss.flatten.withFilter(_.isType).map(_.name)
val methodTypeArgs = targs.map(_.tpe)
val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
formatAnnotationMessage(rawMsg, sym.owner, substituteType)
formatAnnotationMessage(rawMsg, sym.owner, methodTypeParams, methodTypeArgs, _.asSeenFrom(methodOwnerType, methodOwner))

def userDefinedImplicitNotFoundTypeMessage(using Context): Option[String] =
def recur(tp: Type): Option[String] = tp match
def recur(tp: Type, params: List[ParamInfo] = Nil, args: List[Type] = Nil): Option[String] = tp match
case tp: AppliedType =>
val tycon = tp.typeConstructor
val typeParams = if tycon.isLambdaSub then tycon.hkTypeParams else tycon.typeParams
recur(tycon, typeParams ::: params, tp.args ::: args)
case tp: TypeRef =>
val sym = tp.symbol
userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info))
userDefinedImplicitNotFoundTypeMessageFor(tp.symbol, params, args)
.orElse(recur(tp.info))
case tp: ClassInfo =>
tp.baseClasses.iterator
.map(userDefinedImplicitNotFoundTypeMessageFor)
.map(userDefinedImplicitNotFoundTypeMessageFor(_))
.find(_.isDefined).flatten
case tp: TypeProxy =>
recur(tp.superType)
Expand Down
3 changes: 1 addition & 2 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,11 @@ trait ParallelTesting extends RunnerOrchestration { self =>
*/
final def diffTest(testSource: TestSource, checkFile: JFile, actual: List[String], reporters: Seq[TestReporter], logger: LoggedRunnable) = {
for (msg <- FileDiff.check(testSource.title, actual, checkFile.getPath)) {
onFailure(testSource, reporters, logger, Some(msg))

if (updateCheckFiles) {
FileDiff.dump(checkFile.toPath.toString, actual)
echo("Updated checkfile: " + checkFile.getPath)
} else {
onFailure(testSource, reporters, logger, Some(msg))
val outFile = checkFile.toPath.resolveSibling(s"${checkFile.toPath.getFileName}.out").toString
FileDiff.dump(outFile, actual)
echo(FileDiff.diffMessage(checkFile.getPath, outFile))
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i4986c.check
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@
-- [E172] Type Error: tests/neg/i4986c.scala:62:19 ---------------------------------------------------------------------
62 | i.m[Option[Long]] // error
| ^
| String; List; [A, _] =>> List[Option[?]]; Int; Option[Long];
| String; List; [A, _] =>> List[Option[?]]; Int; Option[Long]; XX
32 changes: 32 additions & 0 deletions tests/neg/i7092.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- [E172] Type Error: tests/neg/i7092.scala:24:19 ----------------------------------------------------------------------
24 | summon[F[String]] // error
| ^
| Not found for String
-- [E172] Type Error: tests/neg/i7092.scala:25:19 ----------------------------------------------------------------------
25 | summon[G[String]] // error
| ^
| Not found for String
-- [E172] Type Error: tests/neg/i7092.scala:26:16 ----------------------------------------------------------------------
26 | summon[H[Int]] // error
| ^
| Not found for Int, B
-- [E172] Type Error: tests/neg/i7092.scala:27:23 ----------------------------------------------------------------------
27 | summon[H[Int][Float]] // error
| ^
| Not found for Int, Float
-- [E172] Type Error: tests/neg/i7092.scala:28:18 ----------------------------------------------------------------------
28 | summon[AAA[Int]] // error
| ^
| Not found for Int
-- [E172] Type Error: tests/neg/i7092.scala:29:25 ----------------------------------------------------------------------
29 | summon[AAA[Int][Float]] // error
| ^
| Not found for Int
-- [E172] Type Error: tests/neg/i7092.scala:30:19 ----------------------------------------------------------------------
30 | summon[op.F[Int]] // error
| ^
| Could not find Int
-- [E172] Type Error: tests/neg/i7092.scala:31:28 ----------------------------------------------------------------------
31 | summon[String =!:= String] // error
| ^
| Cannot proof type inequality because types are equal: String =:= String
31 changes: 31 additions & 0 deletions tests/neg/i7092.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import scala.annotation.implicitNotFound
import scala.util.NotGiven

@implicitNotFound("Not found for ${A}")
type F[A]

@implicitNotFound("Not found for ${A}")
trait G[A]

@implicitNotFound("Not found for ${A}, ${B}")
type H = [A] =>> [B] =>> (A, B)

@implicitNotFound("Not found for ${A}")
type AAA = [A] =>> [A] =>> A

object op:
@implicitNotFound("Could not find ${A}")
opaque type F[A] = A

@implicitNotFound("Cannot proof type inequality because types are equal: ${A} =:= ${B}")
type =!:=[A, B] = NotGiven[A =:= B]

object Test:
summon[F[String]] // error
summon[G[String]] // error
summon[H[Int]] // error
summon[H[Int][Float]] // error
summon[AAA[Int]] // error
summon[AAA[Int][Float]] // error
summon[op.F[Int]] // error
summon[String =!:= String] // error

0 comments on commit c550fdc

Please sign in to comment.