Skip to content

Commit 585cb87

Browse files
committed
Better resolution of implicits in patterns
1 parent 034b0a0 commit 585cb87

File tree

12 files changed

+127
-14
lines changed

12 files changed

+127
-14
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,31 @@ trait Applications extends Compatibility {
13441344
unapplyArgType
13451345
}
13461346
val dummyArg = dummyTreeOfType(ownType)
1347-
val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil)))
1347+
var unapplyApp: tpd.Tree = null
1348+
val unapplyAppTpe = {
1349+
val explicitCall = Apply(unapplyFn, dummyArg :: Nil)
1350+
tryEither {
1351+
unapplyApp = typedExpr(untpd.TypedSplice(explicitCall))
1352+
unapplyApp.tpe
1353+
} { (_, _) =>
1354+
// Typing explicitCall may fail if there are implicits that can only
1355+
// be resolved with tighter bounds on the output types
1356+
// (e.g. def unapply[T](x: String)(using Foo[T]): Option[T])
1357+
// If the call above fails to type, we try again here but with wildcards
1358+
// filled in for any implicits. Once we have recursively typed unapplyPatterns below,
1359+
// type variables may have tighter bounds and we can try again.
1360+
unapplyApp = null
1361+
var currType = mt.resultType
1362+
var callWithImplicits = explicitCall
1363+
while (currType.isImplicitMethod) {
1364+
val currMtType = currType.asInstanceOf[MethodType]
1365+
val wildcards = List.fill(currMtType.paramInfos.length)(dummyTreeOfType(WildcardType))
1366+
callWithImplicits = Apply(callWithImplicits, wildcards)
1367+
currType = currMtType.resultType
1368+
}
1369+
typedExpr(untpd.TypedSplice(callWithImplicits)).tpe
1370+
}
1371+
}
13481372
def unapplyImplicits(unapp: Tree): List[Tree] = {
13491373
val res = List.newBuilder[Tree]
13501374
def loop(unapp: Tree): Unit = unapp match {
@@ -1359,8 +1383,8 @@ trait Applications extends Compatibility {
13591383
res.result()
13601384
}
13611385

1362-
var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.srcPos)
1363-
for (argType <- argTypes) assert(!isBounds(argType), unapplyApp.tpe.show)
1386+
var argTypes = unapplyArgs(unapplyAppTpe, unapplyFn, args, tree.srcPos)
1387+
for (argType <- argTypes) assert(!isBounds(argType), unapplyAppTpe.show)
13641388
val bunchedArgs = argTypes match {
13651389
case argType :: Nil =>
13661390
if (args.lengthCompare(1) > 0 && Feature.autoTuplingEnabled && defn.isTupleNType(argType)) untpd.Tuple(args) :: Nil
@@ -1373,6 +1397,7 @@ trait Applications extends Compatibility {
13731397
List.fill(argTypes.length - args.length)(WildcardType)
13741398
}
13751399
val unapplyPatterns = bunchedArgs.lazyZip(argTypes) map (typed(_, _))
1400+
if (unapplyApp == null) unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil)))
13761401
val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits(unapplyApp), unapplyPatterns), ownType)
13771402
unapp.println(s"unapply patterns = $unapplyPatterns")
13781403
if ((ownType eq selType) || ownType.isError) result

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ trait Inferencing { this: Typer =>
699699
end constrainIfDependentParamRef
700700
}
701701

702-
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
702+
/** An enumeration controlling the degree of forcing in "is-fully-defined" checks. */
703703
@sharable object ForceDegree {
704704
class Value(val appliesTo: TypeVar => Boolean, val ifBottom: IfBottom)
705705
val none: Value = new Value(_ => false, IfBottom.ok)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ object ProtoTypes {
294294
* [](args): resultType
295295
*
296296
* @param args The untyped arguments to which the function is applied
297-
* @param resType The expeected result type
297+
* @param resType The expected result type
298298
* @param typer The typer to use for typing the arguments
299299
* @param applyKind The kind of application (regular/using/tupled infix operand)
300300
* @param state The state object to use for tracking the changes to this prototype

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,9 @@ class Typer extends Namer
834834
val expr1 =
835835
if isWildcard then tree.expr.withType(underlyingTreeTpe.tpe)
836836
else typed(tree.expr, underlyingTreeTpe.tpe.widenSkolem)
837+
if (ctx.mode.is(Mode.Pattern)) {
838+
pt <:< underlyingTreeTpe.tpe
839+
}
837840
assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
838841
.withNotNullInfo(expr1.notNullInfo)
839842
}
@@ -2019,7 +2022,7 @@ class Typer extends Namer
20192022
}
20202023

20212024
def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = {
2022-
if !isFullyDefined(pt, ForceDegree.all) then
2025+
if !(ctx.mode.is(Mode.Pattern) || isFullyDefined(pt, ForceDegree.all)) then
20232026
return errorTree(tree, i"expected type of $tree is not fully defined")
20242027
val body1 = typed(tree.body, pt)
20252028
body1 match {

tests/neg-macros/i6436.check

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,3 @@
22
5 | case '{ StringContext(${Varargs(parts)}*) } => // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
| no implicit argument of type scala.quoted.Quotes was found
5-
-- [E006] Not Found Error: tests/neg-macros/i6436.scala:6:34 -----------------------------------------------------------
6-
6 | val ps: Seq[Expr[String]] = parts // error
7-
| ^^^^^
8-
| Not found: parts
9-
10-
longer explanation available when compiling with `-explain`

tests/neg-macros/i6436.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import scala.quoted.*
33
def f(sc: quoted.Expr[StringContext]): Unit = {
44
sc match {
55
case '{ StringContext(${Varargs(parts)}*) } => // error
6-
val ps: Seq[Expr[String]] = parts // error
6+
val ps: Seq[Expr[String]] = parts
77
}
8-
}
8+
}

tests/neg/f242.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Error: tests/neg/f242.scala:23:17 -----------------------------------------------------------------------------------
2+
23 | case Foo.Type(NotAnInstance(_)) => ??? // error: no implicit argument of type Test.Foo[T] was found
3+
| ^
4+
| no implicit argument of type Test.Foo[T] was found for an implicit parameter of method unapply in object Type
5+
|
6+
| where: T is a type variable with constraint <: Test.NotAnInstance

tests/neg/f242.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object Test {
2+
trait Foo[Self]
3+
object Foo {
4+
class Type private(t: Any)
5+
6+
object Type {
7+
def apply[T: Foo](t: T) = new Type(t)
8+
9+
def unapply[T: Foo](t: Type): Option[T] = ???
10+
}
11+
}
12+
13+
case class Instance1(a: String)
14+
given Foo[Instance1]()
15+
16+
case class Instance2(b: Int)
17+
given Foo[Instance2]()
18+
19+
case class NotAnInstance(b: Double)
20+
21+
Foo.Type(Instance1("5")) match {
22+
case Foo.Type(Instance1(_: String)) => ???
23+
case Foo.Type(NotAnInstance(_)) => ??? // error: no implicit argument of type Test.Foo[T] was found
24+
}
25+
}

tests/pos/f242-2.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Test {
2+
object Unapply {
3+
def unapply[T1, T2, T3](x: Int)(using T1)(using T2, T3): Option[(T1, T2, T3)] = ???
4+
}
5+
given Int = 6
6+
given String = "s"
7+
given Boolean = false
8+
9+
5 match {
10+
case Unapply(f: Int, _: String, _: Boolean) if f == 5 => ???
11+
}
12+
}

tests/pos/f242-3.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test {
2+
object Unapply {
3+
def unapply[T](x: Int)(using T): Option[T] = ???
4+
}
5+
trait Foo
6+
object Impl extends Foo
7+
8+
implicit val x: Foo = Impl
9+
implicit val distractor: Int = 5
10+
11+
5 match {
12+
case Unapply(Impl: Foo) => ???
13+
}
14+
}

tests/pos/f242-test.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test {
2+
object Unapply {
3+
def unapply(x: Int): Option[Any] = ???
4+
}
5+
6+
5 match {
7+
case Unapply(f: Int) if f == 5 => ???
8+
}
9+
}

tests/pos/f242.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object Test {
2+
trait Foo[Self]
3+
object Foo {
4+
class Type private(t: Any)
5+
6+
object Type {
7+
def apply[T: Foo](t: T) = new Type(t)
8+
9+
def unapply[T: Foo](t: Type): Option[T] = ???
10+
}
11+
}
12+
13+
case class Instance1(a: String)
14+
given Foo[Instance1]()
15+
16+
case class Instance2(b: Int)
17+
given Foo[Instance2]()
18+
19+
Foo.Type(Instance1("5")) match {
20+
case Foo.Type(Instance1(_: String)) => ???
21+
case Foo.Type(_: Instance1) => ???
22+
case Foo.Type(Instance2(_: Int)) => ???
23+
case Foo.Type(_: Instance2) => ???
24+
}
25+
}

0 commit comments

Comments
 (0)