Skip to content

Commit 00277e9

Browse files
committed
Take actual arguments for dependent TypeVars into account
Fixes #12534
1 parent d26549d commit 00277e9

File tree

6 files changed

+77
-12
lines changed

6 files changed

+77
-12
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import Contexts._, Types._, Symbols._, Names._, Flags._
66
import SymDenotations._
77
import util.Spans._
88
import util.Stats
9-
import NameKinds.DepParamName
109
import Decorators._
1110
import StdNames._
1211
import collection.mutable

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import config.Printers.{ transforms => debug }
2828
object TypeTestsCasts {
2929
import ast.tpd._
3030
import typer.Inferencing.maximizeType
31-
import typer.ProtoTypes.{ constrained, newTypeVar }
31+
import typer.ProtoTypes.constrained
3232

3333
/** Whether `(x:X).isInstanceOf[P]` can be checked at runtime?
3434
*

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
529529
case mt: MethodType => mt
530530
case pt: PolyType =>
531531
inContext(ctx.fresh.setExploreTyperState()) {
532-
val tvars = pt.paramInfos.map(newTypeVar)
532+
val tvars = pt.paramInfos.map(newTypeVar(_))
533533
val mt = pt.instantiate(tvars).asInstanceOf[MethodType]
534534
scrutineeTp <:< mt.paramInfos(0)
535535
// force type inference to infer a narrower type: could be singleton

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,8 @@ trait Inferencing { this: Typer =>
599599
val toInstantiate = new InstantiateQueue
600600
for (tvar <- qualifying)
601601
if (!tvar.isInstantiated && constraint.contains(tvar)) {
602+
constrainIfDependentParamRef(tvar, tree)
603+
602604
// Needs to be checked again, since previous interpolations could already have
603605
// instantiated `tvar` through unification.
604606
val v = vs(tvar)
@@ -663,6 +665,33 @@ trait Inferencing { this: Typer =>
663665
}
664666
tree
665667
}
668+
669+
/** If `tvar` represents a parameter of a dependent method type in the current `call`
670+
* approximate it from below with the type of the actual argument. Skolemize that
671+
* type if necessary to make it a Singleton.
672+
*/
673+
private def constrainIfDependentParamRef(tvar: TypeVar, call: Tree)(using Context): Unit =
674+
representedParamRef(tvar) match
675+
case ref: TermParamRef =>
676+
677+
def findArg(tree: Tree)(using Context): Tree = tree match
678+
case Apply(fn, args) =>
679+
if fn.tpe.widen eq ref.binder then
680+
if ref.paramNum < args.length then args(ref.paramNum)
681+
else EmptyTree
682+
else findArg(fn)
683+
case TypeApply(fn, _) => findArg(fn)
684+
case Block(_, expr) => findArg(expr)
685+
case Inlined(_, _, expr) => findArg(expr)
686+
case _ => EmptyTree
687+
688+
val arg = findArg(call)
689+
if !arg.isEmpty then
690+
var argType = arg.tpe
691+
if !argType.isSingleton then argType = SkolemType(argType)
692+
argType <:< tvar
693+
case _ =>
694+
end constrainIfDependentParamRef
666695
}
667696

668697
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */

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

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -642,30 +642,49 @@ object ProtoTypes {
642642
def constrained(tl: TypeLambda)(using Context): TypeLambda =
643643
constrained(tl, EmptyTree)._1
644644

645-
def newTypeVar(bounds: TypeBounds)(using Context): TypeVar = {
645+
/** A new type variable with given bounds for its origin.
646+
* @param represents If exists, the TermParamRef that the TypeVar represents
647+
* in the substitution generated by `resultTypeApprox`
648+
* If `represents` exists, it is stored in the result type of the PolyType
649+
* that backs the TypeVar, to be retrieved by `representedParamRef`.
650+
*/
651+
def newTypeVar(bounds: TypeBounds, represents: Type = NoType)(using Context): TypeVar = {
646652
val poly = PolyType(DepParamName.fresh().toTypeName :: Nil)(
647653
pt => bounds :: Nil,
648-
pt => defn.AnyType)
654+
pt => represents.orElse(defn.AnyType))
649655
constrained(poly, untpd.EmptyTree, alwaysAddTypeVars = true)
650656
._2.head.tpe.asInstanceOf[TypeVar]
651657
}
652658

653-
/** Create a new TypeVar that represents a dependent method parameter singleton */
654-
def newDepTypeVar(tp: Type)(using Context): TypeVar =
655-
newTypeVar(TypeBounds.upper(AndType(tp.widenExpr, defn.SingletonClass.typeRef)))
659+
/** If `tvar` represents a parameter of a dependent function generated
660+
* by `newDepVar` called from `resultTypeApprox, the term parameter reference
661+
* for which the variable was substituted. Otherwise, NoType.
662+
*/
663+
def representedParamRef(tvar: TypeVar)(using Context): Type =
664+
if tvar.origin.paramName.is(DepParamName) then
665+
tvar.origin.binder.resultType match
666+
case ref: TermParamRef => ref
667+
case _ => NoType
668+
else NoType
669+
670+
/** Create a new TypeVar that represents a dependent method parameter singleton `ref` */
671+
def newDepTypeVar(ref: TermParamRef)(using Context): TypeVar =
672+
newTypeVar(
673+
TypeBounds.upper(AndType(ref.underlying.widenExpr, defn.SingletonClass.typeRef)),
674+
ref)
656675

657676
/** The result type of `mt`, where all references to parameters of `mt` are
658677
* replaced by either wildcards or TypeParamRefs.
659678
*/
660679
def resultTypeApprox(mt: MethodType, wildcardOnly: Boolean = false)(using Context): Type =
661680
if mt.isResultDependent then
662-
def replacement(tp: Type) =
681+
def replacement(ref: TermParamRef) =
663682
if wildcardOnly
664683
|| ctx.mode.is(Mode.TypevarsMissContext)
665-
|| !tp.widenExpr.isValueTypeOrWildcard
684+
|| !ref.underlying.widenExpr.isValueTypeOrWildcard
666685
then WildcardType
667-
else newDepTypeVar(tp)
668-
mt.resultType.substParams(mt, mt.paramInfos.map(replacement))
686+
else newDepTypeVar(ref)
687+
mt.resultType.substParams(mt, mt.paramRefs.map(replacement))
669688
else mt.resultType
670689

671690
/** The normalized form of a type

tests/pos/i8802a.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait Foo[A1, B1] {
2+
type Out
3+
}
4+
5+
object Test {
6+
7+
type Bar[A2]
8+
9+
def unit: Bar[Unit] = ???
10+
def product[A3, B3](fst: Bar[A3], snd: Bar[B3])(implicit foo: Foo[A3, B3]): Bar[foo.Out] = ???
11+
12+
implicit def foo[A4]: Foo[A4, Unit] { type Out = A4 } = ???
13+
14+
def check[A5](bar: Bar[A5])(a: A5): Unit = {}
15+
16+
check(product(unit, unit)) // ok
17+
check(product(unit, unit)(summon[Foo[Unit, Unit]]))(()) // error
18+
}

0 commit comments

Comments
 (0)