From 169828d439133badfd0a8d7b6672195ba73fa349 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Jul 2021 14:36:57 +0200 Subject: [PATCH 01/10] Pick right type parameters in TreeUnpickler Fixes #13190 --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 5 ++++- tests/pos/i13190/A_1.scala | 3 +++ tests/pos/i13190/B_2.scala | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i13190/A_1.scala create mode 100644 tests/pos/i13190/B_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index f7b56c246b8c..fa70dd469b6a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -864,9 +864,12 @@ class TreeUnpickler(reader: TastyReader, override def completerTypeParams(sym: Symbol)(using Context) = rhs.tpe.typeParams } + val tparamSyms = rhs match + case LambdaTypeTree(tparams, body) => tparams.map(_.symbol.asType) + case _ => Nil sym.info = sym.opaqueToBounds( checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false), - rhs, rhs.tpe.typeParams) + rhs, tparamSyms) if sym.isOpaqueAlias then sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on sym.resetFlag(Provisional) TypeDef(rhs) diff --git a/tests/pos/i13190/A_1.scala b/tests/pos/i13190/A_1.scala new file mode 100644 index 000000000000..9bb9b20f2976 --- /dev/null +++ b/tests/pos/i13190/A_1.scala @@ -0,0 +1,3 @@ +object Opaque { + opaque type FieldType[K, +V] <: V = V +} \ No newline at end of file diff --git a/tests/pos/i13190/B_2.scala b/tests/pos/i13190/B_2.scala new file mode 100644 index 000000000000..2752778afa04 --- /dev/null +++ b/tests/pos/i13190/B_2.scala @@ -0,0 +1,15 @@ +import Opaque.* + +object Test { + type FindField[R <: scala.Tuple, K] = R match { + case FieldType[K, f] *: t => f + case _ *: t => FindField[t, K] + } + + val f: FieldType["A", Int] = ??? + val f1: Int = f + //val f2: Int = f + + type R = FieldType["A", Int] *: FieldType["B", Double] *: FieldType["C", String] *: FieldType["D", Boolean] *: EmptyTuple + summon[FindField[R, "B"] =:= Double] +} From 94ac8117d617f57a6d3d1ec269b5f9cb731be626 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Jul 2021 14:40:02 +0200 Subject: [PATCH 02/10] Strengthen argument type of opaqueToBounds --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 112bd182b1ad..f4cad10d3c4e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -407,7 +407,7 @@ object SymDenotations { * @param tparams The type parameters with which the right-hand side bounds should be abstracted * */ - def opaqueToBounds(info: Type, rhs: tpd.Tree, tparams: List[TypeParamInfo])(using Context): Type = + def opaqueToBounds(info: Type, rhs: tpd.Tree, tparams: List[TypeSymbol])(using Context): Type = def setAlias(tp: Type) = def recur(self: Type): Unit = self match From e48f286282ad03a3f71341a5cbc6ece706ba35db Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Jul 2021 14:59:19 +0200 Subject: [PATCH 03/10] Test for #12945 --- tests/pos/i12945/A_1.scala | 10 ++++++++++ tests/pos/i12945/B_2.scala | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 tests/pos/i12945/A_1.scala create mode 100644 tests/pos/i12945/B_2.scala diff --git a/tests/pos/i12945/A_1.scala b/tests/pos/i12945/A_1.scala new file mode 100644 index 000000000000..890171a63051 --- /dev/null +++ b/tests/pos/i12945/A_1.scala @@ -0,0 +1,10 @@ +opaque type Lie[W <: Int] = Int +object Lie: + trait TC[-T]: + type Out + object TC: + given [W <: Int]: TC[Lie[W]] with + type Out = W + +val x = summon[Lie.TC[Lie[7]]] +val works = summon[x.Out =:= 7] diff --git a/tests/pos/i12945/B_2.scala b/tests/pos/i12945/B_2.scala new file mode 100644 index 000000000000..371754a87ff5 --- /dev/null +++ b/tests/pos/i12945/B_2.scala @@ -0,0 +1,3 @@ +object Test: + val x = summon[Lie.TC[Lie[7]]] + val fails = summon[x.Out =:= 7] From ee810b55dc1cfe3b997eaf9861aced82829896d8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Jul 2021 15:01:51 +0200 Subject: [PATCH 04/10] Test for #13001 --- tests/pos/i13001/Main_1.scala | 31 +++++++++++++++++++++++++++++++ tests/pos/i13001/Test_2.scala | 4 ++++ 2 files changed, 35 insertions(+) create mode 100644 tests/pos/i13001/Main_1.scala create mode 100644 tests/pos/i13001/Test_2.scala diff --git a/tests/pos/i13001/Main_1.scala b/tests/pos/i13001/Main_1.scala new file mode 100644 index 000000000000..ad46df9f2fe1 --- /dev/null +++ b/tests/pos/i13001/Main_1.scala @@ -0,0 +1,31 @@ +case class Foo(a: String) + +trait Arbitrary[T] +trait Gen[+T] + +object ArbitraryDerivation: + given deriveArb[A](using gen: DerivedGen[A]): Arbitrary[A] = ??? + +opaque type DerivedGen[A] = Gen[A] +object DerivedGen extends DerivedGenInstances + +sealed abstract class DerivedGenInstances: + inline given derived[A](using gen: K0.Generic[A]): DerivedGen[A] = + val dummy: DerivedGen[A] = ??? + gen.derive(dummy, dummy) + +// from shapeless3-deriving +import scala.deriving.* +object K0 { + type Kind[C, O] = C { type Kind = K0.type ; type MirroredType = O ; type MirroredElemTypes <: Tuple } + type Generic[O] = Kind[Mirror, O] + type ProductGeneric[O] = Kind[Mirror.Product, O] + type CoproductGeneric[O] = Kind[Mirror.Sum, O] + + extension [F[_], T](gen: Generic[T]) + inline def derive(f: => (ProductGeneric[T] & gen.type) ?=> F[T], g: => (CoproductGeneric[T] & gen.type) ?=> F[T]): F[T] = + inline gen match { + case p: ProductGeneric[T] => f(using p.asInstanceOf) + case c: CoproductGeneric[T] => g(using c.asInstanceOf) + } +} \ No newline at end of file diff --git a/tests/pos/i13001/Test_2.scala b/tests/pos/i13001/Test_2.scala new file mode 100644 index 000000000000..42cacb760027 --- /dev/null +++ b/tests/pos/i13001/Test_2.scala @@ -0,0 +1,4 @@ +class Test: + import ArbitraryDerivation.given + private def test[A: Arbitrary]: Unit = {} + test[Foo] \ No newline at end of file From b74b56c7692b52bcbc896ee3b7876c67b06ba357 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Jul 2021 17:33:51 +0200 Subject: [PATCH 05/10] Pretty-print LambdaParams Customized printer instead of falling back to toString --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 +++- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 6 ++++++ compiler/src/dotty/tools/dotc/printing/Printer.scala | 6 +++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b56b2d56f875..fde34d6fde83 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4039,7 +4039,7 @@ object Types { // ----- Type application: LambdaParam, AppliedType --------------------- /** The parameter of a type lambda */ - case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo { + case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo, printing.Showable { type ThisName = TypeName def isTypeParam(using Context): Boolean = tl.paramNames.head.isTypeName @@ -4084,6 +4084,8 @@ object Types { case _ => myVariance = Invariant myVariance + + def toText(printer: Printer): Text = printer.toText(this) } /** A type application `C[T_1, ..., T_n]` */ diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 69df16da08c3..7009a55d3aeb 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -540,6 +540,12 @@ class PlainPrinter(_ctx: Context) extends Printer { def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now + def toText(param: LambdaParam): Text = + varianceSign(param.paramVariance) + ~ toText(param.paramName) + ~ (if param.isTypeParam then "" else ": ") + ~ toText(param.paramInfo) + protected def escapedString(str: String): String = str flatMap escapedChar def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep) diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 8584c889eeda..2079bc7cc1cd 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -4,7 +4,8 @@ package printing import core._ import Texts._, ast.Trees._ -import Types.{Type, SingletonType}, Symbols.Symbol, Scopes.Scope, Constants.Constant, +import Types.{Type, SingletonType, LambdaParam}, + Symbols.Symbol, Scopes.Scope, Constants.Constant, Names.Name, Denotations._, Annotations.Annotation import typer.Implicits.SearchResult import util.SourcePosition @@ -130,6 +131,9 @@ abstract class Printer { /** Textual representation of type */ def toText(tp: Type): Text + /** Textual representation of lambda param */ + def toText(tree: LambdaParam): Text + /** Textual representation of all symbols in given list, * using `dclText` for displaying each. */ From 459de9167ad9de60ebf71542c844c5ca167c9115 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Jul 2021 18:36:21 +0200 Subject: [PATCH 06/10] Refactorings for clarity and to avoid unnecessary computation --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 25 ++++++++++++------- .../src/dotty/tools/dotc/typer/Namer.scala | 11 +++++--- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index fa70dd469b6a..2853052eb0b7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -860,17 +860,24 @@ class TreeUnpickler(reader: TastyReader, sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see i3816.scala sym.setFlag(Provisional) val rhs = readTpt()(using localCtx) - sym.info = new NoCompleter { + + sym.info = new NoCompleter: override def completerTypeParams(sym: Symbol)(using Context) = rhs.tpe.typeParams - } - val tparamSyms = rhs match - case LambdaTypeTree(tparams, body) => tparams.map(_.symbol.asType) - case _ => Nil - sym.info = sym.opaqueToBounds( - checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false), - rhs, tparamSyms) - if sym.isOpaqueAlias then sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on + + def opaqueToBounds(info: Type): Type = + val tparamSyms = rhs match + case LambdaTypeTree(tparams, body) => tparams.map(_.symbol.asType) + case _ => Nil + sym.opaqueToBounds(info, rhs, tparamSyms) + + val info = checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false) + if sym.isOpaqueAlias then + sym.info = opaqueToBounds(info) + sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on + else + sym.info = info + sym.resetFlag(Provisional) TypeDef(rhs) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index d2264e367f36..b4bc79cd1f60 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -425,7 +425,7 @@ class Namer { typer: Typer => * is still missing its parents. Parents are set to Nil when completion starts and are * set to the actual parents later. If a superclass completes a subclass in one * of its parents, the parents of the superclass or some intervening class might - * not yet be set. This situation can be detected by asking for the baseType of Any - + * not yet be set. This situation can be detected by asking for the baseType of Any - * if that type does not exist, one of the base classes of this class misses its parents. * If this situation arises, the computation of the superclass might be imprecise. * For instance, in i12722.scala, the superclass of `IPersonalCoinOps` is computed @@ -988,9 +988,12 @@ class Namer { typer: Typer => val unsafeInfo = if (isDerived) rhsBodyType else abstracted(rhsBodyType) def opaqueToBounds(info: Type): Type = - if sym.isOpaqueAlias && info.typeParams.nonEmpty && info.hkResult.typeParams.nonEmpty then - report.error(em"opaque type alias cannot have multiple type parameter lists", rhs.srcPos) - sym.opaqueToBounds(info, rhs1, tparamSyms) + if sym.isOpaqueAlias then + if info.typeParams.nonEmpty && info.hkResult.typeParams.nonEmpty then + report.error(em"opaque type alias cannot have multiple type parameter lists", rhs.srcPos) + sym.opaqueToBounds(info, rhs1, tparamSyms) + else + info if (isDerived) sym.info = unsafeInfo else { From 5a322fa3b341832ac77fa6d6ff180efcfda3376e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Jul 2021 18:41:00 +0200 Subject: [PATCH 07/10] Fix definition of Tensor in onnx-Scala Type parameter Ax needs to be declared explicitly as covariant since Tensor is opaque. This fell through the cracks before since under separate compilation type parameters of opaque types got assigned structural variances. But this is incorrect. Under joint compilation there would be an error. --- community-build/community-projects/onnx-scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/onnx-scala b/community-build/community-projects/onnx-scala index 87600b3d4234..3a5a45016d1a 160000 --- a/community-build/community-projects/onnx-scala +++ b/community-build/community-projects/onnx-scala @@ -1 +1 @@ -Subproject commit 87600b3d4234517d44e1549de5d8015afc8801fb +Subproject commit 3a5a45016d1a48d2a84dc3159d3e08c1ad5ac587 From 1af63510294a460a3bca4e7f460cad094a97dfe1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Jul 2021 18:46:03 +0200 Subject: [PATCH 08/10] Test case for #12950 --- tests/pos/i12950/repro_1.scala | 27 +++++++++++++++++++++++++++ tests/pos/i12950/test_2.scala | 6 ++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/pos/i12950/repro_1.scala create mode 100644 tests/pos/i12950/test_2.scala diff --git a/tests/pos/i12950/repro_1.scala b/tests/pos/i12950/repro_1.scala new file mode 100644 index 000000000000..4c4a12de8cd6 --- /dev/null +++ b/tests/pos/i12950/repro_1.scala @@ -0,0 +1,27 @@ +package repro +object repro: + object opq: + opaque type Lift[T] = Int + extension(v: Int) + def lift[T]: Lift[T] = v + extension[T](l: Lift[T]) + def value: Int = l + + export opq.Lift as Lift + export opq.lift as lift + + final type Two + + extension[TL](l: Lift[TL]) + def repro[TR](using m: Mul[TL, TR]): Int = l.value + m.value + + abstract class Mul[TL, TR]: + val value: Int + + transparent inline given mulGivenInt[TL <: Int & Singleton, TR <: Int & Singleton]: Mul[TL, TR] = + val m: Int = scala.compiletime.constValue[TL] * scala.compiletime.constValue[TR] + new Mul[TL, TR] { val value: Int = m } + + transparent inline given mulGivenTwo[TR <: Int & Singleton]: Mul[Two, TR] = + val m: Int = 2 * scala.compiletime.constValue[TR] + new Mul[Two, TR] { val value: Int = m } \ No newline at end of file diff --git a/tests/pos/i12950/test_2.scala b/tests/pos/i12950/test_2.scala new file mode 100644 index 000000000000..2640501f4388 --- /dev/null +++ b/tests/pos/i12950/test_2.scala @@ -0,0 +1,6 @@ +import repro.repro.{*, given} + +val x = 1.lift[Two] +val _ = x.repro[2] +val y = 1.lift[2] +val _ = y.repro[2] From 84c2b11ea2a08d65486306990b4d1b853dd40621 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Jul 2021 18:49:41 +0200 Subject: [PATCH 09/10] Test case for #13128 --- tests/pos/i13128/A_1.scala | 1 + tests/pos/i13128/B_2.scala | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/pos/i13128/A_1.scala create mode 100644 tests/pos/i13128/B_2.scala diff --git a/tests/pos/i13128/A_1.scala b/tests/pos/i13128/A_1.scala new file mode 100644 index 000000000000..4a1c9335d2ec --- /dev/null +++ b/tests/pos/i13128/A_1.scala @@ -0,0 +1 @@ +opaque type Foo[T <: Int] = Int diff --git a/tests/pos/i13128/B_2.scala b/tests/pos/i13128/B_2.scala new file mode 100644 index 000000000000..797c917d3620 --- /dev/null +++ b/tests/pos/i13128/B_2.scala @@ -0,0 +1,3 @@ +def grabT[T <: Int](arg : Foo[T]) : T = ??? +final val t1 = grabT(??? : Foo[8]) +val t2 : 8 = t1 \ No newline at end of file From bbcc700e1cd8977bdf2ff8463485ed26eda2224d Mon Sep 17 00:00:00 2001 From: Tom Grigg Date: Fri, 30 Jul 2021 10:11:53 -0700 Subject: [PATCH 10/10] Test case for #12927 --- sbt-test/opaques/i12927/build.sbt | 1 + sbt-test/opaques/i12927/changes/Foo.scala | 7 +++++++ sbt-test/opaques/i12927/src/main/scala/Bar.scala | 4 ++++ sbt-test/opaques/i12927/src/main/scala/Foo.scala | 7 +++++++ sbt-test/opaques/i12927/test | 3 +++ 5 files changed, 22 insertions(+) create mode 100644 sbt-test/opaques/i12927/build.sbt create mode 100644 sbt-test/opaques/i12927/changes/Foo.scala create mode 100644 sbt-test/opaques/i12927/src/main/scala/Bar.scala create mode 100644 sbt-test/opaques/i12927/src/main/scala/Foo.scala create mode 100644 sbt-test/opaques/i12927/test diff --git a/sbt-test/opaques/i12927/build.sbt b/sbt-test/opaques/i12927/build.sbt new file mode 100644 index 000000000000..63e314982c41 --- /dev/null +++ b/sbt-test/opaques/i12927/build.sbt @@ -0,0 +1 @@ +scalaVersion := sys.props("plugin.scalaVersion") diff --git a/sbt-test/opaques/i12927/changes/Foo.scala b/sbt-test/opaques/i12927/changes/Foo.scala new file mode 100644 index 000000000000..5c4cbfafede5 --- /dev/null +++ b/sbt-test/opaques/i12927/changes/Foo.scala @@ -0,0 +1,7 @@ +object Foo: + opaque type BlaBla[+T, D] = Int + extension [T, D](token: BlaBla[T, D]) def data: D = ??? + +//To cause the crash, after initial clean compilation +//replace `???` with `value.data` to cause the compiler crash +def foo[W <: Int](value: Bar.BlaBla[W]): Unit = value.data diff --git a/sbt-test/opaques/i12927/src/main/scala/Bar.scala b/sbt-test/opaques/i12927/src/main/scala/Bar.scala new file mode 100644 index 000000000000..5688944e6e3e --- /dev/null +++ b/sbt-test/opaques/i12927/src/main/scala/Bar.scala @@ -0,0 +1,4 @@ +object Bar: + type Fuzzy[W <: Int] = Int + opaque type BlaBla[W <: Int] <: Foo.BlaBla[Fuzzy[W], Int] = + Foo.BlaBla[Fuzzy[W], Int] diff --git a/sbt-test/opaques/i12927/src/main/scala/Foo.scala b/sbt-test/opaques/i12927/src/main/scala/Foo.scala new file mode 100644 index 000000000000..24ab85caa49a --- /dev/null +++ b/sbt-test/opaques/i12927/src/main/scala/Foo.scala @@ -0,0 +1,7 @@ +object Foo: + opaque type BlaBla[+T, D] = Int + extension [T, D](token: BlaBla[T, D]) def data: D = ??? + +//To cause the crash, after initial clean compilation +//replace `???` with `value.data` to cause the compiler crash +def foo[W <: Int](value: Bar.BlaBla[W]): Unit = ??? //value.data diff --git a/sbt-test/opaques/i12927/test b/sbt-test/opaques/i12927/test new file mode 100644 index 000000000000..c0ed101b5606 --- /dev/null +++ b/sbt-test/opaques/i12927/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/Foo.scala src/main/scala/Foo.scala +> compile