Skip to content

Add subtype annotations #4625

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

Closed
wants to merge 15 commits into from
Prev Previous commit
Next Next commit
More polishings and tests
  • Loading branch information
odersky committed Jun 6, 2018
commit 62b97b8c8f1236a95eecc4d415efd2ba64ccc51c
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
/** An extractor for def of a closure contained the block of the closure. */
object closureDef {
def unapply(tree: Tree): Option[DefDef] = tree match {
case Block(Nil, expr) => unapply(expr)
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
Some(meth)
case _ => None
Expand Down
44 changes: 17 additions & 27 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,13 @@ object Inliner {
// This is quite tricky, as such types can appear anywhere, including as parts
// of types of other things. For the moment we do nothing and complain
// at the implicit expansion site if there's a reference to an inaccessible type.
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tree: Assign =>
transform(tree.lhs) match {
case lhs1: RefTree =>
lhs1.name match {
case InlineAccessorName(name) =>
cpy.Apply(tree)(useSetter(lhs1), transform(tree.rhs) :: Nil)
case _ =>
super.transform(tree)
}
case _ =>
super.transform(tree)
}
case _ =>
super.transform(accessorIfNeeded(tree))
}

override def transform(tree: Tree)(implicit ctx: Context): Tree =
super.transform(accessorIfNeeded(tree)) match {
case tree1 @ Assign(lhs: RefTree, rhs) if lhs.symbol.name.is(InlineAccessorName) =>
cpy.Apply(tree1)(useSetter(lhs), rhs :: Nil)
case tree1 =>
tree1
}
}

/** Adds accessors for all non-public term members accessed
Expand Down Expand Up @@ -104,14 +94,14 @@ object Inliner {
* to have the inlined method as owner.
*/
def registerInlineInfo(
sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
sym.unforcedAnnotation(defn.BodyAnnot) match {
inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
inlined.unforcedAnnotation(defn.BodyAnnot) match {
case Some(ann: ConcreteBodyAnnotation) =>
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
case _ =>
if (!ctx.isAfterTyper) {
val inlineCtx = ctx
sym.updateAnnotation(LazyBodyAnnotation { _ =>
inlined.updateAnnotation(LazyBodyAnnotation { _ =>
implicit val ctx = inlineCtx
val body = treeExpr(ctx)
if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body)
Expand Down Expand Up @@ -173,10 +163,10 @@ object Inliner {

/** Produces an inlined version of `call` via its `inlined` method.
*
* @param call The original call to a `@inline` method
* @param rhs The body of the inline method that replaces the call.
* @param call the original call to a `@inline` method
* @param rhsToInline the body of the inline method that replaces the call.
*/
class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
import tpd._
import Inliner._

Expand All @@ -200,7 +190,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
*/
private val paramProxy = new mutable.HashMap[Type, Type]

/** A map from the classes of (direct and outer) this references in `rhs`
/** A map from the classes of (direct and outer) this references in `rhsToInline`
* to references of their proxies.
* Note that we can't index by the ThisType itself since there are several
* possible forms to express what is logicaly the same ThisType. E.g.
Expand Down Expand Up @@ -315,7 +305,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType)

// Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined.
rhs.foreachSubTree(registerLeaf)
rhsToInline.foreachSubTree(registerLeaf)

// The class that the this-proxy `selfSym` represents
def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
Expand Down Expand Up @@ -387,7 +377,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
// the owner from the inlined method to the current owner.
val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)(inlineCtx)

val expansion = inliner(rhs.withPos(call.pos))
val expansion = inliner(rhsToInline.withPos(call.pos))
trace(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) {

// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
Expand Down Expand Up @@ -426,7 +416,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
/** A typer for inlined code. Its purpose is:
* 1. Implement constant folding over inlined code
* 2. Selectively expand ifs with constant conditions
* 3. Inline arguments that are inlineable closures
* 3. Inline arguments that are by-name closures
* 4. Make sure inlined code is type-correct.
* 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
*/
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -821,12 +821,12 @@ class Namer { typer: Typer =>
else completeInCreationContext(denot)
}

private def addInlineInfo(denot: SymDenotation) = original match {
case original: untpd.DefDef if denot.isInlineableMethod =>
private def addInlineInfo(sym: Symbol) = original match {
case original: untpd.DefDef if sym.isInlineableMethod =>
Inliner.registerInlineInfo(
denot,
sym,
implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
)(localContext(denot.symbol))
)(localContext(sym))
case _ =>
}

Expand All @@ -839,7 +839,7 @@ class Namer { typer: Typer =>
case original: MemberDef => addAnnotations(sym, original)
case _ =>
}
addInlineInfo(denot)
addInlineInfo(sym)
denot.info = typeSig(sym)
Checking.checkWellFormed(sym)
denot.info = avoidPrivateLeaks(sym, sym.pos)
Expand Down
1 change: 1 addition & 0 deletions tests/run/inline-implicits.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(X(),Y())
26 changes: 26 additions & 0 deletions tests/run/inline-implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
case class X()
case class Y()

object impl {
implicit val y: Y = new Y()
}

object inlines {
import impl._

class C {
implicit val x: X = new X()

inline
def f(): (X, Y) =
(implicitly[X], implicitly[Y])
}
}

object Test {
def main(args: Array[String]) = {
val c = new inlines.C
val xy = c.f()
println(xy)
}
}
3 changes: 3 additions & 0 deletions tests/run/typelevel.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ToNat(Z)
ToNat(S(Z))
ToNat(S(S(Z)))
31 changes: 31 additions & 0 deletions tests/run/typelevel.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
object Test extends App {

trait Nat

case object Z extends Nat
type Z = Z.type
case class S[N <: Nat](n: Nat) extends Nat

abstract class HasResult[T] { type Result = T }
case class ToNat[+T](val value: T) extends HasResult[T]

transparent def ToNat(inline n: Int): ToNat[Nat] =
if n == 0 then new ToNat(Z)
else {
val n1 = ToNat(n - 1)
new ToNat[S[n1.Result]](S(n1.value))
}

val x0 = ToNat(0)
//val y0: Z = x0.value
//val z0: x0.Result = y0
val x1 = ToNat(1)
//val y1: S[Z] = x1.value
//val z1: x1.Result = y1
val x2 = ToNat(2)
//val y2: S[S[Z]] = x2.value
//val z2: x2.Result = y2
println(x0)
println(x1)
println(x2)
}