Skip to content

Commit 561ef9c

Browse files
committed
Fix #3448: Improve type inference for IFTs
We now treat a type as an implicit function type also if it is a type variable that has an IFT as upper bound in the current constraint. This means we now create implicit closures if the expected type is such a type.
1 parent 0bae8a3 commit 561ef9c

File tree

5 files changed

+51
-13
lines changed

5 files changed

+51
-13
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -942,11 +942,26 @@ class Definitions {
942942
false
943943
})
944944

945-
946945
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
947946

948-
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) =
949-
isFunctionType(tp) && tp.dealias.typeSymbol.name.isImplicitFunction
947+
/** Return underlying immplicit function type (i.e. instance of an ImplicitFunctionN class)
948+
* or NoType if none exists. The following types are considered as underlying types:
949+
* - the alias of an alias type
950+
* - the instance or origin of a TypeVar (i.e. the result of a stripTypeVar)
951+
* - the upper bound of a TypeParamRef in the current constraint
952+
*/
953+
def asImplicitFunctionType(tp: Type)(implicit ctx: Context): Type =
954+
tp.stripTypeVar.dealias match {
955+
case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) =>
956+
asImplicitFunctionType(ctx.typeComparer.bounds(tp1).hiBound)
957+
case tp1 =>
958+
if (isFunctionType(tp1) && tp1.typeSymbol.name.isImplicitFunction) tp1
959+
else NoType
960+
}
961+
962+
/** Is `tp` an implicit function type? */
963+
def isImplicitFunctionType(tp: Type)(implicit ctx: Context): Boolean =
964+
asImplicitFunctionType(tp).exists
950965

951966
// ----- primitive value class machinery ------------------------------------------
952967

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ object ProtoTypes {
467467
case et: ExprType =>
468468
normalize(et.resultType, pt)
469469
case wtp =>
470-
if (defn.isImplicitFunctionType(wtp)) normalize(wtp.dealias.argInfos.last, pt)
471-
else tp
470+
val iftp = defn.asImplicitFunctionType(wtp)
471+
if (iftp.exists) normalize(iftp.argInfos.last, pt) else tp
472472
}
473473
}
474474

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,12 +1668,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
16681668
case _ => typedUnadapted(desugar(tree), pt)
16691669
}
16701670

1671-
if (defn.isImplicitFunctionType(pt) &&
1671+
val ifpt = defn.asImplicitFunctionType(pt)
1672+
if (ifpt.exists &&
16721673
xtree.isTerm &&
16731674
!untpd.isImplicitClosure(xtree) &&
16741675
!ctx.mode.is(Mode.ImplicitShadowing) &&
16751676
!ctx.isAfterTyper)
1676-
makeImplicitFunction(xtree, pt)
1677+
makeImplicitFunction(xtree, ifpt)
16771678
else xtree match {
16781679
case xtree: untpd.NameTree => typedNamed(xtree, pt)
16791680
case xtree => typedUnnamed(xtree)

tests/run/config.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,6 @@ object Test extends App {
6565
import Configs._
6666
import Exceptions._
6767

68-
type PC[T] = Possibly[Configured[T]]
69-
70-
val names: PC[List[Name]] = readName :: Nil
71-
val firstNames: PC[List[String]] = names.map(_.first)
72-
val longest: PC[String] = firstNames.maxBy(_.length)
73-
7468
def readName: Configured[Possibly[Name]] = {
7569
val parts = config.name.split(" ")
7670
require(parts.length >= 2)
@@ -121,3 +115,18 @@ object OptionTest extends App {
121115
println(readPerson(config1))
122116
println(readPerson(config2))
123117
}
118+
119+
object FancyStuff {
120+
import Configs._
121+
import Exceptions._
122+
import Test._
123+
124+
type PC[T] = Possibly[Configured[T]]
125+
126+
val names: PC[List[Name]] = readName :: Nil
127+
val firstNames: PC[List[String]] = names.map(_.first)
128+
val longest: PC[String] = firstNames.maxBy(_.length)
129+
130+
val xs: List[PC[String]] = List(longest)
131+
val ys: PC[List[String]] = xs.map(x => x)
132+
}

tests/run/i3448.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test extends App {
2+
3+
case class C(x: Int)
4+
type IF[T] = implicit C => T
5+
6+
val x: IF[Int] = implicitly[C].x
7+
8+
val xs0: List[IF[Int]] = List(implicit _ => x)
9+
val xs: List[IF[Int]] = List(x)
10+
val ys: IF[List[Int]] = xs.map(x => x)
11+
val zs = ys(C(22))
12+
assert(zs == List(22))
13+
}

0 commit comments

Comments
 (0)