Skip to content

Support Dotty 0.23.0-RC1 enable Opaque type aliases to be transparent #71

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 1 commit into from
Mar 31, 2020
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
6 changes: 3 additions & 3 deletions project/DottySupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import sbt.librarymanagement.{
* Dotty in .travis.yml.
*/
object DottySupport {
val currentDottyRelease = "0.22.0-RC1" // TASTy version 19
val dottyLibrary = "ch.epfl.lamp" % "dotty-library_0.22" % currentDottyRelease
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.22" % currentDottyRelease
val currentDottyRelease = "0.23.0-RC1" // TASTy version 20
val dottyLibrary = "ch.epfl.lamp" % "dotty-library_0.23" % currentDottyRelease
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.23" % currentDottyRelease
val dottyVersion = "0.21.0-RC1"
val compileWithDotty: Boolean =
Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false)
Expand Down
31 changes: 16 additions & 15 deletions src/compiler/scala/tools/nsc/tasty/TastyFlags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ object TastyFlags {
final val TastyMacro: TastyFlagSet = NoInits.next
final val Enum: TastyFlagSet = TastyMacro.next
final val Open: TastyFlagSet = Enum.next
final val maxFlag: Int = Open.shift
final val SuperParamAlias: TastyFlagSet = Open.next
final val maxFlag: Int = SuperParamAlias.shift

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

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

def debug: String = {
if (!this) {
Expand All @@ -55,19 +55,20 @@ object TastyFlags {
else {
toSingletonSets.map { f =>
(f: @unchecked) match {
case Erased => "Erased"
case Internal => "Internal"
case Inline => "Inline"
case InlineProxy => "InlineProxy"
case Opaque => "Opaque"
case Scala2x => "Scala2x"
case Extension => "Extension"
case Given => "Given"
case Exported => "Exported"
case NoInits => "NoInits"
case TastyMacro => "TastyMacro"
case Enum => "Enum"
case Open => "Open"
case Erased => "Erased"
case Internal => "Internal"
case Inline => "Inline"
case InlineProxy => "InlineProxy"
case Opaque => "Opaque"
case Scala2x => "Scala2x"
case Extension => "Extension"
case Given => "Given"
case Exported => "Exported"
case NoInits => "NoInits"
case TastyMacro => "TastyMacro"
case Enum => "Enum"
case Open => "Open"
case SuperParamAlias => "SuperParamAlias"
}
} mkString(" | ")
}
Expand Down
7 changes: 5 additions & 2 deletions src/compiler/scala/tools/nsc/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ this file adapted from https://github.com/lampepfl/dotty/blob/master/compiler/sr
object TastyFormat {

final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
val MajorVersion: Int = 19
val MajorVersion: Int = 20
val MinorVersion: Int = 0

/** Tags used to serialize names */
Expand Down Expand Up @@ -105,6 +105,7 @@ object TastyFormat {
final val EXPORTED = 39
final val OPEN = 40
final val PARAMEND = 41
final val PARAMalias = 42

// Cat. 2: tag Nat

Expand Down Expand Up @@ -232,7 +233,7 @@ object TastyFormat {

/** Useful for debugging */
def isLegalTag(tag: Int): Boolean =
firstSimpleTreeTag <= tag && tag <= PARAMEND ||
firstSimpleTreeTag <= tag && tag <= PARAMalias ||
firstNatTreeTag <= tag && tag <= RENAMED ||
firstASTTreeTag <= tag && tag <= BOUNDED ||
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
Expand Down Expand Up @@ -275,6 +276,7 @@ object TastyFormat {
| STABLE
| EXTENSION
| PARAMsetter
| PARAMalias
| EXPORTED
| OPEN
| ANNOTATION
Expand Down Expand Up @@ -339,6 +341,7 @@ object TastyFormat {
case EXPORTED => "EXPORTED"
case OPEN => "OPEN"
case PARAMEND => "PARAMEND"
case PARAMalias => "PARAMalias"

case SHAREDterm => "SHAREDterm"
case SHAREDtype => "SHAREDtype"
Expand Down
38 changes: 21 additions & 17 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -528,11 +528,12 @@ class TreeUnpickler[Tasty <: TastyUniverse](
private def localContext(owner: Symbol)(implicit ctx: Context): Context =
ctx.fresh.setOwner(owner)

private def normalizeFlags(tag: Int, owner: Symbol, givenFlags: FlagSet, name: Name, tname: TastyName, isAbsType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = {
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 = {
val lacksDefinition =
rhsIsEmpty &&
name.isTermName && !name.isConstructorName && !givenFlags.isOneOf(TermParamOrAccessor) ||
isAbsType
isAbsType ||
tastyFlags.is(Opaque) && !isClass
var flags = givenFlags
if (lacksDefinition && tag != PARAM) flags |= Deferred
if (tag === DEFDEF) flags |= Method
Expand Down Expand Up @@ -622,7 +623,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
if (!rhsIsEmpty) skipTree()
val (givenFlags, tastyFlagSet, annotFns, privateWithin) =
readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol)
val flags = normalizeFlags(tag, ctx.owner, givenFlags, name, tname, isAbsType, rhsIsEmpty)
val flags = normalizeFlags(tag, ctx.owner, givenFlags, tastyFlagSet, name, tname, isAbsType, isClass, rhsIsEmpty)
def showFlags = {
if (!tastyFlagSet)
show(flags)
Expand Down Expand Up @@ -765,6 +766,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case EXTENSION => addTastyFlag(Extension)
case GIVEN => addFlag(Implicit) //addTastyFlag(Given)
case PARAMsetter => addFlag(ParamAccessor)
case PARAMalias => addTastyFlag(SuperParamAlias)
case EXPORTED => addTastyFlag(Exported)
case OPEN => addTastyFlag(Open)
case PRIVATEqualified =>
Expand Down Expand Up @@ -927,7 +929,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
NoCycle(at = symAddr)
case VALDEF => // valdef in TASTy is either a module value or a method forwarder to a local value.
val isInline = completer.tastyFlagSet.is(Inline)
val unsupported = completer.tastyFlagSet &~ (Inline | Enum)
val unsupported = completer.tastyFlagSet &~ (Inline | Enum | Extension)
assertTasty(!unsupported, s"unsupported Scala 3 flags on $sym: ${show(unsupported)}")
val tpe = readTpt()(localCtx).tpe
if (isInline) assertTasty(isConstantType(tpe), s"inline val ${sym.nameString} with non-constant type $tpe")
Expand All @@ -938,21 +940,19 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}
NoCycle(at = symAddr)
case TYPEDEF | TYPEPARAM =>
val unsupported = completer.tastyFlagSet &~ Enum
val unsupported = completer.tastyFlagSet &~ (Enum | Open | Opaque)
assertTasty(!unsupported, s"unsupported Scala 3 flags on $sym: ${show(unsupported)}")
if (sym.isClass) {
sym.owner.ensureCompleted()
readTemplate(symAddr)(localCtx)
}
else {
sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see i3816.scala
// sym.setFlag(Provisional)
sym.info = TypeBounds.empty // needed to avoid cyclic references when unpickling rhs, see https://github.com/lampepfl/dotty/blob/master/tests/pos/i3816.scala
// sym.setFlag(Provisional) // TODO [tasty]: is there an equivalent in scala 2?
val rhs = readTpt()(localCtx)
sym.info = new NoCompleter {
override def completerTypeParams(sym: Symbol)(implicit ctx: Context) =
rhs.tpe.typeParams
}
// TODO check for cycles
sym.info = new NoCompleter {}
// TODO [tasty]: if opaque type alias will be supported, unwrap `type bounds with alias` to bounds and then
// refine self type of the owner to be aware of the alias.
sym.info = rhs.tpe match {
case bounds @ TypeBounds(lo: PolyType, hi: PolyType) if !(mergeableParams(lo,hi)) =>
new ErrorCompleter(owner =>
Expand All @@ -961,12 +961,13 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case tpe => tpe
}
if (sym.is(Param)) sym.flags &= ~(Private | Protected)
// sym.normalizeOpaque()
// if sym.isOpaqueAlias then sym.typeRef.recomputeDenot() // make sure we see the new bounds from now on
// sym.resetFlag(Provisional)
NoCycle(at = symAddr)
}
case PARAM =>
assertTasty(!completer.tastyFlagSet, s"unsupported Scala 3 flags on parameter $sym: ${show(completer.tastyFlagSet)}")
val unsupported = completer.tastyFlagSet &~ SuperParamAlias
assertTasty(!unsupported, s"unsupported Scala 3 flags on parameter $sym: ${show(unsupported)}")
val tpt = readTpt()(localCtx)
if (nothingButMods(end) && sym.not(ParamAccessor)) {
sym.info = tpt.tpe
Expand Down Expand Up @@ -1040,6 +1041,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
readIndexedMember() // ctor
cls.info = {
val classInfo = new ClassInfoType(parentTypes, cls.rawInfo.decls, cls.asType)
// TODO [tasty]: if support opaque types, refine the self type with any opaque members here
if (tparams.isEmpty) classInfo
else new PolyType(tparams.map(symFromNoCycle), classInfo)
}
Expand Down Expand Up @@ -1565,9 +1567,11 @@ class TreeUnpickler[Tasty <: TastyUniverse](
// if (nextUnsharedTag === CASEDEF) (EmptyTree, fst) else (fst, readTpt())
// MatchTypeTree(bound, scrut, readCases(end))
case TYPEBOUNDStpt =>
val lo = readTpt()
val hi = if (currentAddr === end) lo else readTpt()
TypeBoundsTree(lo, hi).setType(TypeBounds.bounded(lo.tpe, hi.tpe))
val lo = readTpt()
val hi = if (currentAddr == end) lo else readTpt()
val alias = if (currentAddr == end) emptyTree else readTpt()
if (alias != emptyTree) alias // only for opaque type alias
else TypeBoundsTree(lo, hi).setType(TypeBounds.bounded(lo.tpe, hi.tpe))
// case HOLE =>
// readHole(end, isType = false)
// case _ =>
Expand Down
27 changes: 14 additions & 13 deletions src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,20 @@ trait FlagOps extends TastyKernel { self: TastyUniverse =>
if (!flags) "EmptyTastyFlags"
else flags.toSingletonSets.map { f =>
(f: @unchecked) match {
case Erased => "erased"
case Internal => "<internal>"
case Inline => "inline"
case InlineProxy => "<inlineproxy>"
case Opaque => "opaque"
case Scala2x => "<scala2x>"
case Extension => "<extension>"
case Given => "given"
case Exported => "<exported>"
case NoInits => "<noinits>"
case TastyMacro => "<tastymacro>"
case Enum => "enum"
case Open => "open"
case Erased => "erased"
case Internal => "<internal>"
case Inline => "inline"
case InlineProxy => "<inlineproxy>"
case Opaque => "opaque"
case Scala2x => "<scala2x>"
case Extension => "<extension>"
case Given => "given"
case Exported => "<exported>"
case NoInits => "<noinits>"
case TastyMacro => "<tastymacro>"
case Enum => "enum"
case Open => "open"
case SuperParamAlias => "<superparamalias>"
}
} mkString(" | ")
}
5 changes: 0 additions & 5 deletions src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,6 @@ trait TypeOps extends TastyKernel { self: TastyUniverse =>
private[this] var myModuleClassFn: Context => Symbol = NoSymbolFn
private[this] var myTastyFlagSet: TastyFlagSet = emptyTastyFlags

/** The type parameters computed by the completer before completion has finished */
def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[Symbol] = sym.info.typeParams
// if (sym.is(Touched)) Nil // return `Nil` instead of throwing a cyclic reference
// else sym.info.typeParams

override def decls: Scope = myDecls
def sourceModule(implicit ctx: Context): Symbol = mySourceModuleFn(ctx)
def moduleClass(implicit ctx: Context): Symbol = myModuleClassFn(ctx)
Expand Down
2 changes: 1 addition & 1 deletion test/tasty/neg/src-3/intent/Position.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import scala.quoted._

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

object Position with
object Position:

implicit inline def here: Position = ${ genPosition }

Expand Down
16 changes: 8 additions & 8 deletions test/tasty/neg/src-3/intent/internal.scala
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package tastytest.intent

trait TestLanguage {
def expect[T](expr: => T)(given pos: Position): Expect[T] = new Expect[T](expr, pos)
def expect[T](expr: => T)(using pos: Position): Expect[T] = new Expect[T](expr, pos)
}

trait Eq[T]

object IntEq extends Eq[Int]

trait EqGivens with
trait EqGivens:
given Eq[Int] = IntEq

trait Formatter[T]

object IntFmt extends Formatter[Int]

trait FormatterGivens with
trait FormatterGivens:
given Formatter[Int] = IntFmt

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

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

trait ExpectGivens with
trait ExpectGivens:

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

trait IntentStatelessSyntax extends TestLanguage with
trait IntentStatelessSyntax extends TestLanguage:

def (testName: String) in (testImpl: => Expectation)(given pos: Position): Unit = ???
def (blockName: String) apply (block: => Unit)(given pos: Position): Unit = ???
def (testName: String) in (testImpl: => Expectation)(using pos: Position): Unit = ???
def (blockName: String) apply (block: => Unit)(using pos: Position): Unit = ???
14 changes: 14 additions & 0 deletions test/tasty/run/src-2/tastytest/TestLogarithms.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tastytest

import Logarithms._

object TestLogarithms extends Suite("TestLogarithms") {

val Some(l1) = Logarithm.of(2)
val Some(l2) = Logarithm.of(3)

val l3: Double = l1 - l2 // currently opaque type aliases are transparent to Scala 2

test(assert(logarithmOps.toDouble(logarithmOps.+(l1)(l2)) === 4.999999999999999))

}
9 changes: 9 additions & 0 deletions test/tasty/run/src-2/tastytest/TestSuperParamAliases.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tastytest

object TestSuperParamAliases extends Suite("SuperParamAliases") {

class Qux(parent: Option[Qux]) extends SuperParamAliases.Bar(parent)

test(assert(new Qux(None).getParent === Option.empty[SuperParamAliases.Foo]))

}
24 changes: 24 additions & 0 deletions test/tasty/run/src-3/tastytest/Logarithms.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package tastytest

object Logarithms {

opaque type Logarithm = Double

object Logarithm {

// These are the two ways to lift to the Logarithm type

private[Logarithms] def apply(d: Double): Logarithm = math.log(d)

def of(d: Double): Option[Logarithm] =
if (d > 0.0) Some(math.log(d)) else None
}

// Extension methods define opaque types' public APIs
extension logarithmOps on (x: Logarithm) {
def toDouble: Double = math.exp(x)
def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
def * (y: Logarithm): Logarithm = x + y
}

}
12 changes: 12 additions & 0 deletions test/tasty/run/src-3/tastytest/SuperParamAliases.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package tastytest

object SuperParamAliases {

trait Foo {
def parent: Option[Foo]
def getParent: Option[Foo] = parent
}

class Bar(val parent: Option[Foo]) extends Foo

}