Skip to content

Commit 7a840f5

Browse files
authored
Merge pull request #13219 from dotty-staging/fix-13190-backport
[backport] Fix separate compilation issues with opaque types
2 parents 3e099c3 + bbcc700 commit 7a840f5

File tree

22 files changed

+164
-14
lines changed

22 files changed

+164
-14
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ object SymDenotations {
407407
* @param tparams The type parameters with which the right-hand side bounds should be abstracted
408408
*
409409
*/
410-
def opaqueToBounds(info: Type, rhs: tpd.Tree, tparams: List[TypeParamInfo])(using Context): Type =
410+
def opaqueToBounds(info: Type, rhs: tpd.Tree, tparams: List[TypeSymbol])(using Context): Type =
411411

412412
def setAlias(tp: Type) =
413413
def recur(self: Type): Unit = self match

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4039,7 +4039,7 @@ object Types {
40394039
// ----- Type application: LambdaParam, AppliedType ---------------------
40404040

40414041
/** The parameter of a type lambda */
4042-
case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo {
4042+
case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo, printing.Showable {
40434043
type ThisName = TypeName
40444044

40454045
def isTypeParam(using Context): Boolean = tl.paramNames.head.isTypeName
@@ -4084,6 +4084,8 @@ object Types {
40844084
case _ =>
40854085
myVariance = Invariant
40864086
myVariance
4087+
4088+
def toText(printer: Printer): Text = printer.toText(this)
40874089
}
40884090

40894091
/** A type application `C[T_1, ..., T_n]` */

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -860,14 +860,24 @@ class TreeUnpickler(reader: TastyReader,
860860
sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see i3816.scala
861861
sym.setFlag(Provisional)
862862
val rhs = readTpt()(using localCtx)
863-
sym.info = new NoCompleter {
863+
864+
sym.info = new NoCompleter:
864865
override def completerTypeParams(sym: Symbol)(using Context) =
865866
rhs.tpe.typeParams
866-
}
867-
sym.info = sym.opaqueToBounds(
868-
checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false),
869-
rhs, rhs.tpe.typeParams)
870-
if sym.isOpaqueAlias then sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on
867+
868+
def opaqueToBounds(info: Type): Type =
869+
val tparamSyms = rhs match
870+
case LambdaTypeTree(tparams, body) => tparams.map(_.symbol.asType)
871+
case _ => Nil
872+
sym.opaqueToBounds(info, rhs, tparamSyms)
873+
874+
val info = checkNonCyclic(sym, rhs.tpe.toBounds, reportErrors = false)
875+
if sym.isOpaqueAlias then
876+
sym.info = opaqueToBounds(info)
877+
sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on
878+
else
879+
sym.info = info
880+
871881
sym.resetFlag(Provisional)
872882
TypeDef(rhs)
873883
}

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,12 @@ class PlainPrinter(_ctx: Context) extends Printer {
540540

541541
def toText(annot: Annotation): Text = s"@${annot.symbol.name}" // for now
542542

543+
def toText(param: LambdaParam): Text =
544+
varianceSign(param.paramVariance)
545+
~ toText(param.paramName)
546+
~ (if param.isTypeParam then "" else ": ")
547+
~ toText(param.paramInfo)
548+
543549
protected def escapedString(str: String): String = str flatMap escapedChar
544550

545551
def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep)

compiler/src/dotty/tools/dotc/printing/Printer.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ package printing
44

55
import core._
66
import Texts._, ast.Trees._
7-
import Types.{Type, SingletonType}, Symbols.Symbol, Scopes.Scope, Constants.Constant,
7+
import Types.{Type, SingletonType, LambdaParam},
8+
Symbols.Symbol, Scopes.Scope, Constants.Constant,
89
Names.Name, Denotations._, Annotations.Annotation
910
import typer.Implicits.SearchResult
1011
import util.SourcePosition
@@ -130,6 +131,9 @@ abstract class Printer {
130131
/** Textual representation of type */
131132
def toText(tp: Type): Text
132133

134+
/** Textual representation of lambda param */
135+
def toText(tree: LambdaParam): Text
136+
133137
/** Textual representation of all symbols in given list,
134138
* using `dclText` for displaying each.
135139
*/

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ class Namer { typer: Typer =>
425425
* is still missing its parents. Parents are set to Nil when completion starts and are
426426
* set to the actual parents later. If a superclass completes a subclass in one
427427
* of its parents, the parents of the superclass or some intervening class might
428-
* not yet be set. This situation can be detected by asking for the baseType of Any -
428+
* not yet be set. This situation can be detected by asking for the baseType of Any -
429429
* if that type does not exist, one of the base classes of this class misses its parents.
430430
* If this situation arises, the computation of the superclass might be imprecise.
431431
* For instance, in i12722.scala, the superclass of `IPersonalCoinOps` is computed
@@ -988,9 +988,12 @@ class Namer { typer: Typer =>
988988
val unsafeInfo = if (isDerived) rhsBodyType else abstracted(rhsBodyType)
989989

990990
def opaqueToBounds(info: Type): Type =
991-
if sym.isOpaqueAlias && info.typeParams.nonEmpty && info.hkResult.typeParams.nonEmpty then
992-
report.error(em"opaque type alias cannot have multiple type parameter lists", rhs.srcPos)
993-
sym.opaqueToBounds(info, rhs1, tparamSyms)
991+
if sym.isOpaqueAlias then
992+
if info.typeParams.nonEmpty && info.hkResult.typeParams.nonEmpty then
993+
report.error(em"opaque type alias cannot have multiple type parameter lists", rhs.srcPos)
994+
sym.opaqueToBounds(info, rhs1, tparamSyms)
995+
else
996+
info
994997

995998
if (isDerived) sym.info = unsafeInfo
996999
else {

sbt-test/opaques/i12927/build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scalaVersion := sys.props("plugin.scalaVersion")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Foo:
2+
opaque type BlaBla[+T, D] = Int
3+
extension [T, D](token: BlaBla[T, D]) def data: D = ???
4+
5+
//To cause the crash, after initial clean compilation
6+
//replace `???` with `value.data` to cause the compiler crash
7+
def foo[W <: Int](value: Bar.BlaBla[W]): Unit = value.data
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Bar:
2+
type Fuzzy[W <: Int] = Int
3+
opaque type BlaBla[W <: Int] <: Foo.BlaBla[Fuzzy[W], Int] =
4+
Foo.BlaBla[Fuzzy[W], Int]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Foo:
2+
opaque type BlaBla[+T, D] = Int
3+
extension [T, D](token: BlaBla[T, D]) def data: D = ???
4+
5+
//To cause the crash, after initial clean compilation
6+
//replace `???` with `value.data` to cause the compiler crash
7+
def foo[W <: Int](value: Bar.BlaBla[W]): Unit = ??? //value.data

sbt-test/opaques/i12927/test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
> compile
2+
$ copy-file changes/Foo.scala src/main/scala/Foo.scala
3+
> compile

tests/pos/i12945/A_1.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
opaque type Lie[W <: Int] = Int
2+
object Lie:
3+
trait TC[-T]:
4+
type Out
5+
object TC:
6+
given [W <: Int]: TC[Lie[W]] with
7+
type Out = W
8+
9+
val x = summon[Lie.TC[Lie[7]]]
10+
val works = summon[x.Out =:= 7]

tests/pos/i12945/B_2.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test:
2+
val x = summon[Lie.TC[Lie[7]]]
3+
val fails = summon[x.Out =:= 7]

tests/pos/i12950/repro_1.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package repro
2+
object repro:
3+
object opq:
4+
opaque type Lift[T] = Int
5+
extension(v: Int)
6+
def lift[T]: Lift[T] = v
7+
extension[T](l: Lift[T])
8+
def value: Int = l
9+
10+
export opq.Lift as Lift
11+
export opq.lift as lift
12+
13+
final type Two
14+
15+
extension[TL](l: Lift[TL])
16+
def repro[TR](using m: Mul[TL, TR]): Int = l.value + m.value
17+
18+
abstract class Mul[TL, TR]:
19+
val value: Int
20+
21+
transparent inline given mulGivenInt[TL <: Int & Singleton, TR <: Int & Singleton]: Mul[TL, TR] =
22+
val m: Int = scala.compiletime.constValue[TL] * scala.compiletime.constValue[TR]
23+
new Mul[TL, TR] { val value: Int = m }
24+
25+
transparent inline given mulGivenTwo[TR <: Int & Singleton]: Mul[Two, TR] =
26+
val m: Int = 2 * scala.compiletime.constValue[TR]
27+
new Mul[Two, TR] { val value: Int = m }

tests/pos/i12950/test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import repro.repro.{*, given}
2+
3+
val x = 1.lift[Two]
4+
val _ = x.repro[2]
5+
val y = 1.lift[2]
6+
val _ = y.repro[2]

tests/pos/i13001/Main_1.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
case class Foo(a: String)
2+
3+
trait Arbitrary[T]
4+
trait Gen[+T]
5+
6+
object ArbitraryDerivation:
7+
given deriveArb[A](using gen: DerivedGen[A]): Arbitrary[A] = ???
8+
9+
opaque type DerivedGen[A] = Gen[A]
10+
object DerivedGen extends DerivedGenInstances
11+
12+
sealed abstract class DerivedGenInstances:
13+
inline given derived[A](using gen: K0.Generic[A]): DerivedGen[A] =
14+
val dummy: DerivedGen[A] = ???
15+
gen.derive(dummy, dummy)
16+
17+
// from shapeless3-deriving
18+
import scala.deriving.*
19+
object K0 {
20+
type Kind[C, O] = C { type Kind = K0.type ; type MirroredType = O ; type MirroredElemTypes <: Tuple }
21+
type Generic[O] = Kind[Mirror, O]
22+
type ProductGeneric[O] = Kind[Mirror.Product, O]
23+
type CoproductGeneric[O] = Kind[Mirror.Sum, O]
24+
25+
extension [F[_], T](gen: Generic[T])
26+
inline def derive(f: => (ProductGeneric[T] & gen.type) ?=> F[T], g: => (CoproductGeneric[T] & gen.type) ?=> F[T]): F[T] =
27+
inline gen match {
28+
case p: ProductGeneric[T] => f(using p.asInstanceOf)
29+
case c: CoproductGeneric[T] => g(using c.asInstanceOf)
30+
}
31+
}

tests/pos/i13001/Test_2.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Test:
2+
import ArbitraryDerivation.given
3+
private def test[A: Arbitrary]: Unit = {}
4+
test[Foo]

tests/pos/i13128/A_1.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
opaque type Foo[T <: Int] = Int

tests/pos/i13128/B_2.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def grabT[T <: Int](arg : Foo[T]) : T = ???
2+
final val t1 = grabT(??? : Foo[8])
3+
val t2 : 8 = t1

tests/pos/i13190/A_1.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Opaque {
2+
opaque type FieldType[K, +V] <: V = V
3+
}

tests/pos/i13190/B_2.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Opaque.*
2+
3+
object Test {
4+
type FindField[R <: scala.Tuple, K] = R match {
5+
case FieldType[K, f] *: t => f
6+
case _ *: t => FindField[t, K]
7+
}
8+
9+
val f: FieldType["A", Int] = ???
10+
val f1: Int = f
11+
//val f2: Int = f
12+
13+
type R = FieldType["A", Int] *: FieldType["B", Double] *: FieldType["C", String] *: FieldType["D", Boolean] *: EmptyTuple
14+
summon[FindField[R, "B"] =:= Double]
15+
}

0 commit comments

Comments
 (0)