Skip to content

Commit 0cb3b51

Browse files
committed
Keep comnstraints for subtype tests with ProtoType
A subtype test `A <:< P` where `P` is a prototype was implemented as `P.isMatchedBy(A)`. `isMatchedBy` did not keep the constraint which meant that information was lost instead of being propagated.
1 parent 03e0900 commit 0cb3b51

File tree

4 files changed

+31
-19
lines changed

4 files changed

+31
-19
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
12271227
/** Defer constraining type variables when compared against prototypes */
12281228
def isMatchedByProto(proto: ProtoType, tp: Type): Boolean = tp.stripTypeVar match {
12291229
case tp: TypeParamRef if constraint contains tp => true
1230-
case _ => proto.isMatchedBy(tp)
1230+
case _ => proto.isMatchedBy(tp, keepConstraint = true)
12311231
}
12321232

12331233
/** Narrow gadt.bounds for the type parameter referenced by `tr` to include

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1665,7 +1665,7 @@ object Types {
16651665

16661666
/** A trait for proto-types, used as expected types in typer */
16671667
trait ProtoType extends Type {
1668-
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean
1668+
def isMatchedBy(tp: Type, keepConstraint: Boolean = false)(implicit ctx: Context): Boolean
16691669
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T
16701670
def map(tm: TypeMap)(implicit ctx: Context): ProtoType
16711671

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,8 +1118,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11181118
/** Is given method reference applicable to type arguments `targs` and argument trees `args`?
11191119
* @param resultType The expected result type of the application
11201120
*/
1121-
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1122-
ctx.test(implicit ctx => new ApplicableToTrees(methRef, targs, args, resultType).success)
1121+
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
1122+
def isApp(implicit ctx: Context): Boolean =
1123+
new ApplicableToTrees(methRef, targs, args, resultType).success
1124+
if (keepConstraint) isApp else ctx.test(implicit ctx => isApp)
1125+
}
11231126

11241127
/** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views?
11251128
* @param resultType The expected result type of the application
@@ -1137,8 +1140,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11371140
* possibly after inserting an `apply`?
11381141
* @param resultType The expected result type of the application
11391142
*/
1140-
def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1141-
onMethod(tp, isApplicable(_, targs, args, resultType))
1143+
def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
1144+
onMethod(tp, isApplicable(_, targs, args, resultType, keepConstraint))
11421145

11431146
/** Is given type applicable to argument types `args`, possibly after inserting an `apply`?
11441147
* @param resultType The expected result type of the application
@@ -1491,7 +1494,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14911494
)
14921495
if (alts2.isEmpty && !ctx.isAfterTyper)
14931496
alts.filter(alt =>
1494-
isApplicable(alt, targs, args, resultType)
1497+
isApplicable(alt, targs, args, resultType, keepConstraint = false)
14951498
)
14961499
else
14971500
alts2
@@ -1511,14 +1514,14 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15111514
}
15121515

15131516
case pt @ PolyProto(targs1, pt1) if targs.isEmpty =>
1514-
val alts1 = alts filter pt.isMatchedBy
1517+
val alts1 = alts.filter(pt.isMatchedBy(_, keepConstraint = false))
15151518
resolveOverloaded(alts1, pt1, targs1.tpes)
15161519

15171520
case defn.FunctionOf(args, resultType, _, _) =>
15181521
narrowByTypes(alts, args, resultType)
15191522

15201523
case pt =>
1521-
val compat = alts.filter(normalizedCompatible(_, pt))
1524+
val compat = alts.filter(normalizedCompatible(_, pt, keepConstraint = false))
15221525
if (compat.isEmpty)
15231526
/*
15241527
* the case should not be moved to the enclosing match

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,21 @@ object ProtoTypes {
3535
def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
3636
(tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt)
3737

38-
/** Test compatibility after normalization in a fresh typerstate. */
39-
def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
40-
ctx.test { implicit ctx =>
38+
/** Test compatibility after normalization.
39+
* Do this in a fresh typerstate unless `keepConstraint` is true.
40+
*/
41+
def normalizedCompatible(tp: Type, pt: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
42+
def testCompat(implicit ctx: Context): Boolean = {
4143
val normTp = normalize(tp, pt)
4244
isCompatible(normTp, pt) || pt.isRef(defn.UnitClass) && normTp.isParameterless
4345
}
46+
if (keepConstraint && false)
47+
tp.widenSingleton match {
48+
case poly: PolyType => normalizedCompatible(tp, pt, keepConstraint = false)
49+
case _ => testCompat
50+
}
51+
else ctx.test(implicit ctx => testCompat)
52+
}
4453

4554
private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match {
4655
case _: OrType => true
@@ -89,7 +98,7 @@ object ProtoTypes {
8998

9099
/** A trait for prototypes that match all types */
91100
trait MatchAlways extends ProtoType {
92-
def isMatchedBy(tp1: Type)(implicit ctx: Context): Boolean = true
101+
def isMatchedBy(tp1: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = true
93102
def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this
94103
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x
95104
override def toString: String = getClass.toString
@@ -131,13 +140,13 @@ object ProtoTypes {
131140
case _ => false
132141
}
133142

134-
override def isMatchedBy(tp1: Type)(implicit ctx: Context): Boolean = {
143+
override def isMatchedBy(tp1: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
135144
name == nme.WILDCARD || hasUnknownMembers(tp1) ||
136145
{
137146
val mbr = if (privateOK) tp1.member(name) else tp1.nonPrivateMember(name)
138147
def qualifies(m: SingleDenotation) =
139148
memberProto.isRef(defn.UnitClass) ||
140-
tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto)
149+
tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto, keepConstraint)
141150
// Note: can't use `m.info` here because if `m` is a method, `m.info`
142151
// loses knowledge about `m`'s default arguments.
143152
mbr match { // hasAltWith inlined for performance
@@ -234,8 +243,8 @@ object ProtoTypes {
234243
extends UncachedGroundType with ApplyingProto with FunOrPolyProto {
235244
override def resultType(implicit ctx: Context): Type = resType
236245

237-
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean =
238-
typer.isApplicable(tp, Nil, unforcedTypedArgs, resultType)
246+
def isMatchedBy(tp: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
247+
typer.isApplicable(tp, Nil, unforcedTypedArgs, resultType, keepConstraint)
239248

240249
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer): FunProto =
241250
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
@@ -379,7 +388,7 @@ object ProtoTypes {
379388

380389
override def resultType(implicit ctx: Context): Type = resType
381390

382-
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean =
391+
def isMatchedBy(tp: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean =
383392
ctx.typer.isApplicable(tp, argType :: Nil, resultType) || {
384393
resType match {
385394
case SelectionProto(name: TermName, mbrType, _, _) =>
@@ -422,7 +431,7 @@ object ProtoTypes {
422431

423432
override def resultType(implicit ctx: Context): Type = resType
424433

425-
override def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = {
434+
override def isMatchedBy(tp: Type, keepConstraint: Boolean)(implicit ctx: Context): Boolean = {
426435
def isInstantiatable(tp: Type) = tp.widen match {
427436
case tp: PolyType => tp.paramNames.length == targs.length
428437
case _ => false

0 commit comments

Comments
 (0)