Skip to content

Commit efb7b6e

Browse files
authored
Disallow opaque type aliases of context functions (#16041)
We could allow them but they would not do what one probably expects (i.e. create context closures). This is because abstract types upper-bounded by context functions don't do that either (see neg/i16035a.scala), and we have to keep semantic equivalence between the two. Therefore, it's better to disallow them. Fixes #16035
2 parents c704791 + 34ae62f commit efb7b6e

File tree

7 files changed

+50
-1
lines changed

7 files changed

+50
-1
lines changed

compiler/src/dotty/tools/dotc/transform/Erasure.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ object Erasure {
386386
case _: FunProto | AnyFunctionProto => tree
387387
case _ => tree.tpe.widen match
388388
case mt: MethodType if tree.isTerm =>
389-
assert(mt.paramInfos.isEmpty)//, i"bad adapt for $tree: $mt")
389+
assert(mt.paramInfos.isEmpty, i"bad adapt for $tree: $mt")
390390
adaptToType(tree.appliedToNone, pt)
391391
case tpw =>
392392
if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt)

compiler/src/dotty/tools/dotc/typer/Checking.scala

+10
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,15 @@ trait Checking {
14601460
|CanThrow capabilities can only be generated $req.""",
14611461
pat.srcPos)
14621462

1463+
/** Check that tree does not define a context function type */
1464+
def checkNoContextFunctionType(tree: Tree)(using Context): Unit =
1465+
def recur(tp: Type): Unit = tp.dealias match
1466+
case tp: HKTypeLambda => recur(tp.resType)
1467+
case tp if defn.isContextFunctionType(tp) =>
1468+
report.error(em"context function type cannot have opaque aliases", tree.srcPos)
1469+
case _ =>
1470+
recur(tree.tpe)
1471+
14631472
/** (1) Check that every named import selector refers to a type or value member of the
14641473
* qualifier type.
14651474
* (2) Check that no import selector is renamed more than once.
@@ -1495,6 +1504,7 @@ trait ReChecking extends Checking {
14951504
override def checkNoModuleClash(sym: Symbol)(using Context) = ()
14961505
override def checkCanThrow(tp: Type, span: Span)(using Context): Tree = EmptyTree
14971506
override def checkCatch(pat: Tree, guard: Tree)(using Context): Unit = ()
1507+
override def checkNoContextFunctionType(tree: Tree)(using Context): Unit = ()
14981508
override def checkFeature(name: TermName, description: => String, featureUseSite: Symbol, pos: SrcPos)(using Context): Unit = ()
14991509
}
15001510

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

+1
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24202420
case rhs =>
24212421
typedType(rhs)
24222422
checkFullyAppliedType(rhs1)
2423+
if sym.isOpaqueAlias then checkNoContextFunctionType(rhs1)
24232424
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
24242425
}
24252426

docs/_docs/reference/other-new-features/opaques-details.md

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def id(x: o.T): o.T = x
5252
```
5353

5454
Opaque type aliases cannot be `private` and cannot be overridden in subclasses.
55+
Opaque type aliases cannot have a context function type as right-hand side.
5556

5657
## Type Parameters of Opaque Types
5758

tests/neg/i16035.scala

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Scope:
2+
opaque type Uses[A, B] = A ?=> B // error
3+
opaque type UsesAlt = [A, B] =>> A ?=> B // error
4+
5+
object Uses:
6+
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn
7+
8+
import Scope.*
9+
val uses =
10+
given Int = 1
11+
Uses[Int, String](i ?=> s"*$i*")
12+

tests/neg/i16035a.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait S:
2+
type Uses[A, B] <: A ?=> B
3+
object Uses:
4+
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn // error
5+
val uses1 =
6+
given Int = 1
7+
Uses[Int, String](i ?=> s"*$i*")
8+
9+
object I extends S:
10+
type Uses[A, B] = A ?=> B
11+
val uses2 =
12+
given Int = 1
13+
Uses[Int, String](i ?=> s"*$i*")
14+

tests/pos/i16035.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Scope:
2+
type Uses[A, B] = A ?=> B
3+
4+
object Uses:
5+
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn
6+
7+
import Scope.*
8+
val uses =
9+
given Int = 1
10+
Uses[Int, String](i ?=> s"*$i*")
11+

0 commit comments

Comments
 (0)