aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/ast/Desugar.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-11-26 11:46:46 +0100
committerMartin Odersky <odersky@gmail.com>2014-11-26 11:46:52 +0100
commitfc319b002ff4bc82061250352f1568c612c70d72 (patch)
treef2f8a76eb5e8dd8926a35271118cf0e3b91182f5 /src/dotty/tools/dotc/ast/Desugar.scala
parent5733684a4ec6857ece1048d56654dcd749163510 (diff)
downloaddotty-fc319b002ff4bc82061250352f1568c612c70d72.tar.gz
dotty-fc319b002ff4bc82061250352f1568c612c70d72.tar.bz2
dotty-fc319b002ff4bc82061250352f1568c612c70d72.zip
Allow refinements that refine already refined types.
Previously, a double definition errorfor `T` was produced in a case like this: type T1 = C { T <: A } type T2 = T1 { T <: B } This was caused by the way T1 was treated in the refinement class that is used to typecheck the type. Desugaring of T2 with `refinedTypeToClass` would give trait <refinement> extends T1 { type T <: B } and `normalizeToClassRefs` would transform this to: trait <refinement> extends C { type T <: A; type T <: B } Hence the double definition. The new scheme desugars the rhs of `T2` to: trait <refinement> extends C { this: T1 => type T <: B } which avoids the problem. Also, added tests that #232 (fix/boundsPropagation) indeed considers all refinements together when comparing refined types.
Diffstat (limited to 'src/dotty/tools/dotc/ast/Desugar.scala')
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala42
1 files changed, 35 insertions, 7 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 1aab16469..05f652a39 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -834,17 +834,45 @@ object desugar {
}
}.withPos(tree.pos)
- /** Create a class definition with the same info as this refined type.
+ /** Create a class definition with the same info as the refined type given by `parent`
+ * and `refinements`.
+ *
* parent { refinements }
* ==>
- * trait <refinement> extends parent { refinements }
+ * trait <refinement> extends core { this: self => refinements }
+ *
+ * Here, `core` is the (possibly parameterized) class part of `parent`.
+ * If `parent` is the same as `core`, self is empty. Otherwise `self` is `parent`.
+ *
+ * Example: Given
+ *
+ * class C
+ * type T1 extends C { type T <: A }
+ *
+ * the refined type
*
- * If the parent is missing, Object is assumed.
- * The result is used for validity checking, is thrown away afterwards.
+ * T1 { type T <: B }
+ *
+ * is expanded to
+ *
+ * trait <refinement> extends C { this: T1 => type T <: A }
+ *
+ * The result of this method is used for validity checking, is thrown away afterwards.
+ * @param parentType The type of `parent`
*/
- def refinedTypeToClass(tree: RefinedTypeTree)(implicit ctx: Context): TypeDef = {
- val parent = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else tree.tpt
- val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, tree.refinements)
+ def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = {
+ def stripToCore(tp: Type): Type = tp match {
+ case tp: RefinedType if tp.argInfos.nonEmpty => tp // parameterized class type
+ case tp: TypeRef if tp.symbol.isClass => tp // monomorphic class type
+ case tp: TypeProxy => stripToCore(tp.underlying)
+ case _ => defn.AnyType
+ }
+ val parentCore = stripToCore(parent.tpe)
+ val untpdParent = TypedSplice(parent)
+ val (classParent, self) =
+ if (parent.tpe eq parentCore) (untpdParent, EmptyValDef)
+ else (TypeTree(parentCore), ValDef(nme.WILDCARD, untpdParent, EmptyTree))
+ val impl = Template(emptyConstructor, classParent :: Nil, self, refinements)
TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
}