From 753a50ae31b1bd9f58cb95f9fe1ce91a5c7bdab1 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 25 Jan 2018 18:16:42 +0100 Subject: [PATCH 1/5] WIP - dependent types --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ .../tools/dotc/core/SymDenotations.scala | 4 +++ .../src/dotty/tools/dotc/typer/Namer.scala | 13 +++++++- library/src/scala/annotation/dependent.scala | 3 ++ tests/pos/dependent-1262.scala | 18 +++++++++++ tests/pos/dependent-class.scala | 31 +++++++++++++++++++ tests/pos/dependent-t5700.scala | 12 +++++++ 7 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 library/src/scala/annotation/dependent.scala create mode 100644 tests/pos/dependent-1262.scala create mode 100644 tests/pos/dependent-class.scala create mode 100644 tests/pos/dependent-t5700.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 103a6275a2a6..d7b165f1ba3f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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") diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 5ee57d02a107..987d62ad2e32 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1665,6 +1665,10 @@ object SymDenotations { case tparams: List[Symbol @unchecked] => baseTypeOf(tycon).subst(tparams, args) } + case tp @ RefinedType(parent, name, AnnotatedType(refine, annot)) + if annot.symbol eq defn.DependentAnnot => + val res = baseTypeOf(tp.superType) + res.subst(tp.nonPrivateMember(name).symbol :: Nil, refine :: Nil) case tp: TypeProxy => baseTypeOf(tp.superType) case AndType(tp1, tp2) => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e9dcd406716d..651760015890 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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) } diff --git a/library/src/scala/annotation/dependent.scala b/library/src/scala/annotation/dependent.scala new file mode 100644 index 000000000000..9ca30db3991a --- /dev/null +++ b/library/src/scala/annotation/dependent.scala @@ -0,0 +1,3 @@ +package scala.annotation + +final class dependent extends StaticAnnotation diff --git a/tests/pos/dependent-1262.scala b/tests/pos/dependent-1262.scala new file mode 100644 index 000000000000..e068ba02e86b --- /dev/null +++ b/tests/pos/dependent-1262.scala @@ -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 +} \ No newline at end of file diff --git a/tests/pos/dependent-class.scala b/tests/pos/dependent-class.scala new file mode 100644 index 000000000000..6333dd79059b --- /dev/null +++ b/tests/pos/dependent-class.scala @@ -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 +} \ No newline at end of file diff --git a/tests/pos/dependent-t5700.scala b/tests/pos/dependent-t5700.scala new file mode 100644 index 000000000000..ac8eee91a2f1 --- /dev/null +++ b/tests/pos/dependent-t5700.scala @@ -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 }) + } +} From eac71902712a82726261aee22b8407b6479281dd Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 29 Jan 2018 07:14:29 +0100 Subject: [PATCH 2/5] add test for subtyping --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- .../tools/dotc/core/SymDenotations.scala | 3 +- .../src/dotty/tools/dotc/core/Types.scala | 6 +++ tests/neg/dependent-subtype.scala | 45 +++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 tests/neg/dependent-subtype.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f9d76d083cbd..49e11fe78024 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 987d62ad2e32..c8413bb918ea 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1665,8 +1665,7 @@ object SymDenotations { case tparams: List[Symbol @unchecked] => baseTypeOf(tycon).subst(tparams, args) } - case tp @ RefinedType(parent, name, AnnotatedType(refine, annot)) - if annot.symbol eq defn.DependentAnnot => + 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 => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index baeaa9fc4ee6..83464b4e52e8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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 { diff --git a/tests/neg/dependent-subtype.scala b/tests/neg/dependent-subtype.scala new file mode 100644 index 000000000000..55d764b19aa9 --- /dev/null +++ b/tests/neg/dependent-subtype.scala @@ -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 +} \ No newline at end of file From e2f06b8f6e0d5542efc8f7733741409d67040e40 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 29 Jan 2018 07:48:51 +0100 Subject: [PATCH 3/5] add test to fromTasty blacklist --- compiler/test/dotty/tools/dotc/FromTastyTests.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 1ae93007b300..7f15db03dfe4 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -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", From 20b26dc859e4aa6a85baf4971629fe050301d6f8 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 29 Jan 2018 14:37:15 +0100 Subject: [PATCH 4/5] add ML-like functor module example --- tests/run/dependent-functor.scala | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/run/dependent-functor.scala diff --git a/tests/run/dependent-functor.scala b/tests/run/dependent-functor.scala new file mode 100644 index 000000000000..46501533c206 --- /dev/null +++ b/tests/run/dependent-functor.scala @@ -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)) + } +} From 1eab668751ad01924abb1b1d7c5722fa3b5e583a Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 29 Jan 2018 15:08:40 +0100 Subject: [PATCH 5/5] skip fromTasty test --- compiler/test/dotty/tools/dotc/FromTastyTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 7f15db03dfe4..e8b03d7d554b 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -85,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",