Skip to content

Commit

Permalink
Disallow opaque type aliases of context functions
Browse files Browse the repository at this point in the history
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 scala#16035
  • Loading branch information
odersky authored and mpollmeier committed Oct 16, 2022
1 parent 1e8a4c8 commit c561dfd
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 1 deletion.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ object Erasure {
case _: FunProto | AnyFunctionProto => tree
case _ => tree.tpe.widen match
case mt: MethodType if tree.isTerm =>
assert(mt.paramInfos.isEmpty)//, i"bad adapt for $tree: $mt")
assert(mt.paramInfos.isEmpty, i"bad adapt for $tree: $mt")
adaptToType(tree.appliedToNone, pt)
case tpw =>
if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt)
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,15 @@ trait Checking {
|CanThrow capabilities can only be generated $req.""",
pat.srcPos)

/** Check that tree does not define a context fucntion type */
def checkNoContextFunctionType(tree: Tree)(using Context): Unit =
def recur(tp: Type): Unit = tp.dealias match
case tp: HKTypeLambda => recur(tp.resType)
case tp if defn.isContextFunctionType(tp) =>
report.error(em"context functon type cannot have opaque aliases", tree.srcPos)
case _ =>
recur(tree.tpe)

/** (1) Check that every named import selector refers to a type or value member of the
* qualifier type.
* (2) Check that no import selector is renamed more than once.
Expand Down Expand Up @@ -1495,6 +1504,7 @@ trait ReChecking extends Checking {
override def checkNoModuleClash(sym: Symbol)(using Context) = ()
override def checkCanThrow(tp: Type, span: Span)(using Context): Tree = EmptyTree
override def checkCatch(pat: Tree, guard: Tree)(using Context): Unit = ()
override def checkNoContextFunctionType(tree: Tree)(using Context): Unit = ()
override def checkFeature(name: TermName, description: => String, featureUseSite: Symbol, pos: SrcPos)(using Context): Unit = ()
}

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case rhs =>
typedType(rhs)
checkFullyAppliedType(rhs1)
if sym.isOpaqueAlias then checkNoContextFunctionType(rhs1)
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
}

Expand Down
1 change: 1 addition & 0 deletions docs/_docs/reference/other-new-features/opaques-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def id(x: o.T): o.T = x
```

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

## Type Parameters of Opaque Types

Expand Down
12 changes: 12 additions & 0 deletions tests/neg/i16035.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object Scope:
opaque type Uses[A, B] = A ?=> B // error
opaque type UsesAlt = [A, B] =>> A ?=> B // error

object Uses:
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn

import Scope.*
val uses =
given Int = 1
Uses[Int, String](i ?=> s"*$i*")

14 changes: 14 additions & 0 deletions tests/neg/i16035a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
trait S:
type Uses[A, B] <: A ?=> B
object Uses:
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn // error
val uses1 =
given Int = 1
Uses[Int, String](i ?=> s"*$i*")

object I extends S:
type Uses[A, B] = A ?=> B
val uses2 =
given Int = 1
Uses[Int, String](i ?=> s"*$i*")

11 changes: 11 additions & 0 deletions tests/pos/i16035.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object Scope:
type Uses[A, B] = A ?=> B

object Uses:
def apply[A, B](fn: A ?=> B): Uses[A, B] = fn

import Scope.*
val uses =
given Int = 1
Uses[Int, String](i ?=> s"*$i*")

0 comments on commit c561dfd

Please sign in to comment.