Skip to content

Commit

Permalink
introduces global.pendingSuperCall
Browse files Browse the repository at this point in the history
Similarly to global.emptyValDef, which is a dummy that stands for an
empty self-type, this commit introduces global.pendingSuperCall, which
stands for a yet-to-be-filled-in call to a superclass constructor.

pendingSuperCall is emitted by Parsers.template, treated specially by
Typers.typedParentType and replaced with a real superclass ctor call
by Typers.typedTemplate.

To avoid copy/paste, this commit also factors out and unifies dumminess
of EmptyTree, emptyValDef and pendingSuperCall - they all don't have a
position and actively refuse to gain one, same story for tpe.
  • Loading branch information
xeno-by committed Dec 6, 2012
1 parent 40063b0 commit 0ebf72b
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 98 deletions.
4 changes: 3 additions & 1 deletion src/compiler/scala/reflect/reify/codegen/GenTrees.scala
Expand Up @@ -45,7 +45,9 @@ trait GenTrees {
case global.EmptyTree =>
reifyMirrorObject(EmptyTree)
case global.emptyValDef =>
mirrorBuildSelect(nme.emptyValDef)
mirrorSelect(nme.emptyValDef)
case global.pendingSuperCall =>
mirrorSelect(nme.pendingSuperCall)
case FreeDef(_, _, _, _, _) =>
reifyNestedFreeDef(tree)
case FreeRef(_, _) =>
Expand Down
3 changes: 0 additions & 3 deletions src/compiler/scala/reflect/reify/codegen/GenUtils.scala
Expand Up @@ -34,9 +34,6 @@ trait GenUtils {
def mirrorSelect(name: String): Tree =
termPath(nme.UNIVERSE_PREFIX + name)

def mirrorBuildSelect(name: String): Tree =
termPath(nme.UNIVERSE_BUILD_PREFIX + name)

def mirrorMirrorSelect(name: String): Tree =
termPath(nme.MIRROR_PREFIX + name)

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/Positions.scala
Expand Up @@ -20,7 +20,7 @@ trait Positions extends scala.reflect.internal.Positions {
// When we prune due to encountering a position, traverse the
// pruned children so we can warn about those lacking positions.
t.children foreach { c =>
if ((c eq EmptyTree) || (c eq emptyValDef)) ()
if (c.isDummy) ()
else if (c.pos == NoPosition) {
reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase)
inform("parent: " + treeSymStatus(t))
Expand Down
23 changes: 15 additions & 8 deletions src/compiler/scala/tools/nsc/ast/Trees.scala
Expand Up @@ -65,6 +65,13 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>

// --- factory methods ----------------------------------------------------------

/** Factory method for a primary constructor super call `super.<init>(args_1)...(args_n)`
*/
def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match {
case Nil => Apply(gen.mkSuperSelect, Nil)
case xs :: rest => rest.foldLeft(Apply(gen.mkSuperSelect, xs): Tree)(Apply.apply)
}

/** Generates a template with constructor corresponding to
*
* constrmods (vparams1_) ... (vparams_n) preSuper { presupers }
Expand Down Expand Up @@ -117,12 +124,12 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit)
vparamss1 = List() :: vparamss1;
val superRef: Tree = atPos(superPos)(gen.mkSuperSelect)
val superCall = Apply(superRef, Nil) // we can't know in advance which of the parents will end up as a superclass
// this requires knowing which of the parents is a type macro and which is not
// and that's something that cannot be found out before typer
// (the type macros aren't in the trunk yet, but there is a plan for them to land there soon)
// this means that we don't know what will be the arguments of the super call
// therefore here we emit a dummy which gets populated when the template is named and typechecked
val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass
// this requires knowing which of the parents is a type macro and which is not
// and that's something that cannot be found out before typer
// (the type macros aren't in the trunk yet, but there is a plan for them to land there soon)
// this means that we don't know what will be the arguments of the super call
// therefore here we emit a dummy which gets populated when the template is named and typechecked
List(
// TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)`
// is it going to be a problem that we can no longer include the `argss`?
Expand Down Expand Up @@ -330,6 +337,8 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
else
super.transform {
tree match {
case tree if tree.isDummy =>
tree
case tpt: TypeTree =>
if (tpt.original != null)
transform(tpt.original)
Expand All @@ -343,8 +352,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
transform(fn)
case This(_) if tree.symbol != null && tree.symbol.isPackageClass =>
tree
case EmptyTree =>
tree
case _ =>
val dupl = tree.duplicate
if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel))
Expand Down
12 changes: 7 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
Expand Up @@ -21,12 +21,14 @@ trait StdAttachments {
*/
case class SuperCallArgsAttachment(argss: List[List[Tree]])

/** Extractor for `SuperCallArgsAttachment`.
/** Convenience method for `SuperCallArgsAttachment`.
* Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern,
* so it really benefits from a dedicated extractor.
*/
object CarriesSuperCallArgs {
def unapply(tree: Tree): Option[List[List[Tree]]] =
tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss }
}
def superCallArgs(tree: Tree): Option[List[List[Tree]]] =
tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss }

/** Determines whether the given tree has an associated SuperCallArgsAttachment.
*/
def hasSuperArgs(tree: Tree): Boolean = superCallArgs(tree).nonEmpty
}
103 changes: 40 additions & 63 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -52,8 +52,10 @@ trait Typers extends Modes with Adaptations with Tags {

object UnTyper extends Traverser {
override def traverse(tree: Tree) = {
if (tree != EmptyTree) tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
if (!tree.isDummy) {
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
super.traverse(tree)
}
}
Expand Down Expand Up @@ -1561,7 +1563,7 @@ trait Typers extends Modes with Adaptations with Tags {
var supertpt = typedTypeConstructor(decodedtpt)
val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else Nil
if (supertparams.nonEmpty) {
typedPrimaryConstrBody(templ) { superRef =>
typedPrimaryConstrBody(templ) {
val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK)))
val supercall = New(supertpe, mmap(argss)(_.duplicate))
val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall
Expand All @@ -1580,8 +1582,8 @@ trait Typers extends Modes with Adaptations with Tags {
}

/** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template.
* Before commencing the typecheck applies `superCallTransform` to a super call (if the latter exists).
* The transform can return `EmptyTree`, in which case the super call is replaced with a literal unit.
* Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`.
* `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit.
*
* ***Return value and side effects***
*
Expand All @@ -1606,42 +1608,22 @@ trait Typers extends Modes with Adaptations with Tags {
* Block(List(
* ValDef(NoMods, x, TypeTree(), 2)
* ValDef(NoMods, y, TypeTree(), 4)
* Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()),
* global.pendingSuperCall,
* Literal(Constant(())))
*
* Note the Select(Super(_, _), nme.CONSTRUCTOR) part. This is the representation of
* a fill-me-in-later supercall dummy. The argss are Nil, which encodes the fact
* that supercall argss are unknown during parsing and need to be transplanted from one of the parent types.
* Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`.
*
* The entire Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()) is a dummy,
* and it's the one and only possible representation that can be emitted by parser.
*
* Despite of being unwieldy, this tree is quite convenient because:
* * It works as is for the case when no processing is required (empty ctor args for the superclass)
* * Stripping off the Apply produces a core that only needs rewrapping with applications of actual argss.
*
* For some time I was thinking of using just Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)),
* but that one required wrapping even if the superclass doesn't take any argss.
*
* Another option would be to introduce a singleton tree akin to `emptyValDef` and use it as a dummy.
* Unfortunately this won't work out of the box, because the Super part is supposed to get attributed
* during `typedPrimaryConstrBody`.
*
* We could introduce another attachment for that or change SuperCallArgsAttachment
* to accommodate for the attributed Super, and then using the attached info to adjust the primary constructor
* during typedTemplate. However, given the scope of necessary changes (beyond a few lines) and the fact that,
* according to Martin, the whole thing is to be rewritten soon, I'd say we don't do the follow-up refactoring.
* Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy,
* which encodes the fact that supercall argss are unknown during parsing and need to be transplanted
* from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`.
*/
private def typedPrimaryConstrBody(templ: Template)(superCallTransform: Tree => Tree): Tree =
private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree =
treeInfo.firstConstructor(templ.body) match {
case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) =>
val (preSuperStats, superCall) = {
val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x))
(stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate)
}
val superCall1 = (superCall match {
case Apply(superRef @ Select(Super(_, _), nme.CONSTRUCTOR), Nil) => superCallTransform(superRef)
case global.pendingSuperCall => actualSuperCall
case EmptyTree => EmptyTree
}) orElse cunit
val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1)
Expand Down Expand Up @@ -1721,7 +1703,7 @@ trait Typers extends Modes with Adaptations with Tags {
// and therefore early fields have their type trees not assigned
// here we detect this situation and take preventive measures
if (treeInfo.hasUntypedPreSuperFields(templ.body))
typedPrimaryConstrBody(templ)(superRef => EmptyTree)
typedPrimaryConstrBody(templ)(EmptyTree)

supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt))
} catch {
Expand Down Expand Up @@ -1979,6 +1961,8 @@ trait Typers extends Modes with Adaptations with Tags {
validateParentClasses(parents1, selfType)
if (clazz.isCase)
validateNoCaseAncestor(clazz)
if (clazz.isTrait && hasSuperArgs(parents1.head))
ConstrArgsInParentOfTraitError(parents1.head, clazz)

if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass)
unit.error(clazz.pos, "inner classes cannot be classfile annotations")
Expand All @@ -1990,23 +1974,16 @@ trait Typers extends Modes with Adaptations with Tags {
val body =
if (isPastTyper || reporter.hasErrors) templ.body
else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
parents1.head match {
case CarriesSuperCallArgs(argss) =>
if (clazz.isTrait) {
ConstrArgsInParentOfTraitError(parents1.head, clazz)
body
} else {
val primaryCtor = treeInfo.firstConstructor(templ.body)
val primaryCtor1 = (deriveDefDef(primaryCtor) {
case block @ Block(earlyVals :+ Apply(superRef, Nil), unit) =>
val pos = wrappingPos(parents1.head.pos, argss.flatten)
val superCall = atPos(pos)((superRef /: argss)(Apply.apply))
Block(earlyVals :+ superCall, unit) setPos pos
}) setPos pos
body map { case `primaryCtor` => primaryCtor1; case stat => stat }
}
case _ => body
val primaryCtor = treeInfo.firstConstructor(body)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
val argss = superCallArgs(parents1.head) getOrElse Nil
val pos = wrappingPos(parents1.head.pos, argss.flatten)
val superCall = atPos(pos)(PrimarySuperCall(argss))
deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
case _ => primaryCtor
}
body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
}

val body1 = typedStats(body, templ.symbol)
Expand Down Expand Up @@ -5752,21 +5729,21 @@ trait Typers extends Modes with Adaptations with Tags {
// enough to see those. See #3938
ConstructorPrefixError(tree, restpe)
} else {
//@M fix for #2208
// if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef
if (result.tpe.typeArgs.isEmpty) {
// minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) {
// must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not
// designed to deal with the cycles in the scala package (ScalaObject extends
// AnyRef, but the AnyRef type alias is entered after the scala package is
// loaded and completed, so that ScalaObject is unpickled while AnyRef is not
// yet defined )
// !!! TODO - revisit now that ScalaObject is gone.
result setType(restpe)
} else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208
// during uncurry (after refchecks), all types are normalized
result
}
//@M fix for #2208
// if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef
if (result.tpe.typeArgs.isEmpty) {
// minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) {
// must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not
// designed to deal with the cycles in the scala package (ScalaObject extends
// AnyRef, but the AnyRef type alias is entered after the scala package is
// loaded and completed, so that ScalaObject is unpickled while AnyRef is not
// yet defined )
// !!! TODO - revisit now that ScalaObject is gone.
result setType(restpe)
} else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208
// during uncurry (after refchecks), all types are normalized
result
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/reflect/scala/reflect/api/BuildUtils.scala
Expand Up @@ -59,8 +59,6 @@ private[reflect] trait BuildUtils { self: Universe =>

def flagsFromBits(bits: Long): FlagSet

def emptyValDef: ValDef

def This(sym: Symbol): Tree

def Select(qualifier: Tree, sym: Symbol): Select
Expand Down
9 changes: 9 additions & 0 deletions src/reflect/scala/reflect/api/Trees.scala
Expand Up @@ -2366,6 +2366,15 @@ trait Trees { self: Universe =>
*/
val emptyValDef: ValDef

/** An empty superclass constructor call corresponding to:
* super.<init>()
* This is used as a placeholder in the primary constructor body in class templates
* to denote the insertion point of a call to superclass constructor after the typechecker
* figures out the superclass of a given template.
* @group Trees
*/
val pendingSuperCall: Apply

// ---------------------- factories ----------------------------------------------

/** A factory method for `ClassDef` nodes.
Expand Down
2 changes: 0 additions & 2 deletions src/reflect/scala/reflect/internal/BuildUtils.scala
Expand Up @@ -47,8 +47,6 @@ trait BuildUtils { self: SymbolTable =>

def flagsFromBits(bits: Long): FlagSet = bits

def emptyValDef: ValDef = self.emptyValDef

def This(sym: Symbol): Tree = self.This(sym)

def Select(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym)
Expand Down
2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/internal/Importers.scala
Expand Up @@ -334,6 +334,8 @@ trait Importers extends api.Importers { self: SymbolTable =>
new ModuleDef(importModifiers(mods), importName(name).toTermName, importTemplate(impl))
case from.emptyValDef =>
emptyValDef
case from.pendingSuperCall =>
pendingSuperCall
case from.ValDef(mods, name, tpt, rhs) =>
new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs))
case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Positions.scala
Expand Up @@ -38,7 +38,7 @@ trait Positions extends api.Positions { self: SymbolTable =>
protected class DefaultPosAssigner extends PosAssigner {
var pos: Position = _
override def traverse(t: Tree) {
if (t eq EmptyTree) ()
if (t.isDummy) ()
else if (t.pos == NoPosition) {
t.setPos(pos)
super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if?
Expand Down
4 changes: 3 additions & 1 deletion src/reflect/scala/reflect/internal/Printers.scala
Expand Up @@ -542,8 +542,10 @@ trait Printers extends api.Printers { self: SymbolTable =>
print(")")
case EmptyTree =>
print("EmptyTree")
case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef =>
case self.emptyValDef =>
print("emptyValDef")
case self.pendingSuperCall =>
print("pendingSuperCall")
case tree: Tree =>
val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol
val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString
Expand Down
1 change: 1 addition & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -730,6 +730,7 @@ trait StdNames {
val null_ : NameType = "null"
val ofDim: NameType = "ofDim"
val origin: NameType = "origin"
val pendingSuperCall: NameType = "pendingSuperCall"
val prefix : NameType = "prefix"
val productArity: NameType = "productArity"
val productElement: NameType = "productElement"
Expand Down

0 comments on commit 0ebf72b

Please sign in to comment.