Skip to content

Fix #643 - Scala2 unpickling now sets NoInits flag for interfaces. #663

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 5 commits into from
Jun 19, 2015
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
26 changes: 17 additions & 9 deletions src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Names._, StdNames._, NameOps._, Decorators._, Symbols._
import util.HashSet

trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
import TreeInfo._

// Note: the <: Type constraint looks necessary (and is needed to make the file compile in dotc).
// But Scalac accepts the program happily without it. Need to find out why.
Expand All @@ -24,12 +25,16 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => false
}

/** Does tree contain an initialization part when seen as a member of a class or trait?
/** The largest subset of {NoInits, PureInterface} that a
* trait enclosing this statement can have as flags.
* Does tree contain an initialization part when seen as a member of a class or trait?
*/
def isNoInitMember(tree: Tree): Boolean = unsplice(tree) match {
case EmptyTree | Import(_, _) | TypeDef(_, _) | DefDef(_, _, _, _, _) => true
case tree: ValDef => tree.unforcedRhs == EmptyTree
case _ => false
def defKind(tree: Tree): FlagSet = unsplice(tree) match {
case EmptyTree | _: Import => NoInitsInterface
case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
case _ => EmptyFlags
}

def isOpAssign(tree: Tree) = unsplice(tree) match {
Expand Down Expand Up @@ -272,6 +277,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
}

trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
import TreeInfo._

/** The purity level of this statement.
* @return pure if statement has no side effects
Expand Down Expand Up @@ -510,15 +516,17 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case nil =>
Nil
}
}

private class PurityLevel(val x: Int) {
object TreeInfo {
class PurityLevel(val x: Int) extends AnyVal {
def >= (that: PurityLevel) = x >= that.x
def min(that: PurityLevel) = new PurityLevel(x min that.x)
}

private val Pure = new PurityLevel(2)
private val Idempotent = new PurityLevel(1)
private val Impure = new PurityLevel(0)
val Pure = new PurityLevel(2)
val Idempotent = new PurityLevel(1)
val Impure = new PurityLevel(0)
}

/** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe)
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ object Flags {
/** Pure interfaces always have these flags */
final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface

final val NoInitsInterface = NoInits | PureInterface

/** The flags of the self symbol */
final val SelfSymFlags = Private | Local | Deferred

Expand Down
9 changes: 8 additions & 1 deletion src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ trait SymDenotations { this: Context =>
}

def stillValid(denot: SymDenotation): Boolean =
if (denot is ValidForever) true
if (denot.is(ValidForever) || denot.isRefinementClass) true
else {
val initial = denot.initial
if (initial ne denot)
Expand All @@ -49,6 +49,7 @@ trait SymDenotations { this: Context =>
val owner = denot.owner.denot
stillValid(owner) && (
!owner.isClass
|| owner.isRefinementClass
|| (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol)
|| denot.isSelfSym)
} catch {
Expand Down Expand Up @@ -114,6 +115,12 @@ object SymDenotations {
/** Unset given flags(s) of this denotation */
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }

/** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */
final def setApplicableFlags(flags: FlagSet): Unit = {
val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits
setFlag(flags & mask)
}

/** Has this denotation one of the flags in `fs` set? */
final def is(fs: FlagSet)(implicit ctx: Context) = {
(if (fs <= FromStartFlags) myFlags else flags) is fs
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ object Symbols {
/** Subclass tests and casts */
final def isTerm(implicit ctx: Context): Boolean =
(if(isDefinedInCurrentRun) lastDenot else denot).isTerm

final def isType(implicit ctx: Context): Boolean =
(if(isDefinedInCurrentRun) lastDenot else denot).isType

Expand Down
28 changes: 16 additions & 12 deletions src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package tasty
import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._
import StdNames._, Denotations._, Flags._, Constants._, Annotations._
import util.Positions._
import dotty.tools.dotc.ast.{tpd, Trees, untpd}
import ast.{tpd, Trees, untpd}
import Trees._
import Decorators._
import TastyUnpickler._, TastyBuffer._, PositionPickler._
Expand Down Expand Up @@ -350,9 +350,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
}

/** Create symbol of definition node and enter in symAtAddr map
* @return true iff the definition does not contain initialization code
* @return the largest subset of {NoInits, PureInterface} that a
* trait owning this symbol can have as flags.
*/
def createSymbol()(implicit ctx: Context): Boolean = {
def createSymbol()(implicit ctx: Context): FlagSet = {
val start = currentAddr
val tag = readByte()
val end = readEnd()
Expand Down Expand Up @@ -408,7 +409,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
sym.completer.withDecls(newScope)
forkAt(templateStart).indexTemplateParams()(localContext(sym))
}
tag != VALDEF || rhsIsEmpty
if (isClass) NoInits
else if (sym.isType || sym.isConstructor || flags.is(Deferred)) NoInitsInterface
else if (tag == VALDEF) EmptyFlags
else NoInits
}

/** Read modifier list into triplet of flags, annotations and a privateWithin
Expand Down Expand Up @@ -472,25 +476,26 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {

/** Create symbols for a definitions in statement sequence between
* current address and `end`.
* @return true iff none of the statements contains initialization code
* @return the largest subset of {NoInits, PureInterface} that a
* trait owning the indexed statements can have as flags.
*/
def indexStats(end: Addr)(implicit ctx: Context): Boolean = {
val noInitss =
def indexStats(end: Addr)(implicit ctx: Context): FlagSet = {
val flagss =
until(end) {
nextByte match {
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
createSymbol()
case IMPORT =>
skipTree()
true
NoInitsInterface
case PACKAGE =>
processPackage { (pid, end) => implicit ctx => indexStats(end) }
case _ =>
skipTree()
false
EmptyFlags
}
}
noInitss.forall(_ == true)
(NoInitsInterface /: flagss)(_ & _)
}

/** Process package with given operation `op`. The operation takes as arguments
Expand Down Expand Up @@ -631,8 +636,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
}
else EmptyValDef
setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe)
val noInits = fork.indexStats(end)
if (noInits) cls.setFlag(NoInits)
cls.setApplicableFlags(fork.indexStats(end))
val constr = readIndexedDef().asInstanceOf[DefDef]

def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ object PickleBuffer {
DEFERRED_PKL -> Deferred,
FINAL_PKL -> Final,
METHOD_PKL -> Method,
INTERFACE_PKL -> PureInterface,
INTERFACE_PKL -> NoInitsInterface,
MODULE_PKL -> Module,
IMPLICIT_PKL -> Implicit,
SEALED_PKL -> Sealed,
Expand Down
17 changes: 8 additions & 9 deletions src/dotty/tools/dotc/transform/ExpandSAMs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>

import ast.tpd._

def noJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
!cls.is(Trait) ||
cls.superClass != defn.ObjectClass ||
!cls.is(NoInits) ||
!cls.directlyInheritedTraits.forall(_.is(NoInits)) ||
ExplicitOuter.needsOuterIfReferenced(cls) ||
cls.typeRef.fields.nonEmpty // Superaccessors already show up as abstract methods here, so no test necessary

/** Is SAMType `cls` also a SAM under the rules of the JVM? */
def isJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
cls.is(NoInitsTrait) &&
cls.superClass == defn.ObjectClass &&
cls.directlyInheritedTraits.forall(_.is(NoInits)) &&
!ExplicitOuter.needsOuterIfReferenced(cls) &&
cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary

override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol =>
tpt.tpe match {
case NoType => tree // it's a plain function
case tpe @ SAMType(_) if !noJvmSam(tpe.classSymbol.asClass) =>
case tpe @ SAMType(_) if isJvmSam(tpe.classSymbol.asClass) =>
if (tpe isRef defn.PartialFunctionClass) toPartialFunction(tree)
else tree
case tpe =>
Expand Down
5 changes: 0 additions & 5 deletions src/dotty/tools/dotc/transform/NormalizeFlags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ class NormalizeFlags extends MiniPhaseTransform with SymTransformer { thisTransf

def transformSym(ref: SymDenotation)(implicit ctx: Context) = {
var newFlags = ref.flags &~ Local
if (ref.is(NoInitsTrait) && ref.info.decls.forall(isPureInterfaceMember))
newFlags |= PureInterface
if (newFlags != ref.flags) ref.copySymDenotation(initFlags = newFlags)
else ref
}

private def isPureInterfaceMember(sym: Symbol)(implicit ctx: Context) =
if (sym.isTerm) sym.is(Deferred) else !sym.isClass
}
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,8 @@ class Namer { typer: Typer =>

index(rest)(inClassContext(selfInfo))
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
if (impl.body forall isNoInitMember) cls.setFlag(NoInits)
cls.setApplicableFlags(
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
}
}

Expand Down
File renamed without changes.
File renamed without changes.