Skip to content

Commit 3326a9a

Browse files
committed
Better wildcard approximations of higher-kinded applications
1 parent 889aa61 commit 3326a9a

File tree

7 files changed

+68
-57
lines changed

7 files changed

+68
-57
lines changed

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ class ImplicitSearchError(
264264
}
265265
case ambi @ TooUnspecific(target) =>
266266
ex"""No implicit search was attempted${location("for")}
267-
|since the expected type $target is too unspecific"""
267+
|since the expected type $target is not specific enough"""
268268
case _ =>
269269
val shortMessage = userDefinedImplicitNotFoundParamMessage
270270
.orElse(userDefinedImplicitNotFoundTypeMessage)

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -822,17 +822,22 @@ object ProtoTypes {
822822
/** Approximate occurrences of parameter types and uninstantiated typevars
823823
* by wildcard types.
824824
*/
825-
private def wildApprox(tp: Type, theMap: WildApproxMap | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type = tp match {
825+
private def wildApprox(tp: Type, theMap: WildApproxMap | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type =
826+
tp match {
826827
case tp: NamedType => // default case, inlined for speed
827828
val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.isPatternBound
828829
if (isPatternBoundTypeRef) WildcardType(tp.underlying.bounds)
829830
else if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp
830831
else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen, internal))
831832
case tp @ AppliedType(tycon, args) =>
833+
def wildArgs = args.mapConserve(arg => wildApprox(arg, theMap, seen, internal))
832834
wildApprox(tycon, theMap, seen, internal) match {
833-
case _: WildcardType => WildcardType // this ensures we get a * type
834-
case tycon1 => tp.derivedAppliedType(tycon1,
835-
args.mapConserve(arg => wildApprox(arg, theMap, seen, internal)))
835+
case WildcardType(TypeBounds(lo, hi)) if hi.typeParams.hasSameLengthAs(args) =>
836+
val lo1 = if lo.typeParams.hasSameLengthAs(args) then lo.appliedTo(wildArgs) else lo
837+
WildcardType(TypeBounds(lo1, hi.appliedTo(wildArgs)))
838+
case WildcardType(_) =>
839+
WildcardType
840+
case tycon1 => tp.derivedAppliedType(tycon1, wildArgs)
836841
}
837842
case tp: RefinedType => // default case, inlined for speed
838843
tp.derivedRefinedType(

tests/neg/i15998.check

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,20 @@
1-
-- [E007] Type Mismatch Error: tests/neg/i15998.scala:11:23 ------------------------------------------------------------
2-
11 | RingSeq.isRotationOf("DAB") // error
3-
| ^^^^^
4-
| Found: ("DAB" : String)
5-
| Required: CC[A]
1+
-- [E007] Type Mismatch Error: tests/neg/i15998.scala:6:12 -------------------------------------------------------------
2+
6 |val _ = foo(1) // error
3+
| ^
4+
| Found: (1 : Int)
5+
| Required: CC[A]
6+
|
7+
| where: A is a type variable
8+
| CC is a type variable with constraint <: [B] =>> Any
9+
|
10+
| Note that implicit conversions were not tried because the result of an implicit conversion
11+
| must be more specific than CC[A]
12+
|
13+
| longer explanation available when compiling with `-explain`
14+
-- Error: tests/neg/i15998.scala:11:11 ---------------------------------------------------------------------------------
15+
11 |val _ = bar // error
16+
| ^
17+
| No implicit search was attempted for parameter x of method bar
18+
| since the expected type X is not specific enough
619
|
7-
| where: A is a type variable
8-
| CC is a type variable with constraint <: [B] =>> collection.SeqOps[B, CC, CC[B]]
9-
|
10-
| Note that implicit conversions were not tried because the result of an implicit conversion
11-
| must be more specific than CC[A]
12-
|
13-
| longer explanation available when compiling with `-explain`
14-
-- [E008] Not Found Error: tests/neg/i15998.scala:12:9 -----------------------------------------------------------------
15-
12 | "ABCD".isRotationOf("DAB") // error
16-
| ^^^^^^^^^^^^^^^^^^^
17-
| value isRotationOf is not a member of String.
18-
| An extension method was tried, but could not be fully constructed:
19-
|
20-
| RingSeq.isRotationOf[A, CC]("ABCD") failed with
21-
|
22-
| Found: ("ABCD" : String)
23-
| Required: CC[A]
24-
|
25-
| where: A is a type variable
26-
| CC is a type variable with constraint <: [B] =>> collection.SeqOps[B, CC, CC[B]]
27-
|
28-
| Note that implicit conversions were not tried because the result of an implicit conversion
29-
| must be more specific than CC[A]
30-
-- Error: tests/neg/i15998.scala:21:13 ---------------------------------------------------------------------------------
31-
21 | val x = foo // error
32-
| ^
33-
| No implicit search was attempted for parameter x of method foo
34-
| since the expected type X is too unspecific
35-
|
36-
| where: X is a type variable
20+
| where: X is a type variable

tests/neg/i15998.scala

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
1-
import scala.collection.SeqOps
21

3-
trait ComparingOps:
4-
extension[A, CC[B] <: SeqOps[B, CC, CC[B]]](ring: CC[A])
5-
def isRotationOf(that: CC[A]): Boolean = ???
2+
given split: Conversion[Int, List[Int]] = ???
63

7-
object RingSeq extends ComparingOps
8-
import RingSeq.*
4+
def foo[A, CC[B]](ring: CC[A]): Unit = ()
95

10-
@main def Test =
11-
RingSeq.isRotationOf("DAB") // error
12-
"ABCD".isRotationOf("DAB") // error
6+
val _ = foo(1) // error
137

14-
// workaround
15-
RingSeq.isRotationOf[Char, IndexedSeq]("DAB")
16-
RingSeq.isRotationOf(wrapString("DAB"))
17-
wrapString("ABCD").isRotationOf("DAB")
188

19-
def foo[X](using x: X): X = x
9+
def bar[X](using x: X): X = x
2010

21-
val x = foo // error
11+
val _ = bar // error

tests/run/i13986.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package example
2+
3+
sealed trait Xa[T]
4+
sealed trait Mu[T] extends Xa[T]
5+
object Xa {
6+
implicit def convertMu[X[x] <: Xa[x], A, B](implicit t: X[A]): X[B] = t.asInstanceOf[X[B]]
7+
}
8+
object Mu {
9+
implicit def mu: Mu[Int] = new Mu[Int] {}
10+
}
11+
12+
object App extends App {
13+
def constrain(a: Mu[Long]): Unit = ()
14+
constrain(Xa.convertMu)
15+
}

tests/neg/i13987.scala renamed to tests/run/i13987.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ object Mu {
1010
implicit def mu: Mu[Int] = new Mu[Int] {}
1111
}
1212

13-
object App extends App {
14-
def constrain(a: Mu[Long]): Unit = println(a)
13+
object Test extends App {
14+
def constrain(a: Mu[Long]): Unit = ()
1515
constrain(Xa.convertMu) // error
1616
}

tests/run/i15998.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import scala.collection.SeqOps
2+
3+
trait ComparingOps:
4+
extension[A, CC[B] <: SeqOps[B, CC, CC[B]]](ring: CC[A])
5+
def isRotationOf(that: CC[A]): Boolean = true
6+
7+
object RingSeq extends ComparingOps
8+
import RingSeq.*
9+
10+
@main def Test =
11+
RingSeq.isRotationOf("DAB") // error
12+
"ABCD".isRotationOf("DAB") // error
13+
14+
// workaround
15+
RingSeq.isRotationOf[Char, IndexedSeq]("DAB")
16+
RingSeq.isRotationOf(wrapString("DAB"))
17+
wrapString("ABCD").isRotationOf("DAB")

0 commit comments

Comments
 (0)