Skip to content

Commit 05e81c1

Browse files
committed
Merge pull request #663 from dotty-staging/fix/#643-scala2-noinits
Fix #643 - Scala2 unpickling now sets NoInits flag for interfaces.
2 parents d982038 + 0e6b857 commit 05e81c1

File tree

11 files changed

+55
-39
lines changed

11 files changed

+55
-39
lines changed

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Names._, StdNames._, NameOps._, Decorators._, Symbols._
88
import util.HashSet
99

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

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

27-
/** Does tree contain an initialization part when seen as a member of a class or trait?
28+
/** The largest subset of {NoInits, PureInterface} that a
29+
* trait enclosing this statement can have as flags.
30+
* Does tree contain an initialization part when seen as a member of a class or trait?
2831
*/
29-
def isNoInitMember(tree: Tree): Boolean = unsplice(tree) match {
30-
case EmptyTree | Import(_, _) | TypeDef(_, _) | DefDef(_, _, _, _, _) => true
31-
case tree: ValDef => tree.unforcedRhs == EmptyTree
32-
case _ => false
32+
def defKind(tree: Tree): FlagSet = unsplice(tree) match {
33+
case EmptyTree | _: Import => NoInitsInterface
34+
case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
35+
case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
36+
case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
37+
case _ => EmptyFlags
3338
}
3439

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

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

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

514-
private class PurityLevel(val x: Int) {
521+
object TreeInfo {
522+
class PurityLevel(val x: Int) extends AnyVal {
515523
def >= (that: PurityLevel) = x >= that.x
516524
def min(that: PurityLevel) = new PurityLevel(x min that.x)
517525
}
518526

519-
private val Pure = new PurityLevel(2)
520-
private val Idempotent = new PurityLevel(1)
521-
private val Impure = new PurityLevel(0)
527+
val Pure = new PurityLevel(2)
528+
val Idempotent = new PurityLevel(1)
529+
val Impure = new PurityLevel(0)
522530
}
523531

524532
/** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe)

src/dotty/tools/dotc/core/Flags.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,8 @@ object Flags {
467467
/** Pure interfaces always have these flags */
468468
final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface
469469

470+
final val NoInitsInterface = NoInits | PureInterface
471+
470472
/** The flags of the self symbol */
471473
final val SelfSymFlags = Private | Local | Deferred
472474

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ trait SymDenotations { this: Context =>
4040
}
4141

4242
def stillValid(denot: SymDenotation): Boolean =
43-
if (denot is ValidForever) true
43+
if (denot.is(ValidForever) || denot.isRefinementClass) true
4444
else {
4545
val initial = denot.initial
4646
if (initial ne denot)
@@ -49,6 +49,7 @@ trait SymDenotations { this: Context =>
4949
val owner = denot.owner.denot
5050
stillValid(owner) && (
5151
!owner.isClass
52+
|| owner.isRefinementClass
5253
|| (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol)
5354
|| denot.isSelfSym)
5455
} catch {
@@ -115,6 +116,12 @@ object SymDenotations {
115116
/** Unset given flags(s) of this denotation */
116117
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }
117118

119+
/** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */
120+
final def setApplicableFlags(flags: FlagSet): Unit = {
121+
val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits
122+
setFlag(flags & mask)
123+
}
124+
118125
/** Has this denotation one of the flags in `fs` set? */
119126
final def is(fs: FlagSet)(implicit ctx: Context) = {
120127
(if (fs <= FromStartFlags) myFlags else flags) is fs

src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ object Symbols {
408408
/** Subclass tests and casts */
409409
final def isTerm(implicit ctx: Context): Boolean =
410410
(if(isDefinedInCurrentRun) lastDenot else denot).isTerm
411-
411+
412412
final def isType(implicit ctx: Context): Boolean =
413413
(if(isDefinedInCurrentRun) lastDenot else denot).isType
414414

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package tasty
66
import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._
77
import StdNames._, Denotations._, Flags._, Constants._, Annotations._
88
import util.Positions._
9-
import dotty.tools.dotc.ast.{tpd, Trees, untpd}
9+
import ast.{tpd, Trees, untpd}
1010
import Trees._
1111
import Decorators._
1212
import TastyUnpickler._, TastyBuffer._, PositionPickler._
@@ -352,9 +352,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
352352
}
353353

354354
/** Create symbol of definition node and enter in symAtAddr map
355-
* @return true iff the definition does not contain initialization code
355+
* @return the largest subset of {NoInits, PureInterface} that a
356+
* trait owning this symbol can have as flags.
356357
*/
357-
def createSymbol()(implicit ctx: Context): Boolean = {
358+
def createSymbol()(implicit ctx: Context): FlagSet = {
358359
val start = currentAddr
359360
val tag = readByte()
360361
val end = readEnd()
@@ -410,7 +411,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
410411
sym.completer.withDecls(newScope)
411412
forkAt(templateStart).indexTemplateParams()(localContext(sym))
412413
}
413-
tag != VALDEF || rhsIsEmpty
414+
if (isClass) NoInits
415+
else if (sym.isType || sym.isConstructor || flags.is(Deferred)) NoInitsInterface
416+
else if (tag == VALDEF) EmptyFlags
417+
else NoInits
414418
}
415419

416420
/** Read modifier list into triplet of flags, annotations and a privateWithin
@@ -474,25 +478,26 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
474478

475479
/** Create symbols for a definitions in statement sequence between
476480
* current address and `end`.
477-
* @return true iff none of the statements contains initialization code
481+
* @return the largest subset of {NoInits, PureInterface} that a
482+
* trait owning the indexed statements can have as flags.
478483
*/
479-
def indexStats(end: Addr)(implicit ctx: Context): Boolean = {
480-
val noInitss =
484+
def indexStats(end: Addr)(implicit ctx: Context): FlagSet = {
485+
val flagss =
481486
until(end) {
482487
nextByte match {
483488
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
484489
createSymbol()
485490
case IMPORT =>
486491
skipTree()
487-
true
492+
NoInitsInterface
488493
case PACKAGE =>
489494
processPackage { (pid, end) => implicit ctx => indexStats(end) }
490495
case _ =>
491496
skipTree()
492-
false
497+
EmptyFlags
493498
}
494499
}
495-
noInitss.forall(_ == true)
500+
(NoInitsInterface /: flagss)(_ & _)
496501
}
497502

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

640644
def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) =

src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ object PickleBuffer {
220220
DEFERRED_PKL -> Deferred,
221221
FINAL_PKL -> Final,
222222
METHOD_PKL -> Method,
223-
INTERFACE_PKL -> PureInterface,
223+
INTERFACE_PKL -> NoInitsInterface,
224224
MODULE_PKL -> Module,
225225
IMPLICIT_PKL -> Implicit,
226226
SEALED_PKL -> Sealed,

src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,19 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer =>
2525

2626
import ast.tpd._
2727

28-
def noJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
29-
!cls.is(Trait) ||
30-
cls.superClass != defn.ObjectClass ||
31-
!cls.is(NoInits) ||
32-
!cls.directlyInheritedTraits.forall(_.is(NoInits)) ||
33-
ExplicitOuter.needsOuterIfReferenced(cls) ||
34-
cls.typeRef.fields.nonEmpty // Superaccessors already show up as abstract methods here, so no test necessary
35-
28+
/** Is SAMType `cls` also a SAM under the rules of the JVM? */
29+
def isJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
30+
cls.is(NoInitsTrait) &&
31+
cls.superClass == defn.ObjectClass &&
32+
cls.directlyInheritedTraits.forall(_.is(NoInits)) &&
33+
!ExplicitOuter.needsOuterIfReferenced(cls) &&
34+
cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary
3635

3736
override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
3837
case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol =>
3938
tpt.tpe match {
4039
case NoType => tree // it's a plain function
41-
case tpe @ SAMType(_) if !noJvmSam(tpe.classSymbol.asClass) =>
40+
case tpe @ SAMType(_) if isJvmSam(tpe.classSymbol.asClass) =>
4241
if (tpe isRef defn.PartialFunctionClass) toPartialFunction(tree)
4342
else tree
4443
case tpe =>

src/dotty/tools/dotc/transform/NormalizeFlags.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ class NormalizeFlags extends MiniPhaseTransform with SymTransformer { thisTransf
1919

2020
def transformSym(ref: SymDenotation)(implicit ctx: Context) = {
2121
var newFlags = ref.flags &~ Local
22-
if (ref.is(NoInitsTrait) && ref.info.decls.forall(isPureInterfaceMember))
23-
newFlags |= PureInterface
2422
if (newFlags != ref.flags) ref.copySymDenotation(initFlags = newFlags)
2523
else ref
2624
}
27-
28-
private def isPureInterfaceMember(sym: Symbol)(implicit ctx: Context) =
29-
if (sym.isTerm) sym.is(Deferred) else !sym.isClass
3025
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,8 @@ class Namer { typer: Typer =>
563563

564564
index(rest)(inClassContext(selfInfo))
565565
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
566-
if (impl.body forall isNoInitMember) cls.setFlag(NoInits)
566+
cls.setApplicableFlags(
567+
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
567568
}
568569
}
569570

0 commit comments

Comments
 (0)