Skip to content

Always fully define the types of lifted expressions #823

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
Oct 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Names._
import StdNames._
import ProtoTypes._
import EtaExpansion._
import Inferencing._
import collection.mutable
import config.Printers._
import TypeApplications._
Expand Down
4 changes: 3 additions & 1 deletion src/dotty/tools/dotc/typer/EtaExpansion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Decorators._
import Names._
import StdNames._
import Trees._
import Inferencing._
import util.Positions._
import collection.mutable

Expand All @@ -24,7 +25,8 @@ object EtaExpansion {
if (isPureExpr(expr)) expr
else {
val name = ctx.freshName(prefix).toTermName
val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe.widen, coord = positionCoord(expr.pos))
val liftedType = fullyDefinedType(expr.tpe.widen, "lifted expression", expr.pos)
val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, liftedType, coord = positionCoord(expr.pos))
defs += ValDef(sym, expr)
ref(sym.valRef)
}
Expand Down
43 changes: 1 addition & 42 deletions src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import config.Printers._
import annotation.tailrec
import collection.mutable

trait Inferencing { this: Checking =>
object Inferencing {

import tpd._

Expand Down Expand Up @@ -197,47 +197,6 @@ trait Inferencing { this: Checking =>
case _ => NoType
}

/** Ensure that the first type in a list of parent types Ps points to a non-trait class.
* If that's not already the case, add one. The added class type CT is determined as follows.
* First, let C be the unique class such that
* - there is a parent P_i such that P_i derives from C, and
* - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
* Then, let CT be the smallest type which
* - has C as its class symbol, and
* - for all parents P_i: If P_i derives from C then P_i <:< CT.
*/
def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = {
def realClassParent(cls: Symbol): ClassSymbol =
if (!cls.isClass) defn.ObjectClass
else if (!(cls is Trait)) cls.asClass
else cls.asClass.classParents match {
case parentRef :: _ => realClassParent(parentRef.symbol)
case nil => defn.ObjectClass
}
def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = {
val pcls = realClassParent(parent.classSymbol)
if (pcls derivesFrom candidate) pcls else candidate
}
parents match {
case p :: _ if p.classSymbol.isRealClass => parents
case _ =>
val pcls = (defn.ObjectClass /: parents)(improve)
typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %")
val ptype = ctx.typeComparer.glb(
defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls)))
ptype :: parents
}
}

/** Ensure that first parent tree refers to a real class. */
def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match {
case p :: ps if p.tpe.classSymbol.isRealClass => parents
case _ =>
// add synthetic class type
val first :: _ = ensureFirstIsClass(parents.tpes)
TypeTree(checkFeasible(first, pos, d"\n in inferred parent $first")).withPos(pos) :: parents
}

/** Interpolate those undetermined type variables in the widened type of this tree
* which are introduced by type application contained in the tree.
* If such a variable appears covariantly in type `tp` or does not appear at all,
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ErrorReporting._
import tpd.ListOfTreeDecorator
import config.Printers._
import Annotations._
import Inferencing._
import transform.ValueClasses._
import language.implicitConversions

Expand Down
44 changes: 43 additions & 1 deletion src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Flags._
import Decorators._
import ErrorReporting._
import Checking._
import Inferencing._
import EtaExpansion.etaExpand
import dotty.tools.dotc.transform.Erasure.Boxing
import util.Positions._
Expand Down Expand Up @@ -55,7 +56,7 @@ object Typer {
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
}

class Typer extends Namer with TypeAssigner with Applications with Implicits with Inferencing with Checking {
class Typer extends Namer with TypeAssigner with Applications with Implicits with Checking {

import Typer._
import tpd.{cpy => _, _}
Expand Down Expand Up @@ -977,6 +978,47 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
// 4. Polymorphic type defs override nothing.
}

/** Ensure that the first type in a list of parent types Ps points to a non-trait class.
* If that's not already the case, add one. The added class type CT is determined as follows.
* First, let C be the unique class such that
* - there is a parent P_i such that P_i derives from C, and
* - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
* Then, let CT be the smallest type which
* - has C as its class symbol, and
* - for all parents P_i: If P_i derives from C then P_i <:< CT.
*/
def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = {
def realClassParent(cls: Symbol): ClassSymbol =
if (!cls.isClass) defn.ObjectClass
else if (!(cls is Trait)) cls.asClass
else cls.asClass.classParents match {
case parentRef :: _ => realClassParent(parentRef.symbol)
case nil => defn.ObjectClass
}
def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = {
val pcls = realClassParent(parent.classSymbol)
if (pcls derivesFrom candidate) pcls else candidate
}
parents match {
case p :: _ if p.classSymbol.isRealClass => parents
case _ =>
val pcls = (defn.ObjectClass /: parents)(improve)
typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %")
val ptype = ctx.typeComparer.glb(
defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls)))
ptype :: parents
}
}

/** Ensure that first parent tree refers to a real class. */
def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match {
case p :: ps if p.tpe.classSymbol.isRealClass => parents
case _ =>
// add synthetic class type
val first :: _ = ensureFirstIsClass(parents.tpes)
TypeTree(checkFeasible(first, pos, d"\n in inferred parent $first")).withPos(pos) :: parents
}

/** If this is a real class, make sure its first parent is a
* constructor call. Cannot simply use a type. Overridden in ReTyper.
*/
Expand Down
1 change: 1 addition & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class tests extends CompilerTest {
// This directory doesn't exist anymore
// @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling)
@Test def pickle_ast = compileDir(dotcDir, "ast", testPickling)
@Test def pickle_inf = compileFile(posDir, "pickleinf", testPickling)

//@Test def pickle_core = compileDir(dotcDir, "core", testPickling, xerrors = 2) // two spurious comparison errors in Types and TypeOps

Expand Down
9 changes: 9 additions & 0 deletions tests/pos/pickleinf.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Bar[N] {
def bar(name: N, dummy: Int = 42): N = name
}

object Test {
def test(): Unit = {
(new Bar).bar(10)
}
}