Skip to content

Commit ea7af90

Browse files
committed
Fix problem preserving & and | types in WithBounds annotations
1 parent 96180d2 commit ea7af90

File tree

4 files changed

+111
-16
lines changed

4 files changed

+111
-16
lines changed

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,12 @@ object Annotations {
181181
object WithBounds {
182182
def unapply(ann: Annotation)(implicit ctx: Context): Option[TypeBounds] =
183183
if (ann.symbol == defn.WithBoundsAnnot) {
184-
val AppliedType(_, lo :: hi :: Nil) = ann.tree.tpe
184+
// We need to extract the type of the type tree in the New itself.
185+
// The annotation's type has been simplified as the type of an expression,
186+
// which means that `&` or `|` might have been lost.
187+
// Test in pos/reference/opaque.scala
188+
val Apply(TypeApply(Select(New(tpt), nme.CONSTRUCTOR), _), Nil) = ann.tree
189+
val AppliedType(_, lo :: hi :: Nil) = tpt.tpe
185190
Some(TypeBounds(lo, hi))
186191
}
187192
else None

tests/neg/opaque-bounds.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,38 @@ class Test { // error: class Test cannot be instantiated
1111
val f: Flag = Flag.make("hello")
1212
val g: FlagSet = f
1313

14+
}
15+
16+
object Access {
17+
18+
opaque type Permissions = Int
19+
opaque type PermissionChoice = Int
20+
opaque type Permission <: Permissions & PermissionChoice = Int
21+
22+
def (x: Permissions) & (y: Permissions): Permissions = x & y
23+
def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y
24+
def (x: Permissions) is (y: Permissions) = (x & y) == y
25+
def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0
26+
27+
val NoPermission: Permission = 0
28+
val ReadOnly: Permission = 1
29+
val WriteOnly: Permission = 2
30+
val ReadWrite: Permissions = ReadOnly & WriteOnly
31+
val ReadOrWrite: PermissionChoice = ReadOnly | WriteOnly
32+
}
33+
34+
object User {
35+
import Access._
36+
37+
case class Item(rights: Permissions)
38+
39+
val p1: Permissions = ReadOrWrite // error
40+
val p2: PermissionChoice = ReadWrite // error
41+
42+
val x = Item(ReadOnly)
43+
44+
assert( x.rights.is(ReadWrite) == false )
45+
assert( x.rights.isOneOf(ReadOrWrite) == true )
46+
47+
assert( x.rights.isOneOf(ReadWrite) == true ) // error: found Permissions, required: PermissionChoice
1448
}

tests/pos/opaque-bounds.scala

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/pos/reference/opaque.scala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
object Logarithms {
2+
3+
opaque type Logarithm = Double
4+
5+
object Logarithm {
6+
7+
// These are the ways to lift to the logarithm type
8+
def apply(d: Double): Logarithm = math.log(d)
9+
10+
def safe(d: Double): Option[Logarithm] =
11+
if (d > 0.0) Some(math.log(d)) else None
12+
}
13+
14+
// Extension methods define opaque types' public APIs
15+
implied LogarithmOps {
16+
def (x: Logarithm) toDouble: Double = math.exp(x)
17+
def (x: Logarithm) + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
18+
def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y)
19+
}
20+
}
21+
22+
object Test {
23+
import Logarithms._
24+
import Predef.{any2stringadd => _, _}
25+
26+
val l = Logarithm(1.0)
27+
val l2 = Logarithm(2.0)
28+
val l3 = l * l2
29+
val l4 = l + l2
30+
}
31+
32+
33+
object Access {
34+
35+
opaque type Permissions = Int
36+
opaque type PermissionChoice = Int
37+
opaque type Permission <: Permissions & PermissionChoice = Int
38+
39+
def (x: Permissions) & (y: Permissions): Permissions = x & y
40+
def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y
41+
def (x: Permissions) is (y: Permissions) = (x & y) == y
42+
def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0
43+
44+
val NoPermission: Permission = 0
45+
val ReadOnly: Permission = 1
46+
val WriteOnly: Permission = 2
47+
val ReadWrite: Permissions = ReadOnly & WriteOnly
48+
val ReadOrWrite: PermissionChoice = ReadOnly | WriteOnly
49+
}
50+
51+
object User {
52+
import Access._
53+
54+
case class Item(rights: Permissions)
55+
56+
val x = Item(ReadOnly) // OK, since Permission <: Permissions
57+
58+
assert( x.rights.is(ReadWrite) == false )
59+
assert( x.rights.isOneOf(ReadOrWrite) == true )
60+
61+
// Would be a type error:
62+
// assert( x.rights.isOneOf(ReadWrite) == true )
63+
64+
}
65+
66+
object o {
67+
opaque type T = Int
68+
val x: Int = id(1)
69+
val y: Int = identity(1)
70+
}
71+
def id(x: o.T): o.T = x

0 commit comments

Comments
 (0)