Skip to content

Commit

Permalink
Better wildcard approximations of higher-kinded applications
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Sep 8, 2022
1 parent 889aa61 commit 3326a9a
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 57 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ class ImplicitSearchError(
}
case ambi @ TooUnspecific(target) =>
ex"""No implicit search was attempted${location("for")}
|since the expected type $target is too unspecific"""
|since the expected type $target is not specific enough"""
case _ =>
val shortMessage = userDefinedImplicitNotFoundParamMessage
.orElse(userDefinedImplicitNotFoundTypeMessage)
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -822,17 +822,22 @@ object ProtoTypes {
/** Approximate occurrences of parameter types and uninstantiated typevars
* by wildcard types.
*/
private def wildApprox(tp: Type, theMap: WildApproxMap | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type = tp match {
private def wildApprox(tp: Type, theMap: WildApproxMap | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type =
tp match {
case tp: NamedType => // default case, inlined for speed
val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.isPatternBound
if (isPatternBoundTypeRef) WildcardType(tp.underlying.bounds)
else if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp
else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen, internal))
case tp @ AppliedType(tycon, args) =>
def wildArgs = args.mapConserve(arg => wildApprox(arg, theMap, seen, internal))
wildApprox(tycon, theMap, seen, internal) match {
case _: WildcardType => WildcardType // this ensures we get a * type
case tycon1 => tp.derivedAppliedType(tycon1,
args.mapConserve(arg => wildApprox(arg, theMap, seen, internal)))
case WildcardType(TypeBounds(lo, hi)) if hi.typeParams.hasSameLengthAs(args) =>
val lo1 = if lo.typeParams.hasSameLengthAs(args) then lo.appliedTo(wildArgs) else lo
WildcardType(TypeBounds(lo1, hi.appliedTo(wildArgs)))
case WildcardType(_) =>
WildcardType
case tycon1 => tp.derivedAppliedType(tycon1, wildArgs)
}
case tp: RefinedType => // default case, inlined for speed
tp.derivedRefinedType(
Expand Down
54 changes: 19 additions & 35 deletions tests/neg/i15998.check
Original file line number Diff line number Diff line change
@@ -1,36 +1,20 @@
-- [E007] Type Mismatch Error: tests/neg/i15998.scala:11:23 ------------------------------------------------------------
11 | RingSeq.isRotationOf("DAB") // error
| ^^^^^
| Found: ("DAB" : String)
| Required: CC[A]
-- [E007] Type Mismatch Error: tests/neg/i15998.scala:6:12 -------------------------------------------------------------
6 |val _ = foo(1) // error
| ^
| Found: (1 : Int)
| Required: CC[A]
|
| where: A is a type variable
| CC is a type variable with constraint <: [B] =>> Any
|
| Note that implicit conversions were not tried because the result of an implicit conversion
| must be more specific than CC[A]
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg/i15998.scala:11:11 ---------------------------------------------------------------------------------
11 |val _ = bar // error
| ^
| No implicit search was attempted for parameter x of method bar
| since the expected type X is not specific enough
|
| where: A is a type variable
| CC is a type variable with constraint <: [B] =>> collection.SeqOps[B, CC, CC[B]]
|
| Note that implicit conversions were not tried because the result of an implicit conversion
| must be more specific than CC[A]
|
| longer explanation available when compiling with `-explain`
-- [E008] Not Found Error: tests/neg/i15998.scala:12:9 -----------------------------------------------------------------
12 | "ABCD".isRotationOf("DAB") // error
| ^^^^^^^^^^^^^^^^^^^
| value isRotationOf is not a member of String.
| An extension method was tried, but could not be fully constructed:
|
| RingSeq.isRotationOf[A, CC]("ABCD") failed with
|
| Found: ("ABCD" : String)
| Required: CC[A]
|
| where: A is a type variable
| CC is a type variable with constraint <: [B] =>> collection.SeqOps[B, CC, CC[B]]
|
| Note that implicit conversions were not tried because the result of an implicit conversion
| must be more specific than CC[A]
-- Error: tests/neg/i15998.scala:21:13 ---------------------------------------------------------------------------------
21 | val x = foo // error
| ^
| No implicit search was attempted for parameter x of method foo
| since the expected type X is too unspecific
|
| where: X is a type variable
| where: X is a type variable
20 changes: 5 additions & 15 deletions tests/neg/i15998.scala
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import scala.collection.SeqOps

trait ComparingOps:
extension[A, CC[B] <: SeqOps[B, CC, CC[B]]](ring: CC[A])
def isRotationOf(that: CC[A]): Boolean = ???
given split: Conversion[Int, List[Int]] = ???

object RingSeq extends ComparingOps
import RingSeq.*
def foo[A, CC[B]](ring: CC[A]): Unit = ()

@main def Test =
RingSeq.isRotationOf("DAB") // error
"ABCD".isRotationOf("DAB") // error
val _ = foo(1) // error

// workaround
RingSeq.isRotationOf[Char, IndexedSeq]("DAB")
RingSeq.isRotationOf(wrapString("DAB"))
wrapString("ABCD").isRotationOf("DAB")

def foo[X](using x: X): X = x
def bar[X](using x: X): X = x

val x = foo // error
val _ = bar // error
15 changes: 15 additions & 0 deletions tests/run/i13986.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package example

sealed trait Xa[T]
sealed trait Mu[T] extends Xa[T]
object Xa {
implicit def convertMu[X[x] <: Xa[x], A, B](implicit t: X[A]): X[B] = t.asInstanceOf[X[B]]
}
object Mu {
implicit def mu: Mu[Int] = new Mu[Int] {}
}

object App extends App {
def constrain(a: Mu[Long]): Unit = ()
constrain(Xa.convertMu)
}
4 changes: 2 additions & 2 deletions tests/neg/i13987.scala → tests/run/i13987.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ object Mu {
implicit def mu: Mu[Int] = new Mu[Int] {}
}

object App extends App {
def constrain(a: Mu[Long]): Unit = println(a)
object Test extends App {
def constrain(a: Mu[Long]): Unit = ()
constrain(Xa.convertMu) // error
}
17 changes: 17 additions & 0 deletions tests/run/i15998.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import scala.collection.SeqOps

trait ComparingOps:
extension[A, CC[B] <: SeqOps[B, CC, CC[B]]](ring: CC[A])
def isRotationOf(that: CC[A]): Boolean = true

object RingSeq extends ComparingOps
import RingSeq.*

@main def Test =
RingSeq.isRotationOf("DAB") // error
"ABCD".isRotationOf("DAB") // error

// workaround
RingSeq.isRotationOf[Char, IndexedSeq]("DAB")
RingSeq.isRotationOf(wrapString("DAB"))
wrapString("ABCD").isRotationOf("DAB")

0 comments on commit 3326a9a

Please sign in to comment.