Skip to content

WIP- Implement Dependent Class Type #3936

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 5 commits into from
Closed
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
def isStructuralTermSelect(tree: Tree)(implicit ctx: Context) = tree match {
case tree: Select =>
def hasRefinement(qualtpe: Type): Boolean = qualtpe.dealias match {
case RefinedType(parent, rname, rinfo) =>
case tp @ RefinedType(parent, rname, rinfo) if !tp.isDependent =>
rname == tree.name || hasRefinement(parent)
case tp: TypeProxy =>
hasRefinement(tp.underlying)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,8 @@ class Definitions {
def TASTYLongSignatureAnnot(implicit ctx: Context) = TASTYLongSignatureAnnotType.symbol.asClass
lazy val TailrecAnnotType = ctx.requiredClassRef("scala.annotation.tailrec")
def TailrecAnnot(implicit ctx: Context) = TailrecAnnotType.symbol.asClass
lazy val DependentAnnotType = ctx.requiredClassRef("scala.annotation.dependent")
def DependentAnnot(implicit ctx: Context) = DependentAnnotType.symbol.asClass
lazy val SwitchAnnotType = ctx.requiredClassRef("scala.annotation.switch")
def SwitchAnnot(implicit ctx: Context) = SwitchAnnotType.symbol.asClass
lazy val ThrowsAnnotType = ctx.requiredClassRef("scala.throws")
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,9 @@ object SymDenotations {
case tparams: List[Symbol @unchecked] =>
baseTypeOf(tycon).subst(tparams, args)
}
case tp @ RefinedType(parent, name, refine) if tp.isDependent =>
val res = baseTypeOf(tp.superType)
res.subst(tp.nonPrivateMember(name).symbol :: Nil, refine :: Nil)
case tp: TypeProxy =>
baseTypeOf(tp.superType)
case AndType(tp1, tp2) =>
Expand Down
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2255,6 +2255,12 @@ object Types {
if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo)
else parent

/** Is current type a dependent class refinement, i.e. { val x: T @dependent } ? */
def isDependent(implicit ctx: Context): Boolean = refinedInfo match {
case AnnotatedType(_, annot) => annot.symbol eq defn.DependentAnnot
case _ => false
}

override def computeHash = doHash(refinedName, refinedInfo, parent)

override def eql(that: Type) = that match {
Expand Down
13 changes: 12 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,18 @@ class Namer { typer: Typer =>
if (isConstructor) {
// set result type tree to unit, but take the current class as result type of the symbol
typedAheadType(ddef.tpt, defn.UnitType)
wrapMethType(ctx.effectiveResultType(sym, typeParams, NoType))

val base = ctx.effectiveResultType(sym, typeParams, NoType)
val refined = termParamss.foldLeft(base) { (acc, termParams) =>
termParams.foldLeft(base) { case (acc, termParam) =>
val dependentAnnot = termParam.annotations.find(_.symbol eq defn.DependentAnnot)
if (dependentAnnot.nonEmpty)
RefinedType(acc, termParam.name, AnnotatedType(termParam.termRef, dependentAnnot.get))
else acc
}
}

wrapMethType(refined)
}
else valOrDefDefSig(ddef, sym, typeParams, termParamss, wrapMethType)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/test/dotty/tools/dotc/FromTastyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class FromTastyTests extends ParallelTesting {
"t8023.scala",
"hklub0.scala",
"i1365.scala",
"dependent-class.scala",
"dependent-t5700.scala",
"dependent-1262.scala",

// Missing position
"i3000.scala",
Expand Down Expand Up @@ -82,6 +85,7 @@ class FromTastyTests extends ParallelTesting {
val (step1, step2, step3) = compileTastyInDir("../tests/run", defaultOptions,
blacklist = Set(
"t3613.scala",
"dependent-functor.scala",

// Missing position
"Course-2002-13.scala",
Expand Down
3 changes: 3 additions & 0 deletions library/src/scala/annotation/dependent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package scala.annotation

final class dependent extends StaticAnnotation
45 changes: 45 additions & 0 deletions tests/neg/dependent-subtype.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import scala.annotation.dependent

trait Foo {
type Bar
}

class Quux(@dependent val foo: Foo)
class Child1(@dependent override val foo: Foo) extends Quux(foo)
class Child2(override val foo: Foo) extends Quux(foo)
class Child3(foo: Foo) extends Quux(foo)

object FooInt extends Foo {
type Bar = Int
}

object Test {
val quux1 = new Child1(FooInt)
val x1: quux1.foo.Bar = 3 // ok

val quux2 = new Child2(FooInt)
val x2: quux2.foo.Bar = 3 // error

val quux3 = new Child3(FooInt)
val x3: quux3.foo.Bar = 3 // error
}


object Test2 {
trait C
class D extends C
class E extends D

class Bar(@dependent o: C)

val x1 = new Bar(new D)
val d: D = x1.o // ok

val x2 = new Bar(new E)
val e: E = x2.o // ok

def f(x: Int): E = ???

val x3 = new Bar(f(5))
val e2: E = x3.o // ok
}
18 changes: 18 additions & 0 deletions tests/pos/dependent-1262.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.annotation.dependent

trait Foo {
type Bar
}

trait Quux(val foo: Foo)

class Child(@dependent override val foo: Foo) extends Quux(foo)

object FooInt extends Foo {
type Bar = Int
}

object Test {
val quux = new Child(FooInt)
val x: quux.foo.Bar = 3
}
31 changes: 31 additions & 0 deletions tests/pos/dependent-class.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import scala.annotation.dependent

class Animal(name: String)

trait Foo[T]
class Bar(@dependent val a: Animal) extends Foo[a.type]

class Bag(@dependent val a: Animal) {
def g(n: Int): Foo[a.type] = ???
def f(n: Int): a.type = ???
}

object Test {
def foo(a: Animal): Foo[a.type] = ???

val dog = new Animal("dog")

// new instance
new Bar(dog) : Foo[dog.type] // found: Bar, required: Foo[Animal(Test.dog)]

// dependent method
val c: Foo[dog.type] = foo(dog) // works well

// dependent function type
val f : (a: Animal) => Foo[a.type] = ??? // works well
f(dog) : Foo[dog.type]

// dependent class method
new Bag(dog).g(6) : Foo[dog.type]
new Bag(dog).f(5) : dog.type
}
12 changes: 12 additions & 0 deletions tests/pos/dependent-t5700.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.annotation.dependent

trait Foo { type FA }
class Bar(@dependent val foo: Foo) {
type FA = foo.FA
}

object Test {
def main(argv: Array[String]) = {
val barLong: Bar { type FA = Long } = new Bar(new Foo { type FA = Long })
}
}
33 changes: 33 additions & 0 deletions tests/run/dependent-functor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import scala.annotation.dependent

trait Ordering {
type T
def compare(t1:T, t2: T): Int
}

class SetFunctor(@dependent val ord: Ordering) {
type Set = List[ord.T]
def empty: Set = Nil

implicit class helper(s: Set) {
def add(x: ord.T): Set = x :: remove(x)
def remove(x: ord.T): Set = s.filter(e => ord.compare(x, e) != 0)
def member(x: ord.T): Boolean = s.exists(e => ord.compare(x, e) == 0)
}
}

object Test {
val orderInt = new Ordering {
type T = Int
def compare(t1: T, t2: T): Int = t1 - t2
}

val IntSet = new SetFunctor(orderInt)
import IntSet._

def main(args: Array[String]) = {
val set = IntSet.empty.add(6).add(8).add(23)
assert(!set.member(7))
assert(set.member(8))
}
}