Skip to content

Commit 4dc4002

Browse files
authored
Merge pull request #5981 from dotty-staging/fix-#5976
Fix #5976: Don't assume `T <:< (=>T)`.
2 parents b26740d + 5de8dae commit 4dc4002

File tree

10 files changed

+70
-31
lines changed

10 files changed

+70
-31
lines changed

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

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,8 @@ object Denotations {
518518
val info1 = denot1.info
519519
val info2 = denot2.info
520520
val sameSym = sym1 eq sym2
521-
if (sameSym && (info1 frozen_<:< info2)) denot2
522-
else if (sameSym && (info2 frozen_<:< info1)) denot1
521+
if (sameSym && (info1.widenExpr frozen_<:< info2.widenExpr)) denot2
522+
else if (sameSym && (info2.widenExpr frozen_<:< info1.widenExpr)) denot1
523523
else {
524524
val jointSym =
525525
if (sameSym) sym1
@@ -586,9 +586,11 @@ object Denotations {
586586
(for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped)
587587
yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(idx)).toList
588588

589-
/** Normally, `tp1 & tp2`. Special cases for matching methods and classes, with
590-
* the possibility of raising a merge error.
591-
*/
589+
/** Normally, `tp1 & tp2`.
590+
* Special cases for matching methods and classes, with
591+
* the possibility of raising a merge error.
592+
* Special handling of ExprTypes, where mixed intersections widen the ExprType away.
593+
*/
592594
def infoMeet(tp1: Type, tp2: Type, sym1: Symbol, sym2: Symbol, safeIntersection: Boolean)(implicit ctx: Context): Type = {
593595
if (tp1 eq tp2) tp1
594596
else tp1 match {
@@ -645,9 +647,13 @@ object Denotations {
645647
case _ =>
646648
mergeConflict(sym1, sym2, tp1, tp2)
647649
}
648-
650+
case ExprType(rtp1) =>
651+
tp2 match {
652+
case ExprType(rtp2) => ExprType(rtp1 & rtp2)
653+
case _ => rtp1 & tp2
654+
}
649655
case _ =>
650-
try tp1 & tp2
656+
try tp1 & tp2.widenExpr
651657
catch {
652658
case ex: Throwable =>
653659
println(i"error for meet: $tp1 &&& $tp2, ${tp1.getClass}, ${tp2.getClass}")
@@ -656,9 +662,11 @@ object Denotations {
656662
}
657663
}
658664

659-
/** Normally, `tp1 | tp2`. Special cases for matching methods and classes, with
660-
* the possibility of raising a merge error.
661-
*/
665+
/** Normally, `tp1 | tp2`.
666+
* Special cases for matching methods and classes, with
667+
* the possibility of raising a merge error.
668+
* Special handling of ExprTypes, where mixed unions widen the ExprType away.
669+
*/
662670
def infoJoin(tp1: Type, tp2: Type, sym1: Symbol, sym2: Symbol)(implicit ctx: Context): Type = tp1 match {
663671
case tp1: TypeBounds =>
664672
tp2 match {
@@ -697,8 +705,13 @@ object Denotations {
697705
case _ =>
698706
mergeConflict(sym1, sym2, tp1, tp2)
699707
}
708+
case ExprType(rtp1) =>
709+
tp2 match {
710+
case ExprType(rtp2) => ExprType(rtp1 | rtp2)
711+
case _ => rtp1 | tp2
712+
}
700713
case _ =>
701-
tp1 | tp2
714+
tp1 | tp2.widenExpr
702715
}
703716

704717
/** A non-overloaded denotation */

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
621621
// ()T <:< => T, since everything one can do with a => T one can
622622
// also do with a ()T by automatic () insertion.
623623
case tp1 @ MethodType(Nil) => isSubType(tp1.resultType, restpe2)
624-
case _ => isSubType(tp1.widenExpr, restpe2)
624+
case tp1 @ ExprType(restpe1) => isSubType(restpe1, restpe2)
625+
case _ => fourthTry
625626
}
626627
compareExpr
627628
case tp2 @ TypeBounds(lo2, hi2) =>
@@ -1237,7 +1238,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
12371238
}
12381239

12391240
def qualifies(m: SingleDenotation) =
1240-
isSubType(m.info, rinfo2) || matchAbstractTypeMember(m.info)
1241+
isSubType(m.info.widenExpr, rinfo2.widenExpr) || matchAbstractTypeMember(m.info)
12411242

12421243
tp1.member(name) match { // inlined hasAltWith for performance
12431244
case mbr: SingleDenotation => qualifies(mbr)
@@ -1756,7 +1757,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
17561757
case ExprType(rt2) =>
17571758
ExprType(rt1 & rt2)
17581759
case _ =>
1759-
rt1 & tp2
1760+
NoType
17601761
}
17611762
case tp1: TypeVar if tp1.isInstantiated =>
17621763
tp1.underlying & tp2
@@ -1776,7 +1777,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
17761777
*/
17771778
private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match {
17781779
case ExprType(rt1) =>
1779-
ExprType(rt1 | tp2.widenExpr)
1780+
tp2 match {
1781+
case ExprType(rt2) =>
1782+
ExprType(rt1 | rt2)
1783+
case _ =>
1784+
NoType
1785+
}
17801786
case tp1: TypeVar if tp1.isInstantiated =>
17811787
tp1.underlying | tp2
17821788
case tp1: AnnotatedType if !tp1.isRefining =>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ object Applications {
119119
def lengthCompareTp = MethodType(List(defn.IntType), defn.IntType)
120120
def applyTp(elemTp: Type) = MethodType(List(defn.IntType), elemTp)
121121
def dropTp(elemTp: Type) = MethodType(List(defn.IntType), defn.SeqType.appliedTo(elemTp))
122-
def toSeqTp(elemTp: Type) = ExprType(defn.SeqType.appliedTo(elemTp))
122+
def toSeqTp(elemTp: Type) = defn.SeqType.appliedTo(elemTp)
123123

124124
// the result type of `def apply(i: Int): T`
125125
val elemTp = getTp.member(nme.apply).suchThat(_.info <:< applyTp(WildcardType)).info.resultType
@@ -603,7 +603,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
603603
* argument trees.
604604
*/
605605
class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) {
606-
override def argOK(arg: TypedArg, formal: Type): Boolean = argType(arg, formal) <:< formal
606+
override def argOK(arg: TypedArg, formal: Type): Boolean = argType(arg, formal) <:< formal.widenExpr
607607
}
608608

609609
/** Subclass of Application for applicability tests with value argument types. */

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ object Implicits {
9898
else if (mt.paramInfos.lengthCompare(1) == 0 && {
9999
var formal = widenSingleton(mt.paramInfos.head)
100100
if (approx) formal = wildApprox(formal)
101-
ctx.test(implicit ctx => argType relaxed_<:< formal)
101+
ctx.test(implicit ctx => argType relaxed_<:< formal.widenExpr)
102102
})
103103
Candidate.Conversion
104104
else
@@ -1039,7 +1039,7 @@ trait Implicits { self: Typer =>
10391039
val locked = ctx.typerState.ownedVars
10401040
val adapted =
10411041
if (argument.isEmpty)
1042-
adapt(generated, pt, locked)
1042+
adapt(generated, pt.widenExpr, locked)
10431043
else {
10441044
val untpdGenerated = untpd.TypedSplice(generated)
10451045
def tryConversion(implicit ctx: Context) =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,7 @@ class Namer { typer: Typer =>
12151215

12161216
var rhsCtx = ctx.addMode(Mode.InferringReturnType)
12171217
if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody)
1218-
def rhsType = typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe
1218+
def rhsType = typedAheadExpr(mdef.rhs, (inherited orElse rhsProto).widenExpr)(rhsCtx).tpe
12191219

12201220
// Approximate a type `tp` with a type that does not contain skolem types.
12211221
val deskolemize = new ApproximatingTypeMap {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ object ProtoTypes {
3131
* 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type
3232
* 3. there is an implicit conversion from `tp` to `pt`.
3333
* 4. `tp` is a numeric subtype of `pt` (this case applies even if implicit conversions are disabled)
34+
* If `pt` is a by-name type, we compare against the underlying type instead.
3435
*/
3536
def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
3637
(tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ trait TypeAssigner {
5454
required = EmptyFlagConjunction, excluded = Private)
5555
.suchThat(decl.matches(_))
5656
val inheritedInfo = inherited.info
57-
if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) {
57+
if (inheritedInfo.exists &&
58+
decl.info.widenExpr <:< inheritedInfo.widenExpr &&
59+
!(inheritedInfo.widenExpr <:< decl.info.widenExpr)) {
5860
val r = RefinedType(parent, decl.name, decl.info)
5961
typr.println(i"add ref $parent $decl --> " + r)
6062
r

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ class Typer extends Namer
734734
fullyDefinedType(tree.tpe, "block", tree.span)
735735
var avoidingType = avoid(tree.tpe, localSyms)
736736
val ptDefined = isFullyDefined(pt, ForceDegree.none)
737-
if (ptDefined && !(avoidingType <:< pt)) avoidingType = pt
737+
if (ptDefined && !(avoidingType.widenExpr <:< pt)) avoidingType = pt
738738
val tree1 = ascribeType(tree, avoidingType)
739739
assert(ptDefined || noLeaks(tree1) || tree1.tpe.isErroneous,
740740
// `ptDefined` needed because of special case of anonymous classes
@@ -954,7 +954,7 @@ class Typer extends Namer
954954
(defn.isProductSubType(formal) || formal.derivesFrom(defn.PairClass)) &&
955955
productSelectorTypes(formal, tree.sourcePos).corresponds(params) {
956956
(argType, param) =>
957-
param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe
957+
param.tpt.isEmpty || argType.widenExpr <:< typedAheadType(param.tpt).tpe
958958
}
959959
}
960960

@@ -1482,7 +1482,7 @@ class Typer extends Namer
14821482
val tpt1 = checkSimpleKinded(typedType(tpt))
14831483
val rhs1 = vdef.rhs match {
14841484
case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe
1485-
case rhs => typedExpr(rhs, tpt1.tpe)
1485+
case rhs => typedExpr(rhs, tpt1.tpe.widenExpr)
14861486
}
14871487
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
14881488
if (sym.is(Inline, butNot = DeferredOrTermParamOrAccessor))
@@ -1549,7 +1549,7 @@ class Typer extends Namer
15491549
}
15501550
}
15511551
if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody)
1552-
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx)
1552+
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
15531553

15541554
if (sym.isInlineMethod) PrepareInlineable.registerInlineInfo(sym, ddef.rhs, _ => rhs1)
15551555

@@ -2310,7 +2310,7 @@ class Typer extends Namer
23102310
}
23112311

23122312
private def adapt1(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = {
2313-
assert(pt.exists)
2313+
assert(pt.exists && !pt.isInstanceOf[ExprType])
23142314
def methodStr = err.refStr(methPart(tree).tpe)
23152315

23162316
def readapt(tree: Tree)(implicit ctx: Context) = adapt(tree, pt, locked)
@@ -2904,11 +2904,11 @@ class Typer extends Namer
29042904
methType.isImplicit && pt.isContextual // for a transition allow `with` arguments for regular implicit parameters
29052905

29062906
/** Check that `tree == x: pt` is typeable. Used when checking a pattern
2907-
* against a selector of type `pt`. This implementation accounts for
2908-
* user-defined definitions of `==`.
2909-
*
2910-
* Overwritten to no-op in ReTyper.
2911-
*/
2907+
* against a selector of type `pt`. This implementation accounts for
2908+
* user-defined definitions of `==`.
2909+
*
2910+
* Overwritten to no-op in ReTyper.
2911+
*/
29122912
protected def checkEqualityEvidence(tree: tpd.Tree, pt: Type)(implicit ctx: Context) : Unit = {
29132913
tree match {
29142914
case _: RefTree | _: Literal

tests/neg/i5976.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
def f(i: => Int) = i + i
3+
val res = List(42).map(f) // error
4+
5+
val g: (=> Int) => Int = f
6+
val h: Int => Int = g // error
7+
}

tests/run/i5976.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Test extends App {
2+
def f(i: => Int) = i + i
3+
implicit def ups(f: ((=>Int) => Int)): (Int => Int) = x => f(x)
4+
val res = List(42).map(f)
5+
6+
val g: (=> Int) => Int = f
7+
val h: Int => Int = g
8+
9+
assert(res == List(84))
10+
}

0 commit comments

Comments
 (0)