From c561dfdff16c6aacfa3e294caf029b2d5c160e93 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 14 Sep 2022 17:29:13 +0200 Subject: [PATCH] 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 --- .../src/dotty/tools/dotc/transform/Erasure.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 10 ++++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 1 + .../other-new-features/opaques-details.md | 1 + tests/neg/i16035.scala | 12 ++++++++++++ tests/neg/i16035a.scala | 14 ++++++++++++++ tests/pos/i16035.scala | 11 +++++++++++ 7 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i16035.scala create mode 100644 tests/neg/i16035a.scala create mode 100644 tests/pos/i16035.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 9b8ba4504eda..84005424e3ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index a89fa91dc6a6..ddba7e13c4cd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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. @@ -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 = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 765dde1f697e..17271618cf09 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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) } diff --git a/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index 4b126fdd97f2..d7305a249089 100644 --- a/docs/_docs/reference/other-new-features/opaques-details.md +++ b/docs/_docs/reference/other-new-features/opaques-details.md @@ -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 diff --git a/tests/neg/i16035.scala b/tests/neg/i16035.scala new file mode 100644 index 000000000000..6cf53df7abca --- /dev/null +++ b/tests/neg/i16035.scala @@ -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*") + diff --git a/tests/neg/i16035a.scala b/tests/neg/i16035a.scala new file mode 100644 index 000000000000..6d003dadcbe9 --- /dev/null +++ b/tests/neg/i16035a.scala @@ -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*") + diff --git a/tests/pos/i16035.scala b/tests/pos/i16035.scala new file mode 100644 index 000000000000..2ff3c96a3a4f --- /dev/null +++ b/tests/pos/i16035.scala @@ -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*") +