Skip to content

Commit a58e89a

Browse files
committed
Disallow opaque type aliases of context functions
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
1 parent a503b7a commit a58e89a

File tree

7 files changed

+50
-1
lines changed

7 files changed

+50
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 10 additions & 0 deletions
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 fucntion 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 functon 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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,6 +2417,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24172417
case rhs =>
24182418
typedType(rhs)
24192419
checkFullyAppliedType(rhs1)
2420+
if sym.isOpaqueAlias then checkNoContextFunctionType(rhs1)
24202421
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
24212422
}
24222423

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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

Lines changed: 11 additions & 0 deletions
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)