Skip to content

Commit

Permalink
Merge pull request scala#8824 from dotty-staging/fix-#8802
Browse files Browse the repository at this point in the history
Fix scala#8802: Use wildcards for result type approximation of implicits
  • Loading branch information
odersky authored Apr 28, 2020
2 parents 96e45a7 + 7f8a4db commit d436677
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 8 deletions.
17 changes: 9 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -534,22 +534,23 @@ object ProtoTypes {
newTypeVar(TypeBounds.upper(AndType(tp.widenExpr, defn.SingletonClass.typeRef)))

/** The result type of `mt`, where all references to parameters of `mt` are
* replaced by either wildcards (if typevarsMissContext) or TypeParamRefs.
* replaced by either wildcards or TypeParamRefs.
*/
def resultTypeApprox(mt: MethodType)(using Context): Type =
if (mt.isResultDependent) {
def resultTypeApprox(mt: MethodType, wildcardOnly: Boolean = false)(using Context): Type =
if mt.isResultDependent then
def replacement(tp: Type) =
if (ctx.mode.is(Mode.TypevarsMissContext) ||
!tp.widenExpr.isValueTypeOrWildcard) WildcardType
if wildcardOnly
|| ctx.mode.is(Mode.TypevarsMissContext)
|| !tp.widenExpr.isValueTypeOrWildcard
then WildcardType
else newDepTypeVar(tp)
mt.resultType.substParams(mt, mt.paramInfos.map(replacement))
}
else mt.resultType

/** The normalized form of a type
* - unwraps polymorphic types, tracking their parameters in the current constraint
* - skips implicit parameters of methods and functions;
* if result type depends on implicit parameter, replace with fresh type dependent parameter.
* if result type depends on implicit parameter, replace with wildcard.
* - converts non-dependent method types to the corresponding function types
* unless the expected type is an ApplyingProto or IgnoredProto.
* - dereferences parameterless method types
Expand All @@ -568,7 +569,7 @@ object ProtoTypes {
case poly: PolyType =>
normalize(constrained(poly).resultType, pt)
case mt: MethodType =>
if (mt.isImplicitMethod) normalize(resultTypeApprox(mt), pt)
if (mt.isImplicitMethod) normalize(resultTypeApprox(mt, wildcardOnly = true), pt)
else if (mt.isResultDependent) tp
else {
val rt = normalize(mt.resultType, pt)
Expand Down
17 changes: 17 additions & 0 deletions tests/pos/i8802.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
trait Foo[A, B] {
type Out
}

object Test {

type Bar[A]

def unit: Bar[Unit] = ???
def product[A, B](fst: Bar[A], snd: Bar[B])(implicit foo: Foo[A, B]): Bar[foo.Out] = ???

implicit def foo[A]: Foo[A, Unit] { type Out = A } = ???

def check[A](bar: Bar[A])(a: A): Unit = {}

check(product(unit, unit))(()) // error
}
24 changes: 24 additions & 0 deletions tests/pos/i8825.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
sealed trait Nat
case class Succ[N <: Nat](n: N) extends Nat
case object Zero extends Nat
type Zero = Zero.type
type One = Succ[Zero]

sealed trait HList
case class HCons[+H, +T <: HList](head: H, tail: T) extends HList
case object HNil extends HList
type HNil = HNil.type

trait Length[L <: HList] {
type Out <: Nat
}
object Length {
type Aux[L <: HList, Out0 <: Nat] = Length[L] { type Out = Out0 }
def instance[L <: HList, Out0 <: Nat]: Aux[L, Out0] = new Length[L] { type Out = Out0 }

given hnilLength as Aux[HNil, Zero] = instance
given hconsLength[H, T <: HList] (using length: Length[T]) as Aux[HCons[H, T], Succ[length.Out]] = instance // (*)
//given hconsLength[H, T <: HList, N <: Nat] (using length: Aux[T, N]) as Aux[HCons[H, T], Succ[N]] = instance // (**)
}

val test = summon[Length.Aux[HCons[Int, HNil], One]]

0 comments on commit d436677

Please sign in to comment.