From 336ec1360271bf21e61c29f5376718f430028bd2 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 3c6df355873d99d19da80319c91082c48bb4b280 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 df2c195b9e74..3e72f224cab0 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 714f8471d53604fe903db6bc64ae54c7f0d6cc31 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 09b06ef83d292af25c39fc8744f0447bd09b5505 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 882b141eff7f627efa571e227e8f7cd3ee2e65a9 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 41db7d88ffdd..0b13fe0dbb3a 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 eadee84600faf7d3ae52a9b68e9856d3ac4a69b3 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 510aefc8051f94acc35fe2104346d3eca93c1ccf 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 565c5aa86a4071914b1c30018b3a40551a5151d2 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 f89ccf445b72b4830fa06fb729e963d36fffaf2b 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 6c2df3afad5b69242e1b2b4d52131cb724e02150 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