diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ab7187f6f4d4..b2640080db5a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1344,7 +1344,31 @@ trait Applications extends Compatibility { unapplyArgType } val dummyArg = dummyTreeOfType(ownType) - val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) + var unapplyApp: tpd.Tree = null + val unapplyAppTpe = { + val explicitCall = Apply(unapplyFn, dummyArg :: Nil) + tryEither { + unapplyApp = typedExpr(untpd.TypedSplice(explicitCall)) + unapplyApp.tpe + } { (_, _) => + // Typing explicitCall may fail if there are implicits that can only + // be resolved with tighter bounds on the output types + // (e.g. def unapply[T](x: String)(using Foo[T]): Option[T]) + // If the call above fails to type, we try again here but with wildcards + // filled in for any implicits. Once we have recursively typed unapplyPatterns below, + // type variables may have tighter bounds and we can try again. + unapplyApp = null + var currType = mt.resultType + var callWithImplicits = explicitCall + while (currType.isImplicitMethod) { + val currMtType = currType.asInstanceOf[MethodType] + val wildcards = List.fill(currMtType.paramInfos.length)(dummyTreeOfType(WildcardType)) + callWithImplicits = Apply(callWithImplicits, wildcards) + currType = currMtType.resultType + } + typedExpr(untpd.TypedSplice(callWithImplicits)).tpe + } + } def unapplyImplicits(unapp: Tree): List[Tree] = { val res = List.newBuilder[Tree] def loop(unapp: Tree): Unit = unapp match { @@ -1359,8 +1383,8 @@ trait Applications extends Compatibility { res.result() } - var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.srcPos) - for (argType <- argTypes) assert(!isBounds(argType), unapplyApp.tpe.show) + var argTypes = unapplyArgs(unapplyAppTpe, unapplyFn, args, tree.srcPos) + for (argType <- argTypes) assert(!isBounds(argType), unapplyAppTpe.show) val bunchedArgs = argTypes match { case argType :: Nil => if (args.lengthCompare(1) > 0 && Feature.autoTuplingEnabled && defn.isTupleNType(argType)) untpd.Tuple(args) :: Nil @@ -1373,6 +1397,7 @@ trait Applications extends Compatibility { List.fill(argTypes.length - args.length)(WildcardType) } val unapplyPatterns = bunchedArgs.lazyZip(argTypes) map (typed(_, _)) + if (unapplyApp == null) unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits(unapplyApp), unapplyPatterns), ownType) unapp.println(s"unapply patterns = $unapplyPatterns") if ((ownType eq selType) || ownType.isError) result diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index d0565857db91..498ca52dbb88 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -699,7 +699,7 @@ trait Inferencing { this: Typer => end constrainIfDependentParamRef } -/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ +/** An enumeration controlling the degree of forcing in "is-fully-defined" checks. */ @sharable object ForceDegree { class Value(val appliesTo: TypeVar => Boolean, val ifBottom: IfBottom) val none: Value = new Value(_ => false, IfBottom.ok) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 167128be08e4..4430528161b1 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -294,7 +294,7 @@ object ProtoTypes { * [](args): resultType * * @param args The untyped arguments to which the function is applied - * @param resType The expeected result type + * @param resType The expected result type * @param typer The typer to use for typing the arguments * @param applyKind The kind of application (regular/using/tupled infix operand) * @param state The state object to use for tracking the changes to this prototype diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 433075f73fd9..c1612c7bf096 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -834,6 +834,9 @@ class Typer extends Namer val expr1 = if isWildcard then tree.expr.withType(underlyingTreeTpe.tpe) else typed(tree.expr, underlyingTreeTpe.tpe.widenSkolem) + if (ctx.mode.is(Mode.Pattern)) { + pt <:< underlyingTreeTpe.tpe + } assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe) .withNotNullInfo(expr1.notNullInfo) } @@ -2019,7 +2022,7 @@ class Typer extends Namer } def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = { - if !isFullyDefined(pt, ForceDegree.all) then + if !(ctx.mode.is(Mode.Pattern) || isFullyDefined(pt, ForceDegree.all)) then return errorTree(tree, i"expected type of $tree is not fully defined") val body1 = typed(tree.body, pt) body1 match { diff --git a/tests/neg-macros/i6436.check b/tests/neg-macros/i6436.check index d861ad90717d..07015c2efec1 100644 --- a/tests/neg-macros/i6436.check +++ b/tests/neg-macros/i6436.check @@ -2,9 +2,3 @@ 5 | case '{ StringContext(${Varargs(parts)}*) } => // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | no implicit argument of type scala.quoted.Quotes was found --- [E006] Not Found Error: tests/neg-macros/i6436.scala:6:34 ----------------------------------------------------------- -6 | val ps: Seq[Expr[String]] = parts // error - | ^^^^^ - | Not found: parts - -longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/i6436.scala b/tests/neg-macros/i6436.scala index 68d53bb0978d..8faa3c746934 100644 --- a/tests/neg-macros/i6436.scala +++ b/tests/neg-macros/i6436.scala @@ -3,6 +3,6 @@ import scala.quoted.* def f(sc: quoted.Expr[StringContext]): Unit = { sc match { case '{ StringContext(${Varargs(parts)}*) } => // error - val ps: Seq[Expr[String]] = parts // error + val ps: Seq[Expr[String]] = parts } -} \ No newline at end of file +} diff --git a/tests/neg/f242.check b/tests/neg/f242.check new file mode 100644 index 000000000000..a69e9bc58404 --- /dev/null +++ b/tests/neg/f242.check @@ -0,0 +1,6 @@ +-- Error: tests/neg/f242.scala:23:17 ----------------------------------------------------------------------------------- +23 | case Foo.Type(NotAnInstance(_)) => ??? // error: no implicit argument of type Test.Foo[T] was found + | ^ + | no implicit argument of type Test.Foo[T] was found for an implicit parameter of method unapply in object Type + | + | where: T is a type variable with constraint <: Test.NotAnInstance diff --git a/tests/neg/f242.scala b/tests/neg/f242.scala new file mode 100644 index 000000000000..b310bd6c2eb5 --- /dev/null +++ b/tests/neg/f242.scala @@ -0,0 +1,25 @@ +object Test { + trait Foo[Self] + object Foo { + class Type private(t: Any) + + object Type { + def apply[T: Foo](t: T) = new Type(t) + + def unapply[T: Foo](t: Type): Option[T] = ??? + } + } + + case class Instance1(a: String) + given Foo[Instance1]() + + case class Instance2(b: Int) + given Foo[Instance2]() + + case class NotAnInstance(b: Double) + + Foo.Type(Instance1("5")) match { + case Foo.Type(Instance1(_: String)) => ??? + case Foo.Type(NotAnInstance(_)) => ??? // error: no implicit argument of type Test.Foo[T] was found + } +} diff --git a/tests/pos/f242-2.scala b/tests/pos/f242-2.scala new file mode 100644 index 000000000000..36978f2e3285 --- /dev/null +++ b/tests/pos/f242-2.scala @@ -0,0 +1,12 @@ +object Test { + object Unapply { + def unapply[T1, T2, T3](x: Int)(using T1)(using T2, T3): Option[(T1, T2, T3)] = ??? + } + given Int = 6 + given String = "s" + given Boolean = false + + 5 match { + case Unapply(f: Int, _: String, _: Boolean) if f == 5 => ??? + } +} diff --git a/tests/pos/f242-3.scala b/tests/pos/f242-3.scala new file mode 100644 index 000000000000..95c895819472 --- /dev/null +++ b/tests/pos/f242-3.scala @@ -0,0 +1,14 @@ +object Test { + object Unapply { + def unapply[T](x: Int)(using T): Option[T] = ??? + } + trait Foo + object Impl extends Foo + + implicit val x: Foo = Impl + implicit val distractor: Int = 5 + + 5 match { + case Unapply(Impl: Foo) => ??? + } +} diff --git a/tests/pos/f242-4.scala b/tests/pos/f242-4.scala new file mode 100644 index 000000000000..0ef587aec4ba --- /dev/null +++ b/tests/pos/f242-4.scala @@ -0,0 +1,9 @@ +object Test { + object Unapply { + def unapply(x: Int): Option[Any] = ??? + } + + 5 match { + case Unapply(f: Int) if f == 5 => ??? + } +} diff --git a/tests/pos/f242.scala b/tests/pos/f242.scala new file mode 100644 index 000000000000..d1154ea6d419 --- /dev/null +++ b/tests/pos/f242.scala @@ -0,0 +1,25 @@ +object Test { + trait Foo[Self] + object Foo { + class Type private(t: Any) + + object Type { + def apply[T: Foo](t: T) = new Type(t) + + def unapply[T: Foo](t: Type): Option[T] = ??? + } + } + + case class Instance1(a: String) + given Foo[Instance1]() + + case class Instance2(b: Int) + given Foo[Instance2]() + + Foo.Type(Instance1("5")) match { + case Foo.Type(Instance1(_: String)) => ??? + case Foo.Type(_: Instance1) => ??? + case Foo.Type(Instance2(_: Int)) => ??? + case Foo.Type(_: Instance2) => ??? + } +}