Skip to content

Commit 7ab0d0e

Browse files
committed
Support Dotty 0.23.0-RC1; opaque type aliases are transparent
1 parent 666b98e commit 7ab0d0e

File tree

12 files changed

+127
-64
lines changed

12 files changed

+127
-64
lines changed

project/DottySupport.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import sbt.librarymanagement.{
1515
*/
1616
object DottySupport {
1717
val dottyVersion = "0.23.0-RC1"
18-
val currentDottyRelease = "0.22.0-RC1" // TASTy version 19
19-
val dottyLibrary = "ch.epfl.lamp" % "dotty-library_0.22" % currentDottyRelease
20-
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.22" % currentDottyRelease
18+
val currentDottyRelease = "0.23.0-RC1" // TASTy version 20
19+
val dottyLibrary = "ch.epfl.lamp" % "dotty-library_0.23" % currentDottyRelease
20+
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.23" % currentDottyRelease
2121
val compileWithDotty: Boolean =
2222
Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false)
2323
lazy val commonSettings = Seq(

src/compiler/scala/tools/nsc/tasty/TastyFlags.scala

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ object TastyFlags {
1818
final val TastyMacro: TastyFlagSet = NoInits.next
1919
final val Enum: TastyFlagSet = TastyMacro.next
2020
final val Open: TastyFlagSet = Enum.next
21-
final val maxFlag: Int = Open.shift
21+
final val SuperParamAlias: TastyFlagSet = Open.next
22+
final val maxFlag: Int = SuperParamAlias.shift
2223

2324
case class TastyFlagSet private[TastyFlags](private val flags: Int) extends AnyVal {
2425

@@ -46,7 +47,6 @@ object TastyFlags {
4647
def is(mask: TastyFlagSet, butNot: TastyFlagSet): Boolean = if (!butNot) is(mask) else is(mask) && not(butNot)
4748
def not(mask: TastyFlagSet): Boolean = !isOneOf(mask)
4849
def hasFlags: Boolean = this.flags != 0
49-
def except(mask: TastyFlagSet): (Boolean, TastyFlagSet) = is(mask) -> (this &~ mask)
5050

5151
def debug: String = {
5252
if (!this) {
@@ -55,19 +55,20 @@ object TastyFlags {
5555
else {
5656
toSingletonSets.map { f =>
5757
(f: @unchecked) match {
58-
case Erased => "Erased"
59-
case Internal => "Internal"
60-
case Inline => "Inline"
61-
case InlineProxy => "InlineProxy"
62-
case Opaque => "Opaque"
63-
case Scala2x => "Scala2x"
64-
case Extension => "Extension"
65-
case Given => "Given"
66-
case Exported => "Exported"
67-
case NoInits => "NoInits"
68-
case TastyMacro => "TastyMacro"
69-
case Enum => "Enum"
70-
case Open => "Open"
58+
case Erased => "Erased"
59+
case Internal => "Internal"
60+
case Inline => "Inline"
61+
case InlineProxy => "InlineProxy"
62+
case Opaque => "Opaque"
63+
case Scala2x => "Scala2x"
64+
case Extension => "Extension"
65+
case Given => "Given"
66+
case Exported => "Exported"
67+
case NoInits => "NoInits"
68+
case TastyMacro => "TastyMacro"
69+
case Enum => "Enum"
70+
case Open => "Open"
71+
case SuperParamAlias => "SuperParamAlias"
7172
}
7273
} mkString(" | ")
7374
}

src/compiler/scala/tools/nsc/tasty/TastyFormat.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ Standard Section: "Comments" Comment*
256256
object TastyFormat {
257257

258258
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
259-
val MajorVersion: Int = 19
259+
val MajorVersion: Int = 20
260260
val MinorVersion: Int = 0
261261

262262
/** Tags used to serialize names */
@@ -352,6 +352,7 @@ object TastyFormat {
352352
final val EXPORTED = 39
353353
final val OPEN = 40
354354
final val PARAMEND = 41
355+
final val PARAMalias = 42
355356

356357
// Cat. 2: tag Nat
357358

@@ -478,7 +479,7 @@ object TastyFormat {
478479

479480
/** Useful for debugging */
480481
def isLegalTag(tag: Int): Boolean =
481-
firstSimpleTreeTag <= tag && tag <= PARAMEND ||
482+
firstSimpleTreeTag <= tag && tag <= PARAMalias ||
482483
firstNatTreeTag <= tag && tag <= RENAMED ||
483484
firstASTTreeTag <= tag && tag <= BOUNDED ||
484485
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
@@ -521,6 +522,7 @@ object TastyFormat {
521522
| STABLE
522523
| EXTENSION
523524
| PARAMsetter
525+
| PARAMalias
524526
| EXPORTED
525527
| OPEN
526528
| ANNOTATION
@@ -585,6 +587,7 @@ object TastyFormat {
585587
case EXPORTED => "EXPORTED"
586588
case OPEN => "OPEN"
587589
case PARAMEND => "PARAMEND"
590+
case PARAMalias => "PARAMalias"
588591

589592
case SHAREDterm => "SHAREDterm"
590593
case SHAREDtype => "SHAREDtype"

src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -528,11 +528,12 @@ class TreeUnpickler[Tasty <: TastyUniverse](
528528
private def localContext(owner: Symbol)(implicit ctx: Context): Context =
529529
ctx.fresh.setOwner(owner)
530530

531-
private def normalizeFlags(tag: Int, owner: Symbol, givenFlags: FlagSet, name: Name, tname: TastyName, isAbsType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = {
531+
private def normalizeFlags(tag: Int, owner: Symbol, givenFlags: FlagSet, tastyFlags: TastyFlagSet, name: Name, tname: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = {
532532
val lacksDefinition =
533533
rhsIsEmpty &&
534534
name.isTermName && !name.isConstructorName && !givenFlags.isOneOf(TermParamOrAccessor) ||
535-
isAbsType
535+
isAbsType ||
536+
tastyFlags.is(Opaque) && !isClass
536537
var flags = givenFlags
537538
if (lacksDefinition && tag != PARAM) flags |= Deferred
538539
if (tag === DEFDEF) flags |= Method
@@ -622,7 +623,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
622623
if (!rhsIsEmpty) skipTree()
623624
val (givenFlags, tastyFlagSet, annotFns, privateWithin) =
624625
readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol)
625-
val flags = normalizeFlags(tag, ctx.owner, givenFlags, name, tname, isAbsType, rhsIsEmpty)
626+
val flags = normalizeFlags(tag, ctx.owner, givenFlags, tastyFlagSet, name, tname, isAbsType, isClass, rhsIsEmpty)
626627
def showFlags = {
627628
if (!tastyFlagSet)
628629
show(flags)
@@ -765,6 +766,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
765766
case EXTENSION => addTastyFlag(Extension)
766767
case GIVEN => addFlag(Implicit) //addTastyFlag(Given)
767768
case PARAMsetter => addFlag(ParamAccessor)
769+
case PARAMalias => addTastyFlag(SuperParamAlias)
768770
case EXPORTED => addTastyFlag(Exported)
769771
case OPEN => addTastyFlag(Open)
770772
case PRIVATEqualified =>
@@ -927,7 +929,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
927929
NoCycle(at = symAddr)
928930
case VALDEF => // valdef in TASTy is either a module value or a method forwarder to a local value.
929931
val isInline = completer.tastyFlagSet.is(Inline)
930-
val unsupported = completer.tastyFlagSet &~ (Inline | Enum)
932+
val unsupported = completer.tastyFlagSet &~ (Inline | Enum | Extension)
931933
assertTasty(!unsupported, s"unsupported Scala 3 flags on $sym: ${show(unsupported)}")
932934
val tpe = readTpt()(localCtx).tpe
933935
if (isInline) assertTasty(isConstantType(tpe), s"inline val ${sym.nameString} with non-constant type $tpe")
@@ -938,21 +940,19 @@ class TreeUnpickler[Tasty <: TastyUniverse](
938940
}
939941
NoCycle(at = symAddr)
940942
case TYPEDEF | TYPEPARAM =>
941-
val unsupported = completer.tastyFlagSet &~ Enum
943+
val unsupported = completer.tastyFlagSet &~ (Enum | Open | Opaque)
942944
assertTasty(!unsupported, s"unsupported Scala 3 flags on $sym: ${show(unsupported)}")
943945
if (sym.isClass) {
944946
sym.owner.ensureCompleted()
945947
readTemplate(symAddr)(localCtx)
946948
}
947949
else {
948-
sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see i3816.scala
949-
// sym.setFlag(Provisional)
950+
sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see https://github.com/lampepfl/dotty/blob/master/tests/pos/i3816.scala
951+
// sym.setFlag(Provisional) // TODO [tasty]: is there an equivalent in scala 2?
950952
val rhs = readTpt()(localCtx)
951-
sym.info = new NoCompleter {
952-
override def completerTypeParams(sym: Symbol)(implicit ctx: Context) =
953-
rhs.tpe.typeParams
954-
}
955-
// TODO check for cycles
953+
sym.info = new NoCompleter {}
954+
// TODO [tasty]: if opaque type alias will be supported, unwrap `type bounds with alias` to bounds and then
955+
// refine self type of the owner to be aware of the alias.
956956
sym.info = rhs.tpe match {
957957
case bounds @ TypeBounds(lo: PolyType, hi: PolyType) if !(mergeableParams(lo,hi)) =>
958958
new ErrorCompleter(owner =>
@@ -961,12 +961,13 @@ class TreeUnpickler[Tasty <: TastyUniverse](
961961
case tpe => tpe
962962
}
963963
if (sym.is(Param)) sym.flags &= ~(Private | Protected)
964-
// sym.normalizeOpaque()
964+
// if sym.isOpaqueAlias then sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on
965965
// sym.resetFlag(Provisional)
966966
NoCycle(at = symAddr)
967967
}
968968
case PARAM =>
969-
assertTasty(!completer.tastyFlagSet, s"unsupported Scala 3 flags on parameter $sym: ${show(completer.tastyFlagSet)}")
969+
val unsupported = completer.tastyFlagSet &~ SuperParamAlias
970+
assertTasty(!unsupported, s"unsupported Scala 3 flags on parameter $sym: ${show(unsupported)}")
970971
val tpt = readTpt()(localCtx)
971972
if (nothingButMods(end) && sym.not(ParamAccessor)) {
972973
sym.info = tpt.tpe
@@ -1040,6 +1041,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
10401041
readIndexedMember() // ctor
10411042
cls.info = {
10421043
val classInfo = new ClassInfoType(parentTypes, cls.rawInfo.decls, cls.asType)
1044+
// TODO [tasty]: if support opaque types, refine the self type with any opaque members here
10431045
if (tparams.isEmpty) classInfo
10441046
else new PolyType(tparams.map(symFromNoCycle), classInfo)
10451047
}
@@ -1565,9 +1567,11 @@ class TreeUnpickler[Tasty <: TastyUniverse](
15651567
// if (nextUnsharedTag === CASEDEF) (EmptyTree, fst) else (fst, readTpt())
15661568
// MatchTypeTree(bound, scrut, readCases(end))
15671569
case TYPEBOUNDStpt =>
1568-
val lo = readTpt()
1569-
val hi = if (currentAddr === end) lo else readTpt()
1570-
TypeBoundsTree(lo, hi).setType(TypeBounds.bounded(lo.tpe, hi.tpe))
1570+
val lo = readTpt()
1571+
val hi = if (currentAddr == end) lo else readTpt()
1572+
val alias = if (currentAddr == end) emptyTree else readTpt()
1573+
if (alias != emptyTree) alias // only for opaque type alias
1574+
else TypeBoundsTree(lo, hi).setType(TypeBounds.bounded(lo.tpe, hi.tpe))
15711575
// case HOLE =>
15721576
// readHole(end, isType = false)
15731577
// case _ =>

src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,20 @@ trait FlagOps extends TastyKernel { self: TastyUniverse =>
7171
if (!flags) "EmptyTastyFlags"
7272
else flags.toSingletonSets.map { f =>
7373
(f: @unchecked) match {
74-
case Erased => "erased"
75-
case Internal => "<internal>"
76-
case Inline => "inline"
77-
case InlineProxy => "<inlineproxy>"
78-
case Opaque => "opaque"
79-
case Scala2x => "<scala2x>"
80-
case Extension => "<extension>"
81-
case Given => "given"
82-
case Exported => "<exported>"
83-
case NoInits => "<noinits>"
84-
case TastyMacro => "<tastymacro>"
85-
case Enum => "enum"
86-
case Open => "open"
74+
case Erased => "erased"
75+
case Internal => "<internal>"
76+
case Inline => "inline"
77+
case InlineProxy => "<inlineproxy>"
78+
case Opaque => "opaque"
79+
case Scala2x => "<scala2x>"
80+
case Extension => "<extension>"
81+
case Given => "given"
82+
case Exported => "<exported>"
83+
case NoInits => "<noinits>"
84+
case TastyMacro => "<tastymacro>"
85+
case Enum => "enum"
86+
case Open => "open"
87+
case SuperParamAlias => "<superparamalias>"
8788
}
8889
} mkString(" | ")
8990
}

src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,6 @@ trait TypeOps extends TastyKernel { self: TastyUniverse =>
152152
private[this] var myModuleClassFn: Context => Symbol = NoSymbolFn
153153
private[this] var myTastyFlagSet: TastyFlagSet = emptyTastyFlags
154154

155-
/** The type parameters computed by the completer before completion has finished */
156-
def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[Symbol] = sym.info.typeParams
157-
// if (sym.is(Touched)) Nil // return `Nil` instead of throwing a cyclic reference
158-
// else sym.info.typeParams
159-
160155
override def decls: Scope = myDecls
161156
def sourceModule(implicit ctx: Context): Symbol = mySourceModuleFn(ctx)
162157
def moduleClass(implicit ctx: Context): Symbol = myModuleClassFn(ctx)

test/tasty/neg/src-3/intent/Position.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import scala.quoted._
2323

2424
case class Position(filePath: String, lineNumber0: Int, columnNumber0: Int)
2525

26-
object Position with
26+
object Position:
2727

2828
implicit inline def here: Position = ${ genPosition }
2929

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
package tastytest.intent
22

33
trait TestLanguage {
4-
def expect[T](expr: => T)(given pos: Position): Expect[T] = new Expect[T](expr, pos)
4+
def expect[T](expr: => T)(using pos: Position): Expect[T] = new Expect[T](expr, pos)
55
}
66

77
trait Eq[T]
88

99
object IntEq extends Eq[Int]
1010

11-
trait EqGivens with
11+
trait EqGivens:
1212
given Eq[Int] = IntEq
1313

1414
trait Formatter[T]
1515

1616
object IntFmt extends Formatter[Int]
1717

18-
trait FormatterGivens with
18+
trait FormatterGivens:
1919
given Formatter[Int] = IntFmt
2020

2121
abstract class TestSuite
@@ -25,11 +25,11 @@ trait Stateless extends IntentStatelessSyntax with ExpectGivens with EqGivens wi
2525

2626
class Expect[T](blk: => T, position: Position, negated: Boolean = false)
2727

28-
trait ExpectGivens with
28+
trait ExpectGivens:
2929

30-
def [T](expect: Expect[T]) toEqual (expected: T)(given eqq: Eq[T], fmt: Formatter[T]): Expectation = ???
30+
def [T](expect: Expect[T]) toEqual (expected: T)(using eqq: Eq[T], fmt: Formatter[T]): Expectation = ???
3131

32-
trait IntentStatelessSyntax extends TestLanguage with
32+
trait IntentStatelessSyntax extends TestLanguage:
3333

34-
def (testName: String) in (testImpl: => Expectation)(given pos: Position): Unit = ???
35-
def (blockName: String) apply (block: => Unit)(given pos: Position): Unit = ???
34+
def (testName: String) in (testImpl: => Expectation)(using pos: Position): Unit = ???
35+
def (blockName: String) apply (block: => Unit)(using pos: Position): Unit = ???
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tastytest
2+
3+
import Logarithms._
4+
5+
object TestLogarithms extends Suite("TestLogarithms") {
6+
7+
val Some(l1) = Logarithm.of(2)
8+
val Some(l2) = Logarithm.of(3)
9+
10+
val l3: Double = l1 - l2 // currently opaque type aliases are transparent to Scala 2
11+
12+
test(assert(logarithmOps.toDouble(logarithmOps.+(l1)(l2)) === 4.999999999999999))
13+
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package tastytest
2+
3+
object TestSuperParamAliases extends Suite("SuperParamAliases") {
4+
5+
class Qux(parent: Option[Qux]) extends SuperParamAliases.Bar(parent)
6+
7+
test(assert(new Qux(None).getParent === Option.empty[SuperParamAliases.Foo]))
8+
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package tastytest
2+
3+
object Logarithms {
4+
5+
opaque type Logarithm = Double
6+
7+
object Logarithm {
8+
9+
// These are the two ways to lift to the Logarithm type
10+
11+
private[Logarithms] def apply(d: Double): Logarithm = math.log(d)
12+
13+
def of(d: Double): Option[Logarithm] =
14+
if (d > 0.0) Some(math.log(d)) else None
15+
}
16+
17+
// Extension methods define opaque types' public APIs
18+
extension logarithmOps on (x: Logarithm) {
19+
def toDouble: Double = math.exp(x)
20+
def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
21+
def * (y: Logarithm): Logarithm = x + y
22+
}
23+
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package tastytest
2+
3+
object SuperParamAliases {
4+
5+
trait Foo {
6+
def parent: Option[Foo]
7+
def getParent: Option[Foo] = parent
8+
}
9+
10+
class Bar(val parent: Option[Foo]) extends Foo
11+
12+
}

0 commit comments

Comments
 (0)