Skip to content

Commit b18ce86

Browse files
committed
Merge pull request #244 from dotty-staging/fix/refinementTypes-v2
Allow refinements that refine already refined types.
2 parents 5733684 + fc319b0 commit b18ce86

File tree

5 files changed

+103
-8
lines changed

5 files changed

+103
-8
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -834,17 +834,45 @@ object desugar {
834834
}
835835
}.withPos(tree.pos)
836836

837-
/** Create a class definition with the same info as this refined type.
837+
/** Create a class definition with the same info as the refined type given by `parent`
838+
* and `refinements`.
839+
*
838840
* parent { refinements }
839841
* ==>
840-
* trait <refinement> extends parent { refinements }
842+
* trait <refinement> extends core { this: self => refinements }
843+
*
844+
* Here, `core` is the (possibly parameterized) class part of `parent`.
845+
* If `parent` is the same as `core`, self is empty. Otherwise `self` is `parent`.
846+
*
847+
* Example: Given
848+
*
849+
* class C
850+
* type T1 extends C { type T <: A }
851+
*
852+
* the refined type
841853
*
842-
* If the parent is missing, Object is assumed.
843-
* The result is used for validity checking, is thrown away afterwards.
854+
* T1 { type T <: B }
855+
*
856+
* is expanded to
857+
*
858+
* trait <refinement> extends C { this: T1 => type T <: A }
859+
*
860+
* The result of this method is used for validity checking, is thrown away afterwards.
861+
* @param parentType The type of `parent`
844862
*/
845-
def refinedTypeToClass(tree: RefinedTypeTree)(implicit ctx: Context): TypeDef = {
846-
val parent = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else tree.tpt
847-
val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, tree.refinements)
863+
def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = {
864+
def stripToCore(tp: Type): Type = tp match {
865+
case tp: RefinedType if tp.argInfos.nonEmpty => tp // parameterized class type
866+
case tp: TypeRef if tp.symbol.isClass => tp // monomorphic class type
867+
case tp: TypeProxy => stripToCore(tp.underlying)
868+
case _ => defn.AnyType
869+
}
870+
val parentCore = stripToCore(parent.tpe)
871+
val untpdParent = TypedSplice(parent)
872+
val (classParent, self) =
873+
if (parent.tpe eq parentCore) (untpdParent, EmptyValDef)
874+
else (TypeTree(parentCore), ValDef(nme.WILDCARD, untpdParent, EmptyTree))
875+
val impl = Template(emptyConstructor, classParent :: Nil, self, refinements)
848876
TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
849877
}
850878

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
756756

757757
def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") {
758758
val tpt1 = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else typedAheadType(tree.tpt)
759-
val refineClsDef = desugar.refinedTypeToClass(tree)
759+
val refineClsDef = desugar.refinedTypeToClass(tpt1, tree.refinements)
760760
val refineCls = createSymbol(refineClsDef).asClass
761761
val TypeDef(_, Template(_, _, _, refinements1)) = typed(refineClsDef)
762762
assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1")

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class tests extends CompilerTest {
106106
@Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1)
107107
@Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 8)
108108
@Test def neg_boundspropagation = compileFile(negDir, "boundspropagation", xerrors = 4)
109+
@Test def neg_refinedSubtyping = compileFile(negDir, "refinedSubtyping", xerrors = 2)
109110

110111
@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes)
111112
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)

tests/neg/refinedSubtyping.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// tests that a refinement subtype satisfies all constraint
2+
// of its refinemen supertype
3+
class Test3 {
4+
5+
trait A
6+
trait B
7+
8+
class C { type T }
9+
10+
type T1 = C { type T <: A }
11+
type T2 = T1 { type T <: B }
12+
13+
type U1 = C { type T <: B }
14+
type U2 = C { type T <: A }
15+
16+
var x: T2 = _
17+
val y1: U1 = ???
18+
val y2: U2 = ???
19+
20+
x = y1 // error
21+
x = y2 // error
22+
23+
}

tests/pos/refinedSubtyping.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,46 @@ class Test {
1717
y = x
1818

1919
}
20+
21+
class Test2 {
22+
23+
trait A
24+
trait B
25+
26+
class C { type T }
27+
28+
type T1 = C { type T <: A } { type T <: B }
29+
30+
type U1 = C { type T <: B } { type T <: A }
31+
32+
var x: T1 = _
33+
var y: U1 = _
34+
35+
x = y
36+
y = x
37+
}
38+
39+
40+
class Test3 {
41+
42+
trait A
43+
trait B
44+
45+
class C { type T }
46+
47+
type T1 = C { type T <: A }
48+
type T2 = T1 { type T <: B }
49+
50+
type U1 = C { type T <: B }
51+
type U2 = U1 { type T <: A }
52+
53+
var x: T2 = _
54+
var y: U2 = _
55+
56+
val x1 = x
57+
val y1 = y
58+
59+
x = y
60+
y = x
61+
62+
}

0 commit comments

Comments
 (0)