Skip to content

Fix #5976: Don't assume T <:< (=>T). #5981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ object Denotations {
}

case _ =>
try tp1 & tp2
try tp1.widenExpr & tp2.widenExpr
catch {
case ex: Throwable =>
println(i"error for meet: $tp1 &&& $tp2, ${tp1.getClass}, ${tp2.getClass}")
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
// ()T <:< => T, since everything one can do with a => T one can
// also do with a ()T by automatic () insertion.
case tp1 @ MethodType(Nil) => isSubType(tp1.resultType, restpe2)
case _ => isSubType(tp1.widenExpr, restpe2)
case tp1 @ ExprType(restpe1) => isSubType(restpe1, restpe2)
case _ => fourthTry
}
compareExpr
case tp2 @ TypeBounds(lo2, hi2) =>
Expand Down Expand Up @@ -1237,7 +1238,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] {
}

def qualifies(m: SingleDenotation) =
isSubType(m.info, rinfo2) || matchAbstractTypeMember(m.info)
isSubType(m.info.widenExpr, rinfo2.widenExpr) || matchAbstractTypeMember(m.info)

tp1.member(name) match { // inlined hasAltWith for performance
case mbr: SingleDenotation => qualifies(mbr)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ object Applications {
def lengthCompareTp = MethodType(List(defn.IntType), defn.IntType)
def applyTp(elemTp: Type) = MethodType(List(defn.IntType), elemTp)
def dropTp(elemTp: Type) = MethodType(List(defn.IntType), defn.SeqType.appliedTo(elemTp))
def toSeqTp(elemTp: Type) = ExprType(defn.SeqType.appliedTo(elemTp))
def toSeqTp(elemTp: Type) = defn.SeqType.appliedTo(elemTp)

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have some guarantee that the result of argType cannot be an ExprType here ? Even if we do, it seems more regular to widen both sides.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case we dropped in TypeComparer is where the RHS is an ExprType. => T was never a subtype of T.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but if both argType and formal are => T, then argOK used to return true but will return false now that we widen formal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I believe argTypes are never ExprTypes. They might be TermRefs with an underlying ExprType. But we skip the ExprType when we do a widen on these. lub also does widen, not widenExpr. So I think we are good.

}

/** Subclass of Application for applicability tests with value argument types. */
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ object Implicits {
else if (mt.paramInfos.lengthCompare(1) == 0 && {
var formal = widenSingleton(mt.paramInfos.head)
if (approx) formal = wildApprox(formal)
ctx.test(implicit ctx => argType relaxed_<:< formal)
ctx.test(implicit ctx => argType relaxed_<:< formal.widenExpr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here.

})
Candidate.Conversion
else
Expand Down Expand Up @@ -1039,7 +1039,7 @@ trait Implicits { self: Typer =>
val locked = ctx.typerState.ownedVars
val adapted =
if (argument.isEmpty)
adapt(generated, pt, locked)
adapt(generated, pt.widenExpr, locked)
else {
val untpdGenerated = untpd.TypedSplice(generated)
def tryConversion(implicit ctx: Context) =
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ class Namer { typer: Typer =>

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

// Approximate a type `tp` with a type that does not contain skolem types.
val deskolemize = new ApproximatingTypeMap {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ object ProtoTypes {
* 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type
* 3. there is an implicit conversion from `tp` to `pt`.
* 4. `tp` is a numeric subtype of `pt` (this case applies even if implicit conversions are disabled)
* If `pt` is a by-name type, we compare against the underlying type instead.
*/
def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
(tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ trait TypeAssigner {
required = EmptyFlagConjunction, excluded = Private)
.suchThat(decl.matches(_))
val inheritedInfo = inherited.info
if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) {
if (inheritedInfo.exists &&
decl.info.widenExpr <:< inheritedInfo.widenExpr &&
!(inheritedInfo.widenExpr <:< decl.info.widenExpr)) {
val r = RefinedType(parent, decl.name, decl.info)
typr.println(i"add ref $parent $decl --> " + r)
r
Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ class Typer extends Namer
fullyDefinedType(tree.tpe, "block", tree.span)
var avoidingType = avoid(tree.tpe, localSyms)
val ptDefined = isFullyDefined(pt, ForceDegree.none)
if (ptDefined && !(avoidingType <:< pt)) avoidingType = pt
if (ptDefined && !(avoidingType.widenExpr <:< pt)) avoidingType = pt
val tree1 = ascribeType(tree, avoidingType)
assert(ptDefined || noLeaks(tree1) || tree1.tpe.isErroneous,
// `ptDefined` needed because of special case of anonymous classes
Expand Down Expand Up @@ -954,7 +954,7 @@ class Typer extends Namer
(defn.isProductSubType(formal) || formal.derivesFrom(defn.PairClass)) &&
productSelectorTypes(formal, tree.sourcePos).corresponds(params) {
(argType, param) =>
param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe
param.tpt.isEmpty || argType.widenExpr <:< typedAheadType(param.tpt).tpe
}
}

Expand Down Expand Up @@ -1482,7 +1482,7 @@ class Typer extends Namer
val tpt1 = checkSimpleKinded(typedType(tpt))
val rhs1 = vdef.rhs match {
case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe
case rhs => typedExpr(rhs, tpt1.tpe)
case rhs => typedExpr(rhs, tpt1.tpe.widenExpr)
}
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
if (sym.is(Inline, butNot = DeferredOrTermParamOrAccessor))
Expand Down Expand Up @@ -1549,7 +1549,7 @@ class Typer extends Namer
}
}
if (sym.isInlineMethod) rhsCtx = rhsCtx.addMode(Mode.InlineableBody)
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx)
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)

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

Expand Down Expand Up @@ -2310,7 +2310,7 @@ class Typer extends Namer
}

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

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

/** Check that `tree == x: pt` is typeable. Used when checking a pattern
* against a selector of type `pt`. This implementation accounts for
* user-defined definitions of `==`.
*
* Overwritten to no-op in ReTyper.
*/
* against a selector of type `pt`. This implementation accounts for
* user-defined definitions of `==`.
*
* Overwritten to no-op in ReTyper.
*/
protected def checkEqualityEvidence(tree: tpd.Tree, pt: Type)(implicit ctx: Context) : Unit = {
tree match {
case _: RefTree | _: Literal
Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i5976.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Test {
def f(i: => Int) = i + i
val res = List(42).map(f) // error

val g: (=> Int) => Int = f
val h: Int => Int = g // error
}
10 changes: 10 additions & 0 deletions tests/run/i5976.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
object Test extends App {
def f(i: => Int) = i + i
implicit def ups(f: ((=>Int) => Int)): (Int => Int) = x => f(x)
val res = List(42).map(f)

val g: (=> Int) => Int = f
val h: Int => Int = g

assert(res == List(84))
}