Skip to content

Commit f9aff3e

Browse files
committed
Allow toplevel opaque types
1 parent 560d608 commit f9aff3e

File tree

7 files changed

+72
-3
lines changed

7 files changed

+72
-3
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,13 +1022,23 @@ object desugar {
10221022
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts)
10231023
}
10241024

1025-
/** Group all patterm, value and method definitions and all non-class type definitions
1026-
* in an object named `<source>#object` where `<source>` is the name of the source file.
1025+
/** Group all definitions that can't be at the toplebel in
1026+
* an object named `<source>#object` where `<source>` is the name of the source file.
1027+
* Definitions that can't be at the toplevel are:
1028+
*
1029+
* - all pattern, value and method definitions
1030+
* - non-class type definitions
1031+
* - implicit classes and objects
1032+
* - companion objects of opaque types
10271033
*/
10281034
def packageDef(pdef: PackageDef)(implicit ctx: Context): PackageDef = {
1035+
val opaqueNames = pdef.stats.collect {
1036+
case stat: TypeDef if stat.mods.is(Opaque) => stat.name
1037+
}
10291038
def needsObject(stat: Tree) = stat match {
10301039
case _: ValDef | _: PatDef | _: DefDef => true
1031-
case stat: ModuleDef => stat.mods.is(Implicit)
1040+
case stat: ModuleDef =>
1041+
stat.mods.is(Implicit) || opaqueNames.contains(stat.name.stripModuleClassSuffix.toTypeName)
10321042
case stat: TypeDef => !stat.isClassDef || stat.mods.is(Implicit)
10331043
case _ => false
10341044
}

tests/neg/toplevel-opaque/Test.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
val x: T[Int] = 2 // error
3+
val y: Int = x // error
4+
val a: Int = T.f(x) // ok
5+
val b: T[Int] = T.g(y) // ok
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
opaque type T[X] = X
2+
object T {
3+
def f(x: T[Int]): Int = x // OK
4+
def g(x: Int): T[Int] = x // OK
5+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package logs
2+
3+
opaque type Logarithm = Double
4+
5+
implicit 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+
// This is the first way to unlift the logarithm type
14+
def exponent(l: Logarithm): Double = l
15+
16+
// Extension methods define opaque types' public APIs
17+
18+
// This is the second way to unlift the logarithm type
19+
def (x: Logarithm) toDouble: Double = math.exp(x)
20+
def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y))
21+
def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y)
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package logs
2+
3+
import Predef.{any2stringadd => _, _}
4+
5+
object Test {
6+
val l = Logarithm(1.0)
7+
val l2 = Logarithm(2.0)
8+
val l3 = l * l2
9+
val l4 = l + l2 // currently requires any2stringadd to be disabled because
10+
// as a contextual implicit this takes precedence over the
11+
// implicit scope implicit LogarithmOps.
12+
// TODO: Remove any2stringadd
13+
val d = Logarithm.toDouble(l3)
14+
val l5: Logarithm = (1.0).asInstanceOf[Logarithm]
15+
}

tests/pos/toplevel-opaque/Test.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
val x: T[Int] = ??? //2 // error
3+
val y: Int = 1 // x // error
4+
val a: Int = T.f(x) // ok
5+
val b: T[Int] = T.g(y) // ok
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
opaque type T[X] = X
2+
object T {
3+
def f(x: T[Int]): Int = x // OK
4+
def g(x: Int): T[Int] = x // OK
5+
}

0 commit comments

Comments
 (0)