Skip to content

Pick right type parameters for opaqueToBounds in TreeUnpickler #13206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion community-build/community-projects/onnx-scala
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]` */
Expand Down
22 changes: 16 additions & 6 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -860,14 +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
}
sym.info = sym.opaqueToBounds(
checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false),
rhs, rhs.tpe.typeParams)
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)
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
*/
Expand Down
11 changes: 7 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions sbt-test/opaques/i12927/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scalaVersion := sys.props("plugin.scalaVersion")
7 changes: 7 additions & 0 deletions sbt-test/opaques/i12927/changes/Foo.scala
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions sbt-test/opaques/i12927/src/main/scala/Bar.scala
Original file line number Diff line number Diff line change
@@ -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]
7 changes: 7 additions & 0 deletions sbt-test/opaques/i12927/src/main/scala/Foo.scala
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions sbt-test/opaques/i12927/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> compile
$ copy-file changes/Foo.scala src/main/scala/Foo.scala
> compile
10 changes: 10 additions & 0 deletions tests/pos/i12945/A_1.scala
Original file line number Diff line number Diff line change
@@ -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]
3 changes: 3 additions & 0 deletions tests/pos/i12945/B_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Test:
val x = summon[Lie.TC[Lie[7]]]
val fails = summon[x.Out =:= 7]
27 changes: 27 additions & 0 deletions tests/pos/i12950/repro_1.scala
Original file line number Diff line number Diff line change
@@ -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 }
6 changes: 6 additions & 0 deletions tests/pos/i12950/test_2.scala
Original file line number Diff line number Diff line change
@@ -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]
31 changes: 31 additions & 0 deletions tests/pos/i13001/Main_1.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
4 changes: 4 additions & 0 deletions tests/pos/i13001/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Test:
import ArbitraryDerivation.given
private def test[A: Arbitrary]: Unit = {}
test[Foo]
1 change: 1 addition & 0 deletions tests/pos/i13128/A_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opaque type Foo[T <: Int] = Int
3 changes: 3 additions & 0 deletions tests/pos/i13128/B_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def grabT[T <: Int](arg : Foo[T]) : T = ???
final val t1 = grabT(??? : Foo[8])
val t2 : 8 = t1
3 changes: 3 additions & 0 deletions tests/pos/i13190/A_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Opaque {
opaque type FieldType[K, +V] <: V = V
}
15 changes: 15 additions & 0 deletions tests/pos/i13190/B_2.scala
Original file line number Diff line number Diff line change
@@ -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]
}