summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Typers.scala
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-02-05 11:53:43 +0100
committerEugene Burmako <xeno.by@gmail.com>2013-02-05 20:05:55 +0100
commitc9a0e36224b6eb2807bad0df2d5aa11bb05c8a32 (patch)
tree1d426346ba57ce6957403c232dacf1bf09635ff7 /src/compiler/scala/tools/nsc/typechecker/Typers.scala
parent570f4a46f663a8f55ce045bfde2d834bd4902f9c (diff)
downloadscala-c9a0e36224b6eb2807bad0df2d5aa11bb05c8a32.tar.gz
scala-c9a0e36224b6eb2807bad0df2d5aa11bb05c8a32.tar.bz2
scala-c9a0e36224b6eb2807bad0df2d5aa11bb05c8a32.zip
[nomaster] Revert "refactors handling of parent types"
This reverts commit 40063b0009d55ed527bf1625d99a168a8faa4124. Conflicts: src/compiler/scala/tools/nsc/ast/parser/Parsers.scala src/compiler/scala/tools/nsc/typechecker/Typers.scala
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Typers.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala396
1 files changed, 123 insertions, 273 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 45b151348b..f518712701 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1399,6 +1399,13 @@ trait Typers extends Modes with Adaptations with Tags {
if (member(qual, name) != NoSymbol) qual
else adaptToMember(qual, HasMember(name))
+ private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = {
+ // XXX: see about using the class's symbol....
+ enclTparams foreach (sym => context.scope.enter(sym))
+ namer.enterValueParams(vparamss)
+ typed(cbody)
+ }
+
private def validateNoCaseAncestor(clazz: Symbol) = {
if (!phase.erasedTypes) {
for (ancestor <- clazz.ancestors find (_.isCase)) {
@@ -1500,263 +1507,126 @@ trait Typers extends Modes with Adaptations with Tags {
unit.error(tparam.pos, "type parameter of value class may not be specialized")
}
- /** Typechecks a parent type reference.
- *
- * This typecheck is harder than it might look, because it should honor early
- * definitions and also perform type argument inference with the help of super call
- * arguments provided in `encodedtpt`.
- *
- * The method is called in batches (batch = 1 time per each parent type referenced),
- * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef
- * and once from typer, when typechecking the definition.
- *
- * ***Arguments***
- *
- * `encodedtpt` represents the parent type reference wrapped in an `Apply` node
- * which indicates value arguments (i.e. type macro arguments or super constructor call arguments)
- * If no value arguments are provided by the user, the `Apply` node is still
- * there, but its `args` will be set to `Nil`.
- * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`.
- *
- * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser.
- * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call
- * (I wrote "maybe" because trait constructors don't call super constructors).
- * This argument is synthesized by `tools.nsc.ast.Trees.Template`.
- *
- * `inMixinPosition` indicates whether the reference is not the first in the
- * list of parents (and therefore cannot be a class) or the opposite.
- *
- * ***Return value and side effects***
- *
- * Returns a `TypeTree` representing a resolved parent type.
- * If the typechecked parent reference implies non-nullary and non-empty argument list,
- * this argument list is attached to the returned value in SuperCallArgsAttachment.
- * The attachment is necessary for the subsequent typecheck to fixup a super constructor call
- * in the body of the primary constructor (see `typedTemplate` for details).
- *
- * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects
- * described in the docs of that method. It might also attribute the Super(_, _) reference
- * (if present) inside the primary constructor of `templ`.
- *
- * ***Example***
- *
- * For the following definition:
- *
- * class D extends {
- * val x = 2
- * val y = 4
- * } with B(x)(3) with C(y) with T
- *
- * this method will be called six times:
- *
- * (3 times from the namer)
- * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false)
- * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true)
- * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true)
- *
- * (3 times from the typer)
- * <the same three calls>
- */
- private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = {
- val app = treeInfo.dissectApplied(encodedtpt)
- val (treeInfo.Applied(core, targs, argss), decodedtpt) = (app, app.callee)
- val argssAreTrivial = argss == Nil || argss == ListOfNil
-
- // we cannot avoid cyclic references with `initialize` here, because when type macros arrive,
- // we'll have to check the probe for isTypeMacro anyways.
- // therefore I think it's reasonable to trade a more specific "inherits itself" error
- // for a generic, yet understandable "cyclic reference" error
- var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol
- if (probe == null) probe = NoSymbol
- probe.initialize
-
- if (probe.isTrait || inMixinPosition) {
- if (!argssAreTrivial) {
- if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe)
- else () // a class in a mixin position - this warrants an error in `validateParentClasses`
- // therefore here we do nothing, e.g. don't check that the # of ctor arguments
- // matches the # of ctor parameters or stuff like that
- }
- typedType(decodedtpt)
- } else {
- var supertpt = typedTypeConstructor(decodedtpt)
- val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else Nil
- if (supertparams.nonEmpty) {
- typedPrimaryConstrBody(templ) { superRef =>
- 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
- ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck
- atPos(supertpt.pos.focus)(supercall)
- } match {
- case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt)
- case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos.focus
+ def parentTypes(templ: Template): List[Tree] =
+ if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe)))
+ else try {
+ val clazz = context.owner
+ // Normalize supertype and mixins so that supertype is always a class, not a trait.
+ var supertpt = typedTypeConstructor(templ.parents.head)
+ val firstParent = supertpt.tpe.typeSymbol
+ var mixins = templ.parents.tail map typedType
+ // If first parent is a trait, make it first mixin and add its superclass as first parent
+ while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) {
+ val supertpt1 = typedType(supertpt)
+ if (!supertpt1.isErrorTyped) {
+ mixins = supertpt1 :: mixins
+ supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus
}
}
- // this is the place where we tell the typer what argss should be used for the super call
- // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`)
- // the super call dummy is already good enough, so we don't need to do anything
- if (argssAreTrivial) supertpt else supertpt updateAttachment SuperCallArgsAttachment(argss)
- }
- }
-
- /** 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.
- *
- * ***Return value and side effects***
- *
- * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked.
- * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`.
- *
- * As a side effect, this method attributes the underlying fields of early vals.
- * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody`
- * at least once per definition. It'd be great to disentangle this logic at some point.
- *
- * ***Example***
- *
- * For the following definition:
- *
- * class D extends {
- * val x = 2
- * val y = 4
- * } with B(x)(3) with C(y) with T
- *
- * the primary constructor of `templ` will be:
- *
- * Block(List(
- * ValDef(NoMods, x, TypeTree(), 2)
- * ValDef(NoMods, y, TypeTree(), 4)
- * Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()),
- * 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.
- */
- private def typedPrimaryConstrBody(templ: Template)(superCallTransform: Tree => 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 EmptyTree => EmptyTree
- }) orElse cunit
- val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1)
-
- val clazz = context.owner
- assert(clazz != NoSymbol, templ)
- val cscope = context.outer.makeNewScope(ctor, context.outer.owner)
- val cbody2 = { // called both during completion AND typing.
- val typer1 = newTyper(cscope)
- // XXX: see about using the class's symbol....
- clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym))
- typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate)))
- typer1.typed(cbody1)
- }
+ if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait)
+ supertpt.tpe = AnyRefClass.tpe
+
+ // Determine
+ // - supertparams: Missing type parameters from supertype
+ // - supertpe: Given supertype, polymorphic in supertparams
+ val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List()
+ var supertpe = supertpt.tpe
+ if (!supertparams.isEmpty)
+ supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK)))
+
+ // A method to replace a super reference by a New in a supercall
+ def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match {
+ case Apply(fn, args) =>
+ treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate))
+ case Select(Super(_, _), nme.CONSTRUCTOR) =>
+ treeCopy.Select(
+ scall,
+ atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe),
+ nme.CONSTRUCTOR)
+ }
- val preSuperVals = treeInfo.preSuperFields(templ.body)
- if (preSuperVals.isEmpty && preSuperStats.nonEmpty)
- debugwarn("Wanted to zip empty presuper val list with " + preSuperStats)
- else
- map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe)
+ treeInfo.firstConstructor(templ.body) match {
+ case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) =>
+ // Convert constructor body to block in environment and typecheck it
+ val (preSuperStats, superCall) = {
+ val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x))
+ (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate)
+ }
+ val cstats1 = if (superCall == EmptyTree) preSuperStats else preSuperStats :+ superCall
+ val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match {
+ case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall)
+ case _ => cunit.duplicate
+ })
+ val outercontext = context.outer
+
+ assert(clazz != NoSymbol, templ)
+ val cscope = outercontext.makeNewScope(constr, outercontext.owner)
+ val cbody2 = newTyper(cscope) // called both during completion AND typing.
+ .typePrimaryConstrBody(clazz,
+ cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate)))
+
+ superCall match {
+ case Apply(_, _) =>
+ val treeInfo.Applied(_, _, argss) = superCall
+ val sarg = argss.flatten.headOption.getOrElse(EmptyTree)
+ if (sarg != EmptyTree && supertpe.typeSymbol != firstParent)
+ ConstrArgsInTraitParentTpeError(sarg, firstParent)
+ if (!supertparams.isEmpty)
+ supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus
+ case _ =>
+ if (!supertparams.isEmpty)
+ MissingTypeArgumentsParentTpeError(supertpt)
+ }
- if (superCall1 == cunit) EmptyTree else cbody2
- case _ =>
- EmptyTree
- }
+ val preSuperVals = treeInfo.preSuperFields(templ.body)
+ if (preSuperVals.isEmpty && preSuperStats.nonEmpty)
+ debugwarn("Wanted to zip empty presuper val list with " + preSuperStats)
+ else
+ map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe)
- /** Makes sure that the first type tree in the list of parent types is always a class.
- * If the first parent is a trait, prepend its supertype to the list until it's a class.
- */
- private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match {
- case first :: rest if treeInfo.isTraitRef(first) =>
- def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = {
- if (treeInfo.isTraitRef(supertpt)) {
- val supertpt1 = typedType(supertpt)
- if (!supertpt1.isErrorTyped) {
- val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus
- return explode(supersupertpt, supertpt1 :: acc)
- }
- }
- if (supertpt.tpe.typeSymbol == AnyClass) supertpt.tpe = AnyRefClass.tpe
- supertpt :: acc
+ case _ =>
+ if (!supertparams.isEmpty)
+ MissingTypeArgumentsParentTpeError(supertpt)
}
- explode(first, Nil) ++ rest
- case _ => parents
- }
+/* experimental: early types as type arguments
+ val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef)
+ val earlyMap = new EarlyMap(clazz)
+ List.mapConserve(supertpt :: mixins){ tpt =>
+ val tpt1 = checkNoEscaping.privates(clazz, tpt)
+ if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe)
+ }
+*/
- /** Certain parents are added in the parser before it is known whether
- * that class also declared them as parents. For instance, this is an
- * error unless we take corrective action here:
- *
- * case class Foo() extends Serializable
- *
- * So we strip the duplicates before typer.
- */
- private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match {
- case Nil => Nil
- case x :: xs =>
- val sym = x.symbol
- x :: fixDuplicateSyntheticParents(
- if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
- else xs
- )
- }
+ //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG
- def parentTypes(templ: Template): List[Tree] = templ.parents match {
- case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe)))
- case first :: rest =>
- try {
- val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent(
- typedParentType(first, templ, inMixinPosition = false) +:
- (rest map (typedParentType(_, templ, inMixinPosition = true)))))
-
- // if that is required to infer the targs of a super call
- // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck
- // as a side effect, that typecheck also assigns types to the fields underlying early vals
- // however if inference is not required, the typecheck doesn't happen
- // 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)
-
- supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt))
- } catch {
- case ex: TypeError =>
- // fallback in case of cyclic errors
- // @H none of the tests enter here but I couldn't rule it out
- // upd. @E when a definitions inherits itself, we end up here
- // because `typedParentType` triggers `initialize` for parent types symbols
- log("Type error calculating parents in template " + templ)
- log("Error: " + ex)
- ParentTypesError(templ, ex)
- List(TypeTree(AnyRefClass.tpe))
+ // Certain parents are added in the parser before it is known whether
+ // that class also declared them as parents. For instance, this is an
+ // error unless we take corrective action here:
+ //
+ // case class Foo() extends Serializable
+ //
+ // So we strip the duplicates before typer.
+ def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match {
+ case Nil => Nil
+ case x :: xs =>
+ val sym = x.symbol
+ x :: fixDuplicates(
+ if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
+ else xs
+ )
}
- }
+
+ fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt))
+ }
+ catch {
+ case ex: TypeError =>
+ // fallback in case of cyclic errors
+ // @H none of the tests enter here but I couldn't rule it out
+ log("Type error calculating parents in template " + templ)
+ log("Error: " + ex)
+ ParentTypesError(templ, ex)
+ List(TypeTree(AnyRefClass.tpe))
+ }
/** <p>Check that</p>
* <ul>
@@ -2021,8 +1891,7 @@ trait Typers extends Modes with Adaptations with Tags {
// the following is necessary for templates generated later
assert(clazz.info.decls != EmptyScope, clazz)
enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body)
- if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
- validateParentClasses(parents1, selfType)
+ validateParentClasses(parents1, selfType)
if (clazz.isCase)
validateNoCaseAncestor(clazz)
@@ -2032,28 +1901,9 @@ trait Typers extends Modes with Adaptations with Tags {
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
- val body = {
- 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 pos = wrappingPos(parents1.head.pos, argss.flatten)
- val primaryCtor = treeInfo.firstConstructor(templ.body)
- val primaryCtor1 = (deriveDefDef(primaryCtor) {
- case block @ Block(earlyVals :+ Apply(superRef, Nil), unit) =>
- 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 body =
+ if (isPastTyper || reporter.hasErrors) templ.body
+ else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
val body1 = typedStats(body, templ.symbol)
@@ -2843,7 +2693,7 @@ trait Typers extends Modes with Adaptations with Tags {
members foreach (m => anonClass.info.decls enter m.symbol)
val typedBlock = typedPos(tree.pos, mode, pt) {
- Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe)))
+ Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe)))
}
if (typedBlock.isErrorTyped) typedBlock