diff options
author | Paul Phillips <paulp@improving.org> | 2011-05-16 21:11:17 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-05-16 21:11:17 +0000 |
commit | fff93cd0497916708e6a9a9207660623ed2e50ee (patch) | |
tree | 22ade6ccc3605c91c2f227ace6536fd94db13bf9 /src/compiler/scala/reflect/common | |
parent | 1e5194b41cdc5563237381b80a9f948abbf96e6e (diff) | |
download | scala-fff93cd0497916708e6a9a9207660623ed2e50ee.tar.gz scala-fff93cd0497916708e6a9a9207660623ed2e50ee.tar.bz2 scala-fff93cd0497916708e6a9a9207660623ed2e50ee.zip |
And the remainder of the scala.reflect refactor...
And the remainder of the scala.reflect refactoring (think of it like a
"balloon payment") no review.
Diffstat (limited to 'src/compiler/scala/reflect/common')
26 files changed, 15855 insertions, 92 deletions
diff --git a/src/compiler/scala/reflect/common/AnnotationCheckers.scala b/src/compiler/scala/reflect/common/AnnotationCheckers.scala new file mode 100644 index 0000000000..cd3b343238 --- /dev/null +++ b/src/compiler/scala/reflect/common/AnnotationCheckers.scala @@ -0,0 +1,120 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +/** Additions to the type checker that can be added at + * run time. Typically these are added by + * compiler plugins. */ +trait AnnotationCheckers { + self: SymbolTable => + + + /** An additional checker for annotations on types. + * Typically these are registered by compiler plugins + * with the addAnnotationChecker method. */ + abstract class AnnotationChecker { + /** Check the annotations on two types conform. */ + def annotationsConform(tpe1: Type, tpe2: Type): Boolean + + /** Refine the computed least upper bound of a list of types. + * All this should do is add annotations. */ + def annotationsLub(tp: Type, ts: List[Type]): Type = tp + + /** Refine the computed greatest lower bound of a list of types. + * All this should do is add annotations. */ + def annotationsGlb(tp: Type, ts: List[Type]): Type = tp + + /** Refine the bounds on type parameters to the given type arguments. */ + def adaptBoundsToAnnotations(bounds: List[TypeBounds], + tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = bounds + + /** Modify the type that has thus far been inferred + * for a tree. All this should do is add annotations. */ + def addAnnotations(tree: Tree, tpe: Type): Type = tpe + + /** Decide whether this annotation checker can adapt a tree + * that has an annotated type to the given type tp, taking + * into account the given mode (see method adapt in trait Typers).*/ + def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = false + + /** Adapt a tree that has an annotated type to the given type tp, + * taking into account the given mode (see method adapt in trait Typers). + * An implementation cannot rely on canAdaptAnnotations being called + * before. If the implementing class cannot do the adaptiong, it + * should return the tree unchanged.*/ + def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree + } + + /** The list of annotation checkers that have been registered */ + private var annotationCheckers: List[AnnotationChecker] = Nil + + /** Register an annotation checker. Typically these + * are added by compiler plugins. */ + def addAnnotationChecker(checker: AnnotationChecker) { + if (!(annotationCheckers contains checker)) + annotationCheckers = checker :: annotationCheckers + } + + /** Remove all annotation checkers */ + def removeAllAnnotationCheckers() { + annotationCheckers = Nil + } + + /** Check that the annotations on two types conform. To do + * so, consult all registered annotation checkers. */ + def annotationsConform(tp1: Type, tp2: Type): Boolean = { + /* Finish quickly if there are no annotations */ + if (tp1.annotations.isEmpty && tp2.annotations.isEmpty) + true + else + annotationCheckers.forall( + _.annotationsConform(tp1,tp2)) + } + + /** Refine the computed least upper bound of a list of types. + * All this should do is add annotations. */ + def annotationsLub(tpe: Type, ts: List[Type]): Type = { + annotationCheckers.foldLeft(tpe)((tpe, checker) => + checker.annotationsLub(tpe, ts)) + } + + /** Refine the computed greatest lower bound of a list of types. + * All this should do is add annotations. */ + def annotationsGlb(tpe: Type, ts: List[Type]): Type = { + annotationCheckers.foldLeft(tpe)((tpe, checker) => + checker.annotationsGlb(tpe, ts)) + } + + /** Refine the bounds on type parameters to the given type arguments. */ + def adaptBoundsToAnnotations(bounds: List[TypeBounds], + tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = { + annotationCheckers.foldLeft(bounds)((bounds, checker) => + checker.adaptBoundsToAnnotations(bounds, tparams, targs)) + } + + /** Let all annotations checkers add extra annotations + * to this tree's type. */ + def addAnnotations(tree: Tree, tpe: Type): Type = { + annotationCheckers.foldLeft(tpe)((tpe, checker) => + checker.addAnnotations(tree, tpe)) + } + + /** Find out whether any annotation checker can adapt a tree + * to a given type. Called by Typers.adapt. */ + def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { + annotationCheckers.exists(_.canAdaptAnnotations(tree, mode, pt)) + } + + /** Let registered annotation checkers adapt a tree + * to a given type (called by Typers.adapt). Annotation checkers + * that cannot do the adaption should pass the tree through + * unchanged. */ + def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = { + annotationCheckers.foldLeft(tree)((tree, checker) => + checker.adaptAnnotations(tree, mode, pt)) + } +} diff --git a/src/compiler/scala/reflect/common/AnnotationInfos.scala b/src/compiler/scala/reflect/common/AnnotationInfos.scala new file mode 100644 index 0000000000..642e2f0b22 --- /dev/null +++ b/src/compiler/scala/reflect/common/AnnotationInfos.scala @@ -0,0 +1,143 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import util._ + +/** AnnotationInfo and its helpers */ +trait AnnotationInfos /*extends reflect.generic.AnnotationInfos*/ { self: SymbolTable => + + /** Arguments to classfile annotations (which are written to + * bytecode as java annotations) are either: + * <ul> + * <li>constants</li> + * <li>arrays of constants</li> + * <li>or nested classfile annotations</li> + * </ul> + */ + abstract class ClassfileAnnotArg + + /** Represents a compile-time Constant (Boolean, Byte, Short, + * Char, Int, Long, Float, Double, String, java.lang.Class or + * an instance of a Java enumeration value). + */ + case class LiteralAnnotArg(const: Constant) + extends ClassfileAnnotArg { + override def toString = const.escapedStringValue + } + + /** Represents an array of classfile annotation arguments */ + case class ArrayAnnotArg(args: Array[ClassfileAnnotArg]) + extends ClassfileAnnotArg { + override def toString = args.mkString("[", ", ", "]") + } + + /** A specific annotation argument that encodes an array of bytes as an array of `Long`. The type of the argument + * declared in the annotation must be `String`. This specialised class is used to encode scala signatures for + * reasons of efficiency, both in term of class-file size and in term of compiler performance. */ + case class ScalaSigBytes(bytes: Array[Byte]) extends ClassfileAnnotArg { + override def toString = (bytes map { byte => (byte & 0xff).toHexString }).mkString("[ ", " ", " ]") + lazy val encodedBytes = + reflect.generic.ByteCodecs.encode(bytes) + def isLong: Boolean = (encodedBytes.length > 65535) + def sigAnnot: Type = + if (this.isLong) + definitions.ScalaLongSignatureAnnotation.tpe + else + definitions.ScalaSignatureAnnotation.tpe + } + + /** Represents a nested classfile annotation */ + case class NestedAnnotArg(annInfo: AnnotationInfo) + extends ClassfileAnnotArg { + // The nested annotation should not have any Scala annotation arguments + assert(annInfo.args.isEmpty, annInfo.args) + override def toString = annInfo.toString + } + + class AnnotationInfoBase + + /** <p> + * Typed information about an annotation. It can be attached to + * either a symbol or an annotated type. + * </p> + * <p> + * Annotations are written to the classfile as java annotations + * if <code>atp</code> conforms to <code>ClassfileAnnotation</code> + * (the classfile parser adds this interface to any Java annotation + * class). + * </p> + * <p> + * Annotations are pickled (written to scala symtab attribute + * in the classfile) if <code>atp</code> inherits form + * <code>StaticAnnotation</code>. + * </p> + * <p> + * <code>args</code> stores arguments to Scala annotations, + * represented as typed trees. Note that these trees are not + * transformed by any phases following the type-checker. + * </p> + * <p> + * <code>assocs</code> stores arguments to classfile annotations + * as name-value pairs. + * </p> + */ + case class AnnotationInfo(atp: Type, args: List[Tree], + assocs: List[(Name, ClassfileAnnotArg)]) + extends AnnotationInfoBase { + + // Classfile annot: args empty. Scala annot: assocs empty. + assert(args.isEmpty || assocs.isEmpty) + + private var rawpos: Position = NoPosition + def pos = rawpos + def setPos(pos: Position): this.type = { + rawpos = pos + this + } + + lazy val isTrivial: Boolean = atp.isTrivial && !(args exists (_.exists(_.isInstanceOf[This]))) // see annotationArgRewriter + + override def toString: String = atp + + (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + + (if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "") + + /** Check whether the type or any of the arguments are erroneous */ + def isErroneous = atp.isErroneous || args.exists(_.isErroneous) + + /** Check whether any of the arguments mention a symbol */ + def refsSymbol(sym: Symbol) = + args.exists(_.exists(_.symbol == sym)) + + /** Change all ident's with Symbol "from" to instead use symbol "to" */ + def substIdentSyms(from: Symbol, to: Symbol) = { + val subs = new TreeSymSubstituter(List(from), List(to)) + AnnotationInfo(atp, args.map(subs(_)), assocs).setPos(pos) + } + + // !!! when annotation arguments are not literal strings, but any sort of + // assembly of strings, there is a fair chance they will turn up here not as + // Literal(const) but some arbitrary AST. + def stringArg(index: Int): Option[String] = if(args.size > index) Some(args(index) match { + case Literal(const) => const.stringValue + case x => x.toString // should not be necessary, but better than silently ignoring an issue + }) else None + + def intArg(index: Int): Option[Int] = if(args.size > index) Some(args(index)) collect { + case Literal(Constant(x: Int)) => x + } else None + } + + lazy val classfileAnnotArgManifest: ClassManifest[ClassfileAnnotArg] = + reflect.ClassManifest.classType(classOf[ClassfileAnnotArg]) + + /** Symbol annotations parsed in Namer (typeCompleter of + * definitions) have to be lazy (#1782) + */ + case class LazyAnnotationInfo(annot: () => AnnotationInfo) + extends AnnotationInfoBase +} diff --git a/src/compiler/scala/reflect/common/BaseTypeSeqs.scala b/src/compiler/scala/reflect/common/BaseTypeSeqs.scala new file mode 100644 index 0000000000..7a77ca8e62 --- /dev/null +++ b/src/compiler/scala/reflect/common/BaseTypeSeqs.scala @@ -0,0 +1,251 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect +package common + +// todo implement in terms of BitSet +import scala.collection.mutable.{ListBuffer, BitSet} +import math.max +import util.Statistics._ + +/** A base type sequence (BaseTypeSeq) is an ordered sequence spanning all the base types + * of a type. It characterized by the following two laws: + * + * (1) Each element of `tp.baseTypeSeq' is a basetype of `tp' + * (2) For each basetype `bt1' of `tp' there is an element `bt' in `tp.baseTypeSeq' such that + * + * bt.typeSymbol = bt1.typeSymbol + * bt <: bt1 + * + * (3) The type symbols of different elements are different. + * + * Elements in the sequence are ordered by Symbol.isLess. + * @note base type sequences were called closures up to 2.7.1. The name has been changed + * to avoid confusion with function closures. + */ +trait BaseTypeSeqs { + this: SymbolTable => + import definitions._ + + class BaseTypeSeq(parents: List[Type], elems: Array[Type]) { + self => + incCounter(baseTypeSeqCount) + incCounter(baseTypeSeqLenTotal, elems.length) + + /** The number of types in the sequence */ + def length: Int = elems.length + + // #3676 shows why we can't store NoType in elems to mark cycles + // (while NoType is in there to indicate a cycle in this BTS, during the execution of + // the mergePrefixAndArgs below, the elems get copied without the pending map, + // so that NoType's are seen instead of the original type --> spurious compile error) + val pending = new BitSet(length) + + /** The type at i'th position in this sequence; lazy types are returned evaluated. */ + def apply(i: Int): Type = + if(pending contains i) { + pending.clear() + throw CyclicInheritance + } else + elems(i) match { + case rtp @ RefinedType(variants, decls) => + // can't assert decls.isEmpty; see t0764 + //if (!decls.isEmpty) assert(false, "computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j)) + //Console.println("compute closure of "+this+" => glb("+variants+")") + pending += i + try { + mergePrefixAndArgs(variants, -1, lubDepth(variants)) match { + case Some(tp0) => + pending(i) = false + elems(i) = tp0 + tp0 + case None => + typeError( + "no common type instance of base types "+(variants mkString ", and ")+" exists.") + } + } catch { + case CyclicInheritance => + typeError( + "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.") + } + case tp => + tp + } + + def rawElem(i: Int) = elems(i) + + /** The type symbol of the type at i'th position in this sequence; + * no evaluation needed. + */ + def typeSymbol(i: Int): Symbol = { + elems(i) match { + case RefinedType(v :: vs, _) => v.typeSymbol + case tp => tp.typeSymbol + } + } + + /** Return all evaluated types in this sequence as a list */ + def toList: List[Type] = elems.toList + + protected def copy(head: Type, offset: Int): BaseTypeSeq = { + val arr = new Array[Type](elems.length + offset) + compat.Platform.arraycopy(elems, 0, arr, offset, elems.length) + arr(0) = head + new BaseTypeSeq(parents, arr) + } + + /** Compute new base type sequence with `tp' prepended to this sequence */ + def prepend(tp: Type): BaseTypeSeq = copy(tp, 1) + + /** Compute new base type sequence with `tp' replacing the head of this sequence */ + def updateHead(tp: Type): BaseTypeSeq = copy(tp, 0) + + /** Compute new base type sequence where every element is mapped + * with function `f'. Lazy types are mapped but not evaluated */ + def map(f: Type => Type): BaseTypeSeq = { + // inlined `elems map f' for performance + val len = length + var arr = new Array[Type](len) + var i = 0 + while (i < len) { + arr(i) = f(elems(i)) + i += 1 + } + new BaseTypeSeq(parents, arr) + } + + def lateMap(f: Type => Type): BaseTypeSeq = new BaseTypeSeq(parents map f, elems) { + override def apply(i: Int) = f(self.apply(i)) + override def rawElem(i: Int) = f(self.rawElem(i)) + override def typeSymbol(i: Int) = self.typeSymbol(i) + override def toList = self.toList map f + override protected def copy(head: Type, offset: Int) = (self map f).copy(head, offset) + override def map(g: Type => Type) = lateMap(g) + override def lateMap(g: Type => Type) = self.lateMap(x => g(f(x))) + override def exists(p: Type => Boolean) = elems exists (x => p(f(x))) + override protected def maxDepthOfElems: Int = elems map (x => maxDpth(f(x))) max + override def toString = elems.mkString("MBTS(", ",", ")") + } + + def exists(p: Type => Boolean): Boolean = elems exists p + + lazy val maxDepth: Int = maxDepthOfElems + + protected def maxDepthOfElems = { + var d = 0 + for (i <- 0 until length) d = max(d, maxDpth(elems(i))) + d + } + + /** The maximum depth of type `tp' */ + protected def maxDpth(tp: Type): Int = tp match { + case TypeRef(pre, sym, args) => + max(maxDpth(pre), maxDpth(args) + 1) + case RefinedType(parents, decls) => + max(maxDpth(parents), maxDpth(decls.toList.map(_.info)) + 1) + case TypeBounds(lo, hi) => + max(maxDpth(lo), maxDpth(hi)) + case MethodType(paramtypes, result) => + maxDpth(result) + case NullaryMethodType(result) => + maxDpth(result) + case PolyType(tparams, result) => + max(maxDpth(result), maxDpth(tparams map (_.info)) + 1) + case ExistentialType(tparams, result) => + max(maxDpth(result), maxDpth(tparams map (_.info)) + 1) + case _ => + 1 + } + + /** The maximum depth of all types `tps' */ + private def maxDpth(tps: Seq[Type]): Int = { + var d = 0 + for (tp <- tps) d = max(d, maxDpth(tp)) + d + } + + override def toString = elems.mkString("BTS(", ",", ")") + + private def typeError(msg: String): Nothing = + throw new TypeError( + "the type intersection "+(parents mkString " with ")+" is malformed"+ + "\n --- because ---\n"+msg) + } + + /** A merker object for a base type sequence that's no yet computed. + * used to catch inheritance cycles + */ + val undetBaseTypeSeq: BaseTypeSeq = new BaseTypeSeq(List(), Array()) + + /** Create a base type sequence consisting of a single type */ + def baseTypeSingletonSeq(tp: Type): BaseTypeSeq = new BaseTypeSeq(List(), Array(tp)) + + /** Create the base type sequence of a compound type wuth given tp.parents */ + def compoundBaseTypeSeq(tp: Type): BaseTypeSeq = { + val tsym = tp.typeSymbol + val parents = tp.parents +// Console.println("computing baseTypeSeq of " + tsym.tpe + " " + parents)//DEBUG + val buf = new ListBuffer[Type] + buf += tsym.tpe + var btsSize = 1 + if (parents.nonEmpty) { + val nparents = parents.length + val pbtss = new Array[BaseTypeSeq](nparents) + val index = new Array[Int](nparents) + var i = 0 + for (p <- parents) { + pbtss(i) = + if (p.baseTypeSeq eq undetBaseTypeSeq) AnyClass.info.baseTypeSeq + else p.baseTypeSeq + index(i) = 0 + i += 1 + } + def nextTypeSymbol(i: Int): Symbol = { + val j = index(i) + val pbts = pbtss(i) + if (j < pbts.length) pbts.typeSymbol(j) else AnyClass + } + def nextRawElem(i: Int): Type = { + val j = index(i) + val pbts = pbtss(i) + if (j < pbts.length) pbts.rawElem(j) else AnyClass.tpe + } + var minSym: Symbol = NoSymbol + while (minSym != AnyClass) { + minSym = nextTypeSymbol(0) + i = 1 + while (i < nparents) { + val nextSym = nextTypeSymbol(i) + if (nextSym isLess minSym) + minSym = nextSym + i += 1 + } + var minTypes: List[Type] = List() + i = 0 + while (i < nparents) { + if (nextTypeSymbol(i) == minSym) { + nextRawElem(i) match { + case RefinedType(variants, decls) => + for (tp <- variants) + if (!(minTypes exists (tp =:=))) minTypes = tp :: minTypes + case tp => + if (!(minTypes exists (tp =:=))) minTypes = tp :: minTypes + } + index(i) = index(i) + 1 + } + i += 1 + } + buf += intersectionType(minTypes) + btsSize += 1 + } + } + val elems = new Array[Type](btsSize) + buf.copyToArray(elems, 0) +// Console.println("computed baseTypeSeq of " + tsym.tpe + " " + parents + ": "+elems.toString)//DEBUG + new BaseTypeSeq(parents, elems) + } + + val CyclicInheritance = new Throwable +} diff --git a/src/compiler/scala/reflect/common/Caches.scala b/src/compiler/scala/reflect/common/Caches.scala new file mode 100644 index 0000000000..2666761f92 --- /dev/null +++ b/src/compiler/scala/reflect/common/Caches.scala @@ -0,0 +1,103 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect +package common + +import scala.collection.{ mutable, immutable } + +/** A cache for some entity whose validity depends on a monotonically + * increasing sequence number. + */ +abstract class SequencedCache[T >: Null] { + def zero: Int + def sequenceId: Int + def calculate(): T + + /** If sequence numbers differ, this condition is consulted before + * updating the cached value. + */ + def isCacheValid: Boolean + + /** Public so accesses can be inlined. */ + @inline var cachedId: Int = 0 + @inline var cachedValue: T = _ + + /** Puts cache back in uninitialized state. */ + @inline final def clear() = { + cachedId = zero + cachedValue = null + } + /** Resets the sequence id without touching the cached value. */ + @inline final def reset() = { + cachedId = zero + } + + final def get(): T = { + if (cachedValue == null) { + cachedValue = calculate() + cachedId = sequenceId + } + else if (cachedId != sequenceId) { + if (!isCacheValid) + cachedValue = calculate() + + cachedId = sequenceId + } + cachedValue + } +} + +trait Caches { + self: SymbolTable => + + final def isValid(period: Period): Boolean = + period != 0 && runId(period) == currentRunId && { + val pid = phaseId(period) + if (phase.id > pid) infoTransformers.nextFrom(pid).pid >= phase.id + else infoTransformers.nextFrom(phase.id).pid >= pid + } + + final def isValidForBaseClasses(period: Period): Boolean = { + def noChangeInBaseClasses(it: InfoTransformer, limit: Phase#Id): Boolean = ( + it.pid >= limit || + !it.changesBaseClasses && noChangeInBaseClasses(it.next, limit) + ); + period != 0 && runId(period) == currentRunId && { + val pid = phaseId(period) + if (phase.id > pid) noChangeInBaseClasses(infoTransformers.nextFrom(pid), phase.id) + else noChangeInBaseClasses(infoTransformers.nextFrom(phase.id), pid) + } + } + + abstract class PeriodCache[T >: Null] extends SequencedCache[T] { + final val zero = NoPeriod + @inline final def sequenceId = currentPeriod + } + + abstract class ListOfTypesCache extends PeriodCache[List[Type]] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class ListOfSymbolsCache extends PeriodCache[List[Symbol]] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class BaseTypeSeqCache extends PeriodCache[BaseTypeSeq] { + @inline final def isCacheValid = isValidForBaseClasses(cachedId) + } + abstract class TypeCache extends PeriodCache[Type] { + @inline final def isCacheValid = isValid(cachedId) + } + abstract class TypeCacheForRunId extends SequencedCache[Type] { + final val zero = NoRunId + @inline final def sequenceId = currentRunId + @inline final override def isCacheValid = false + } + object TypeCache { + def apply(body: => Type): TypeCache = new TypeCache { + @inline final def calculate() = body + } + } +} + diff --git a/src/compiler/scala/reflect/common/ClassfileConstants.scala b/src/compiler/scala/reflect/common/ClassfileConstants.scala new file mode 100644 index 0000000000..a48b16be96 --- /dev/null +++ b/src/compiler/scala/reflect/common/ClassfileConstants.scala @@ -0,0 +1,332 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +object ClassfileConstants { + + final val JAVA_MAGIC = 0xCAFEBABE + final val JAVA_MAJOR_VERSION = 45 + final val JAVA_MINOR_VERSION = 3 + + /** <p> + * (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html) + * </p> + * <p> + * If the <code>ACC_INTERFACE</code> flag is set, the <code>ACC_ABSTRACT</code> + * flag must also be set (ch. 2.13.1). + * </p> + * <p> + * A class file cannot have both its <code>ACC_FINAL</code> and + * <code>ACC_ABSTRACT</code> flags set (ch. 2.8.2). + * </p> + * <p> + * A field may have at most one of its <code>ACC_PRIVATE</code>, + * <code>ACC_PROTECTED</code>, <code>ACC_PUBLIC</code> flags set (ch. 2.7.4). + * </p> + * <p> + * A field may not have both its <code>ACC_FINAL</code> and + * <code>ACC_VOLATILE</code> flags set (ch. 2.9.1). + * </p> + * <p> + * If a method has its <code>ACC_ABSTRACT</code> flag set it must not + * have any of its <code>ACC_FINAL</code>, <code>ACC_NATIVE</code>, + * <code>ACC_PRIVATE</code>, <code>ACC_STATIC</code>, <code>ACC_STRICT</code>, + * or <code>ACC_SYNCHRONIZED</code> flags set (ch. 2.13.3.2). + * </p> + * <p> + * All interface methods must have their <code>ACC_ABSTRACT</code> and + * <code>ACC_PUBLIC</code> flags set. + * </p> + */ // Class Field Method + final val JAVA_ACC_PUBLIC = 0x0001 // X X X + final val JAVA_ACC_PRIVATE = 0x0002 // X X + final val JAVA_ACC_PROTECTED = 0x0004 // X X + final val JAVA_ACC_STATIC = 0x0008 // X X + final val JAVA_ACC_FINAL = 0x0010 // X X X + final val JAVA_ACC_SUPER = 0x0020 // X + final val JAVA_ACC_SYNCHRONIZED = 0x0020 // X + final val JAVA_ACC_VOLATILE = 0x0040 // X + final val JAVA_ACC_BRIDGE = 0x0040 // X + final val JAVA_ACC_TRANSIENT = 0x0080 // X + final val JAVA_ACC_VARARGS = 0x0080 // X + final val JAVA_ACC_NATIVE = 0x0100 // X + final val JAVA_ACC_INTERFACE = 0x0200 // X + final val JAVA_ACC_ABSTRACT = 0x0400 // X X + final val JAVA_ACC_STRICT = 0x0800 // X + final val JAVA_ACC_SYNTHETIC = 0x1000 // X X X + final val JAVA_ACC_ANNOTATION = 0x2000 // X + final val JAVA_ACC_ENUM = 0x4000 // X X + + // tags describing the type of a literal in the constant pool + final val CONSTANT_UTF8 = 1 + final val CONSTANT_UNICODE = 2 + final val CONSTANT_INTEGER = 3 + final val CONSTANT_FLOAT = 4 + final val CONSTANT_LONG = 5 + final val CONSTANT_DOUBLE = 6 + final val CONSTANT_CLASS = 7 + final val CONSTANT_STRING = 8 + final val CONSTANT_FIELDREF = 9 + final val CONSTANT_METHODREF = 10 + final val CONSTANT_INTFMETHODREF = 11 + final val CONSTANT_NAMEANDTYPE = 12 + + // tags describing the type of a literal in attribute values + final val BYTE_TAG = 'B' + final val CHAR_TAG = 'C' + final val DOUBLE_TAG = 'D' + final val FLOAT_TAG = 'F' + final val INT_TAG = 'I' + final val LONG_TAG = 'J' + final val SHORT_TAG = 'S' + final val BOOL_TAG = 'Z' + final val STRING_TAG = 's' + final val ENUM_TAG = 'e' + final val CLASS_TAG = 'c' + final val ARRAY_TAG = '[' + final val VOID_TAG = 'V' + final val TVAR_TAG = 'T' + final val ANNOTATION_TAG = '@' + final val SCALA_NOTHING = "scala.runtime.Nothing$" + final val SCALA_NULL = "scala.runtime.Null$" + + + // tags describing the type of newarray + final val T_BOOLEAN = 4 + final val T_CHAR = 5 + final val T_FLOAT = 6 + final val T_DOUBLE = 7 + final val T_BYTE = 8 + final val T_SHORT = 9 + final val T_INT = 10 + final val T_LONG = 11 + + // JVM mnemonics + final val nop = 0x00 + final val aconst_null = 0x01 + final val iconst_m1 = 0x02 + + final val iconst_0 = 0x03 + final val iconst_1 = 0x04 + final val iconst_2 = 0x05 + final val iconst_3 = 0x06 + final val iconst_4 = 0x07 + final val iconst_5 = 0x08 + + final val lconst_0 = 0x09 + final val lconst_1 = 0x0a + final val fconst_0 = 0x0b + final val fconst_1 = 0x0c + final val fconst_2 = 0x0d + final val dconst_0 = 0x0e + final val dconst_1 = 0x0f + + final val bipush = 0x10 + final val sipush = 0x11 + final val ldc = 0x12 + final val ldc_w = 0x13 + final val ldc2_w = 0x14 + + final val iload = 0x15 + final val lload = 0x16 + final val fload = 0x17 + final val dload = 0x18 + final val aload = 0x19 + + final val iload_0 = 0x1a + final val iload_1 = 0x1b + final val iload_2 = 0x1c + final val iload_3 = 0x1d + final val lload_0 = 0x1e + final val lload_1 = 0x1f + final val lload_2 = 0x20 + final val lload_3 = 0x21 + final val fload_0 = 0x22 + final val fload_1 = 0x23 + final val fload_2 = 0x24 + final val fload_3 = 0x25 + final val dload_0 = 0x26 + final val dload_1 = 0x27 + final val dload_2 = 0x28 + final val dload_3 = 0x29 + final val aload_0 = 0x2a + final val aload_1 = 0x2b + final val aload_2 = 0x2c + final val aload_3 = 0x2d + final val iaload = 0x2e + final val laload = 0x2f + final val faload = 0x30 + final val daload = 0x31 + final val aaload = 0x32 + final val baload = 0x33 + final val caload = 0x34 + final val saload = 0x35 + + final val istore = 0x36 + final val lstore = 0x37 + final val fstore = 0x38 + final val dstore = 0x39 + final val astore = 0x3a + final val istore_0 = 0x3b + final val istore_1 = 0x3c + final val istore_2 = 0x3d + final val istore_3 = 0x3e + final val lstore_0 = 0x3f + final val lstore_1 = 0x40 + final val lstore_2 = 0x41 + final val lstore_3 = 0x42 + final val fstore_0 = 0x43 + final val fstore_1 = 0x44 + final val fstore_2 = 0x45 + final val fstore_3 = 0x46 + final val dstore_0 = 0x47 + final val dstore_1 = 0x48 + final val dstore_2 = 0x49 + final val dstore_3 = 0x4a + final val astore_0 = 0x4b + final val astore_1 = 0x4c + final val astore_2 = 0x4d + final val astore_3 = 0x4e + final val iastore = 0x4f + final val lastore = 0x50 + final val fastore = 0x51 + final val dastore = 0x52 + final val aastore = 0x53 + final val bastore = 0x54 + final val castore = 0x55 + final val sastore = 0x56 + + final val pop = 0x57 + final val pop2 = 0x58 + final val dup = 0x59 + final val dup_x1 = 0x5a + final val dup_x2 = 0x5b + final val dup2 = 0x5c + final val dup2_x1 = 0x5d + final val dup2_x2 = 0x5e + final val swap = 0x5f + + final val iadd = 0x60 + final val ladd = 0x61 + final val fadd = 0x62 + final val dadd = 0x63 + final val isub = 0x64 + final val lsub = 0x65 + final val fsub = 0x66 + final val dsub = 0x67 + final val imul = 0x68 + final val lmul = 0x69 + final val fmul = 0x6a + final val dmul = 0x6b + final val idiv = 0x6c + final val ldiv = 0x6d + final val fdiv = 0x6e + final val ddiv = 0x6f + final val irem = 0x70 + final val lrem = 0x71 + final val frem = 0x72 + final val drem = 0x73 + + final val ineg = 0x74 + final val lneg = 0x75 + final val fneg = 0x76 + final val dneg = 0x77 + + final val ishl = 0x78 + final val lshl = 0x79 + final val ishr = 0x7a + final val lshr = 0x7b + final val iushr = 0x7c + final val lushr = 0x7d + final val iand = 0x7e + final val land = 0x7f + final val ior = 0x80 + final val lor = 0x81 + final val ixor = 0x82 + final val lxor = 0x83 + final val iinc = 0x84 + + final val i2l = 0x85 + final val i2f = 0x86 + final val i2d = 0x87 + final val l2i = 0x88 + final val l2f = 0x89 + final val l2d = 0x8a + final val f2i = 0x8b + final val f2l = 0x8c + final val f2d = 0x8d + final val d2i = 0x8e + final val d2l = 0x8f + final val d2f = 0x90 + final val i2b = 0x91 + final val i2c = 0x92 + final val i2s = 0x93 + + final val lcmp = 0x94 + final val fcmpl = 0x95 + final val fcmpg = 0x96 + final val dcmpl = 0x97 + final val dcmpg = 0x98 + + final val ifeq = 0x99 + final val ifne = 0x9a + final val iflt = 0x9b + final val ifge = 0x9c + final val ifgt = 0x9d + final val ifle = 0x9e + final val if_icmpeq = 0x9f + final val if_icmpne = 0xa0 + final val if_icmplt = 0xa1 + final val if_icmpge = 0xa2 + final val if_icmpgt = 0xa3 + final val if_icmple = 0xa4 + final val if_acmpeq = 0xa5 + final val if_acmpne = 0xa6 + final val goto = 0xa7 + final val jsr = 0xa8 + final val ret = 0xa9 + final val tableswitch = 0xaa + final val lookupswitch = 0xab + final val ireturn = 0xac + final val lreturn = 0xad + final val freturn = 0xae + final val dreturn = 0xaf + final val areturn = 0xb0 + final val return_ = 0xb1 + + final val getstatic = 0xb2 + final val putstatic = 0xb3 + final val getfield = 0xb4 + final val putfield = 0xb5 + + final val invokevirtual = 0xb6 + final val invokespecial = 0xb7 + final val invokestatic = 0xb8 + final val invokeinterface = 0xb9 + final val xxxunusedxxxx = 0xba + + final val new_ = 0xbb + final val newarray = 0xbc + final val anewarray = 0xbd + final val arraylength = 0xbe + final val athrow = 0xbf + final val checkcast = 0xc0 + final val instanceof = 0xc1 + final val monitorenter = 0xc2 + final val monitorexit = 0xc3 + final val wide = 0xc4 + final val multianewarray = 0xc5 + final val ifnull = 0xc6 + final val ifnonnull = 0xc7 + final val goto_w = 0xc8 + final val jsr_w = 0xc9 + + // reserved opcodes + final val breakpoint = 0xca + final val impdep1 = 0xfe + final val impdep2 = 0xff +} diff --git a/src/compiler/scala/reflect/common/Constants.scala b/src/compiler/scala/reflect/common/Constants.scala new file mode 100644 index 0000000000..6e96dae3e6 --- /dev/null +++ b/src/compiler/scala/reflect/common/Constants.scala @@ -0,0 +1,238 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import java.lang.Integer.toOctalString +import annotation.switch + +trait Constants { + self: SymbolTable => + + import definitions._ + + final val NoTag = 0 + final val UnitTag = 1 + final val BooleanTag = 2 + final val ByteTag = 3 + final val ShortTag = 4 + final val CharTag = 5 + final val IntTag = 6 + final val LongTag = 7 + final val FloatTag = 8 + final val DoubleTag = 9 + final val StringTag = 10 + final val NullTag = 11 + final val ClassTag = 12 + // For supporting java enumerations inside java annotations (see ClassfileParser) + final val EnumTag = 13 + + case class Constant(value: Any) { + val tag: Int = value match { + case null => NullTag + case x: Unit => UnitTag + case x: Boolean => BooleanTag + case x: Byte => ByteTag + case x: Short => ShortTag + case x: Int => IntTag + case x: Long => LongTag + case x: Float => FloatTag + case x: Double => DoubleTag + case x: String => StringTag + case x: Char => CharTag + case x: Type => ClassTag + case x: Symbol => EnumTag + case _ => throw new Error("bad constant value: " + value) + } + + def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue + def isShortRange: Boolean = isIntRange && Short.MinValue <= intValue && intValue <= Short.MaxValue + def isCharRange: Boolean = isIntRange && Char.MinValue <= intValue && intValue <= Char.MaxValue + def isIntRange: Boolean = ByteTag <= tag && tag <= IntTag + def isLongRange: Boolean = ByteTag <= tag && tag <= LongTag + def isFloatRange: Boolean = ByteTag <= tag && tag <= FloatTag + def isNumeric: Boolean = ByteTag <= tag && tag <= DoubleTag + + def tpe: Type = tag match { + case UnitTag => UnitClass.tpe + case BooleanTag => BooleanClass.tpe + case ByteTag => ByteClass.tpe + case ShortTag => ShortClass.tpe + case CharTag => CharClass.tpe + case IntTag => IntClass.tpe + case LongTag => LongClass.tpe + case FloatTag => FloatClass.tpe + case DoubleTag => DoubleClass.tpe + case StringTag => StringClass.tpe + case NullTag => NullClass.tpe + case ClassTag => ClassType(value.asInstanceOf[Type]) + case EnumTag => + // given (in java): "class A { enum E { VAL1 } }" + // - symbolValue: the symbol of the actual enumeration value (VAL1) + // - .owner: the ModuleClasSymbol of the enumeration (object E) + // - .linkedClassOfClass: the ClassSymbol of the enumeration (class E) + symbolValue.owner.linkedClassOfClass.tpe + } + + /** We need the equals method to take account of tags as well as values. + */ + override def equals(other: Any): Boolean = other match { + case that: Constant => + this.tag == that.tag && + (this.value == that.value || this.isNaN && that.isNaN) + case _ => false + } + + def isNaN = value match { + case f: Float => f.isNaN + case d: Double => d.isNaN + case _ => false + } + + def booleanValue: Boolean = + if (tag == BooleanTag) value.asInstanceOf[Boolean] + else throw new Error("value " + value + " is not a boolean"); + + def byteValue: Byte = tag match { + case ByteTag => value.asInstanceOf[Byte] + case ShortTag => value.asInstanceOf[Short].toByte + case CharTag => value.asInstanceOf[Char].toByte + case IntTag => value.asInstanceOf[Int].toByte + case LongTag => value.asInstanceOf[Long].toByte + case FloatTag => value.asInstanceOf[Float].toByte + case DoubleTag => value.asInstanceOf[Double].toByte + case _ => throw new Error("value " + value + " is not a Byte") + } + + def shortValue: Short = tag match { + case ByteTag => value.asInstanceOf[Byte].toShort + case ShortTag => value.asInstanceOf[Short] + case CharTag => value.asInstanceOf[Char].toShort + case IntTag => value.asInstanceOf[Int].toShort + case LongTag => value.asInstanceOf[Long].toShort + case FloatTag => value.asInstanceOf[Float].toShort + case DoubleTag => value.asInstanceOf[Double].toShort + case _ => throw new Error("value " + value + " is not a Short") + } + + def charValue: Char = tag match { + case ByteTag => value.asInstanceOf[Byte].toChar + case ShortTag => value.asInstanceOf[Short].toChar + case CharTag => value.asInstanceOf[Char] + case IntTag => value.asInstanceOf[Int].toChar + case LongTag => value.asInstanceOf[Long].toChar + case FloatTag => value.asInstanceOf[Float].toChar + case DoubleTag => value.asInstanceOf[Double].toChar + case _ => throw new Error("value " + value + " is not a Char") + } + + def intValue: Int = tag match { + case ByteTag => value.asInstanceOf[Byte].toInt + case ShortTag => value.asInstanceOf[Short].toInt + case CharTag => value.asInstanceOf[Char].toInt + case IntTag => value.asInstanceOf[Int] + case LongTag => value.asInstanceOf[Long].toInt + case FloatTag => value.asInstanceOf[Float].toInt + case DoubleTag => value.asInstanceOf[Double].toInt + case _ => throw new Error("value " + value + " is not an Int") + } + + def longValue: Long = tag match { + case ByteTag => value.asInstanceOf[Byte].toLong + case ShortTag => value.asInstanceOf[Short].toLong + case CharTag => value.asInstanceOf[Char].toLong + case IntTag => value.asInstanceOf[Int].toLong + case LongTag => value.asInstanceOf[Long] + case FloatTag => value.asInstanceOf[Float].toLong + case DoubleTag => value.asInstanceOf[Double].toLong + case _ => throw new Error("value " + value + " is not a Long") + } + + def floatValue: Float = tag match { + case ByteTag => value.asInstanceOf[Byte].toFloat + case ShortTag => value.asInstanceOf[Short].toFloat + case CharTag => value.asInstanceOf[Char].toFloat + case IntTag => value.asInstanceOf[Int].toFloat + case LongTag => value.asInstanceOf[Long].toFloat + case FloatTag => value.asInstanceOf[Float] + case DoubleTag => value.asInstanceOf[Double].toFloat + case _ => throw new Error("value " + value + " is not a Float") + } + + def doubleValue: Double = tag match { + case ByteTag => value.asInstanceOf[Byte].toDouble + case ShortTag => value.asInstanceOf[Short].toDouble + case CharTag => value.asInstanceOf[Char].toDouble + case IntTag => value.asInstanceOf[Int].toDouble + case LongTag => value.asInstanceOf[Long].toDouble + case FloatTag => value.asInstanceOf[Float].toDouble + case DoubleTag => value.asInstanceOf[Double] + case _ => throw new Error("value " + value + " is not a Double") + } + + /** Convert constant value to conform to given type. + */ + def convertTo(pt: Type): Constant = { + val target = pt.typeSymbol + if (target == tpe.typeSymbol) + this + else if (target == ByteClass && isByteRange) + Constant(byteValue) + else if (target == ShortClass && isShortRange) + Constant(shortValue) + else if (target == CharClass && isCharRange) + Constant(charValue) + else if (target == IntClass && isIntRange) + Constant(intValue) + else if (target == LongClass && isLongRange) + Constant(longValue) + else if (target == FloatClass && isFloatRange) + Constant(floatValue) + else if (target == DoubleClass && isNumeric) + Constant(doubleValue) + else + null + } + + def stringValue: String = + if (value == null) "null" + else if (tag == ClassTag) signature(typeValue) + else value.toString() + + @switch def escapedChar(ch: Char): String = ch match { + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '"' => "\\\"" + case '\'' => "\\\'" + case '\\' => "\\\\" + case _ => String.valueOf(ch) + } + + def escapedStringValue: String = { + def escape(text: String): String = { + text map { ch => + if (ch.isControl) "\\0" + toOctalString(ch) + else escapedChar(ch) + } mkString "" + } + tag match { + case NullTag => "null" + case StringTag => "\"" + escape(stringValue) + "\"" + case ClassTag => "classOf[" + signature(typeValue) + "]" + case CharTag => escape("'" + escapedChar(charValue) + "'") + case LongTag => longValue.toString() + "L" + case _ => String.valueOf(value) + } + } + def typeValue: Type = value.asInstanceOf[Type] + def symbolValue: Symbol = value.asInstanceOf[Symbol] + + override def hashCode: Int = value.## * 41 + 17 + } +} diff --git a/src/compiler/scala/reflect/common/Definitions.scala b/src/compiler/scala/reflect/common/Definitions.scala new file mode 100644 index 0000000000..f2c4fc844e --- /dev/null +++ b/src/compiler/scala/reflect/common/Definitions.scala @@ -0,0 +1,887 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.{ HashMap } +import Flags._ +import PartialFunction._ + +trait Definitions /*extends reflect.generic.StandardDefinitions*/ { + self: SymbolTable => + + // the scala value classes + trait ValueClassDefinitions { + self: definitions.type => + + private[Definitions] def valueCache(name: Name) = { + if (name.isTypeName) ScalaPackageClass.info member name + else ScalaPackageClass.info member name suchThat (_ hasFlag MODULE) + } + private[Definitions] def valueModuleMethod(className: Name, methodName: Name): Symbol = { + valueCache(className.toTermName).moduleClass.tpe member methodName + } + + import ClassfileConstants._ + + private val nameToWeight = Map[Name, Int]( + tpnme.Byte -> 2, + tpnme.Char -> 3, + tpnme.Short -> 4, + tpnme.Int -> 12, + tpnme.Long -> 24, + tpnme.Float -> 48, + tpnme.Double -> 96 + ) + + private val nameToTag = Map[Name, Char]( + tpnme.Byte -> BYTE_TAG, + tpnme.Char -> CHAR_TAG, + tpnme.Short -> SHORT_TAG, + tpnme.Int -> INT_TAG, + tpnme.Long -> LONG_TAG, + tpnme.Float -> FLOAT_TAG, + tpnme.Double -> DOUBLE_TAG, + tpnme.Boolean -> BOOL_TAG, + tpnme.Unit -> VOID_TAG, + tpnme.Object -> TVAR_TAG + ) + + private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) + private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = syms zip (syms map (x => f(x.name))) toMap + private def symbolsMapFilt[T](syms: List[Symbol], p: Name => Boolean, f: Name => T) = symbolsMap(syms filter (x => p(x.name)), f) + + private def boxedName(name: Name) = sn.Boxed(name.toTypeName) + + lazy val abbrvTag = symbolsMap(ObjectClass :: ScalaValueClasses, nameToTag) + lazy val numericWeight = symbolsMapFilt(ScalaValueClasses, nameToWeight.keySet, nameToWeight) + lazy val boxedModule = classesMap(x => getModule(boxedName(x))) + lazy val boxedClass = classesMap(x => getClass(boxedName(x))) + lazy val refClass = classesMap(x => getClass("scala.runtime." + x + "Ref")) + lazy val volatileRefClass = classesMap(x => getClass("scala.runtime.Volatile" + x + "Ref")) + lazy val boxMethod = classesMap(x => valueModuleMethod(x, nme.box)) + lazy val unboxMethod = classesMap(x => valueModuleMethod(x, nme.unbox)) + + private def newClass(owner: Symbol, name: TypeName, parents: List[Type]): Symbol = { + val clazz = owner.newClass(NoPosition, name) + clazz.setInfo(ClassInfoType(parents, new Scope, clazz)) + owner.info.decls.enter(clazz) + clazz + } + + def isNumericSubClass(sub: Symbol, sup: Symbol) = { + val cmp = for (w1 <- numericWeight get sub ; w2 <- numericWeight get sup) yield w2 % w1 + cmp exists (_ == 0) + } + + /** Is symbol a numeric value class? */ + def isNumericValueClass(sym: Symbol): Boolean = + numericWeight contains sym + + private[Definitions] def fullNameStrings: List[String] = nme.ScalaValueNames map ("scala." + _) + private[Definitions] lazy val fullValueName: Set[Name] = { + val values = nme.ScalaValueNames flatMap (x => List(newTypeName("scala." + x), newTermName("scala." + x))) + values.toSet + newTypeName("scala.AnyVal") + } + + lazy val AnyValClass = valueCache(tpnme.AnyVal) + lazy val UnitClass = valueCache(tpnme.Unit) + lazy val ByteClass = valueCache(tpnme.Byte) + lazy val ShortClass = valueCache(tpnme.Short) + lazy val CharClass = valueCache(tpnme.Char) + lazy val IntClass = valueCache(tpnme.Int) + lazy val LongClass = valueCache(tpnme.Long) + lazy val FloatClass = valueCache(tpnme.Float) + lazy val DoubleClass = valueCache(tpnme.Double) + lazy val BooleanClass = valueCache(tpnme.Boolean) + def Boolean_and = getMember(BooleanClass, nme.ZAND) + def Boolean_or = getMember(BooleanClass, nme.ZOR) + + def ScalaValueClassesNoUnit = ScalaValueClasses filterNot (_ eq UnitClass) + def ScalaValueClasses: List[Symbol] = List( + UnitClass, + BooleanClass, + ByteClass, + ShortClass, + CharClass, + IntClass, + LongClass, + FloatClass, + DoubleClass + ) + } + + object definitions extends ValueClassDefinitions { + private var isInitialized = false + def isDefinitionsInitialized = isInitialized + + // symbols related to packages + var emptypackagescope: Scope = null //debug + + // This is the package _root_. The actual root cannot be referenced at + // the source level, but _root_ is essentially a function () => <root>. + lazy val RootPackage: Symbol = { + val rp = NoSymbol.newValue(NoPosition, nme.ROOTPKG) + .setFlag(FINAL | MODULE | PACKAGE | JAVA) + .setInfo(NullaryMethodType(RootClass.tpe)) + RootClass.sourceModule = rp + rp + } + // This is the actual root of everything, including the package _root_. + lazy val RootClass: ModuleClassSymbol = NoSymbol.newModuleClass(NoPosition, tpnme.ROOT) + .setFlag(FINAL | MODULE | PACKAGE | JAVA).setInfo(rootLoader) + + // The empty package, which holds all top level types without given packages. + lazy val EmptyPackage = RootClass.newPackage(NoPosition, nme.EMPTY_PACKAGE_NAME).setFlag(FINAL) + lazy val EmptyPackageClass = EmptyPackage.moduleClass + + lazy val JavaLangPackage = getModule(sn.JavaLang) + lazy val ScalaPackage = getModule("scala") + lazy val ScalaPackageClass = ScalaPackage.tpe.typeSymbol + + lazy val RuntimePackage = getModule("scala.runtime") + lazy val RuntimePackageClass = RuntimePackage.tpe.typeSymbol + + // convenient one-argument parameter lists + lazy val anyparam = List(AnyClass.typeConstructor) + lazy val anyvalparam = List(AnyValClass.typeConstructor) + lazy val anyrefparam = List(AnyRefClass.typeConstructor) + + // private parameter conveniences + private def booltype = BooleanClass.typeConstructor + private def boolparam = List(booltype) + private def bytetype = ByteClass.typeConstructor + private def byteparam = List(bytetype) + private def shorttype = ShortClass.typeConstructor + private def shortparam = List(shorttype) + private def inttype = IntClass.typeConstructor + private def intparam = List(inttype) + private def longtype = LongClass.typeConstructor + private def longparam = List(longtype) + private def floattype = FloatClass.typeConstructor + private def floatparam = List(floattype) + private def doubletype = DoubleClass.typeConstructor + private def doubleparam = List(doubletype) + private def chartype = CharClass.typeConstructor + private def charparam = List(chartype) + private def stringtype = StringClass.typeConstructor + + // top types + lazy val AnyClass = newClass(ScalaPackageClass, tpnme.Any, Nil) setFlag (ABSTRACT) + lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectClass.typeConstructor) + lazy val ObjectClass = getClass(sn.Object) + lazy val AnyCompanionClass = getClass("scala.AnyCompanion") setFlag (SEALED | ABSTRACT | TRAIT) + lazy val AnyValCompanionClass = getClass("scala.AnyValCompanion") setFlag (SEALED | ABSTRACT | TRAIT) + + // bottom types + lazy val NullClass = newClass(ScalaPackageClass, tpnme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL) + lazy val NothingClass = newClass(ScalaPackageClass, tpnme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL) + lazy val RuntimeNothingClass = getClass(ClassfileConstants.SCALA_NOTHING) + lazy val RuntimeNullClass = getClass(ClassfileConstants.SCALA_NULL) + + // exceptions and other throwables + lazy val ClassCastExceptionClass = getClass("java.lang.ClassCastException") + lazy val IndexOutOfBoundsExceptionClass = getClass(sn.IOOBException) + lazy val InvocationTargetExceptionClass = getClass(sn.InvTargetException) + lazy val MatchErrorClass = getClass("scala.MatchError") + lazy val NonLocalReturnControlClass = getClass("scala.runtime.NonLocalReturnControl") + lazy val NullPointerExceptionClass = getClass(sn.NPException) + lazy val ThrowableClass = getClass(sn.Throwable) + lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError") + + // annotations + lazy val AnnotationClass = getClass("scala.annotation.Annotation") + lazy val ClassfileAnnotationClass = getClass("scala.annotation.ClassfileAnnotation") + lazy val StaticAnnotationClass = getClass("scala.annotation.StaticAnnotation") + lazy val uncheckedStableClass = getClass("scala.annotation.unchecked.uncheckedStable") + lazy val uncheckedVarianceClass = getClass("scala.annotation.unchecked.uncheckedVariance") + lazy val UncheckedClass = getClass("scala.unchecked") + lazy val ThrowsClass = getClass("scala.throws") + lazy val TailrecClass = getClass("scala.annotation.tailrec") + lazy val SwitchClass = getClass("scala.annotation.switch") + lazy val ElidableMethodClass = getClass("scala.annotation.elidable") + lazy val ImplicitNotFoundClass = getClass("scala.annotation.implicitNotFound") + lazy val VarargsClass = getClass("scala.annotation.varargs") + lazy val FieldTargetClass = getClass("scala.annotation.target.field") + lazy val GetterTargetClass = getClass("scala.annotation.target.getter") + lazy val SetterTargetClass = getClass("scala.annotation.target.setter") + lazy val BeanGetterTargetClass = getClass("scala.annotation.target.beanGetter") + lazy val BeanSetterTargetClass = getClass("scala.annotation.target.beanSetter") + lazy val ParamTargetClass = getClass("scala.annotation.target.param") + lazy val ScalaInlineClass = getClass("scala.inline") + lazy val ScalaNoInlineClass = getClass("scala.noinline") + lazy val SpecializedClass = getClass("scala.specialized") + lazy val BridgeClass = getClass("scala.annotation.bridge") + + // fundamental reference classes + lazy val ScalaObjectClass = getClass("scala.ScalaObject") + lazy val PartialFunctionClass = getClass("scala.PartialFunction") + lazy val SymbolClass = getClass("scala.Symbol") + lazy val StringClass = getClass(sn.String) + lazy val StringModule = StringClass.linkedClassOfClass + lazy val ClassClass = getClass(sn.Class) + def Class_getMethod = getMember(ClassClass, nme.getMethod_) + lazy val DynamicClass = getClass("scala.Dynamic") + + // fundamental modules + lazy val PredefModule: Symbol = getModule("scala.Predef") + lazy val PredefModuleClass = PredefModule.tpe.typeSymbol + def Predef_AnyRef = getMember(PredefModule, "AnyRef") // used by the specialization annotation + def Predef_classOf = getMember(PredefModule, nme.classOf) + def Predef_error = getMember(PredefModule, nme.error) + def Predef_identity = getMember(PredefModule, nme.identity) + def Predef_conforms = getMember(PredefModule, nme.conforms) + def Predef_wrapRefArray = getMember(PredefModule, nme.wrapRefArray) + lazy val ConsoleModule: Symbol = getModule("scala.Console") + lazy val ScalaRunTimeModule: Symbol = getModule("scala.runtime.ScalaRunTime") + lazy val SymbolModule: Symbol = getModule("scala.Symbol") + lazy val Symbol_apply = getMember(SymbolModule, nme.apply) + def SeqFactory = getMember(ScalaRunTimeModule, nme.Seq) + def arrayApplyMethod = getMember(ScalaRunTimeModule, "array_apply") + def arrayUpdateMethod = getMember(ScalaRunTimeModule, "array_update") + def arrayLengthMethod = getMember(ScalaRunTimeModule, "array_length") + def arrayCloneMethod = getMember(ScalaRunTimeModule, "array_clone") + def ensureAccessibleMethod = getMember(ScalaRunTimeModule, "ensureAccessible") + def scalaRuntimeHash = getMember(ScalaRunTimeModule, "hash") + def scalaRuntimeSameElements = getMember(ScalaRunTimeModule, nme.sameElements) + + // classes with special meanings + lazy val NotNullClass = getClass("scala.NotNull") + lazy val DelayedInitClass = getClass("scala.DelayedInit") + def delayedInitMethod = getMember(DelayedInitClass, nme.delayedInit) + // a dummy value that communicates that a delayedInit call is compiler-generated + // from phase UnCurry to phase Constructors + def delayedInitArgVal = EmptyPackageClass.newValue(NoPosition, nme.delayedInitArg) + .setInfo(UnitClass.tpe) + + lazy val TypeConstraintClass = getClass("scala.annotation.TypeConstraint") + lazy val SingletonClass = newClass(ScalaPackageClass, tpnme.Singleton, anyparam) setFlag (ABSTRACT | TRAIT | FINAL) + lazy val SerializableClass = getClass("scala.Serializable") + lazy val JavaSerializableClass = getClass(sn.JavaSerializable) + lazy val ComparableClass = getClass("java.lang.Comparable") + + lazy val RepeatedParamClass = newCovariantPolyClass( + ScalaPackageClass, + tpnme.REPEATED_PARAM_CLASS_NAME, + tparam => seqType(tparam.typeConstructor) + ) + + lazy val JavaRepeatedParamClass = newCovariantPolyClass( + ScalaPackageClass, + tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, + tparam => arrayType(tparam.typeConstructor) + ) + + def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass + def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass + def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass + def isRepeatedParamType(tp: Type) = isScalaRepeatedParamType(tp) || isJavaRepeatedParamType(tp) + + def isScalaVarArgs(params: List[Symbol]) = params.nonEmpty && isScalaRepeatedParamType(params.last.tpe) + def isVarArgsList(params: List[Symbol]) = params.nonEmpty && isRepeatedParamType(params.last.tpe) + def isVarArgTypes(formals: List[Type]) = formals.nonEmpty && isRepeatedParamType(formals.last) + + def isPrimitiveArray(tp: Type) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => isValueClass(arg.typeSymbol) + case _ => false + } + def isArrayOfSymbol(tp: Type, elem: Symbol) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => arg.typeSymbol == elem + case _ => false + } + + lazy val ByNameParamClass = newCovariantPolyClass( + ScalaPackageClass, + tpnme.BYNAME_PARAM_CLASS_NAME, + tparam => AnyClass.typeConstructor + ) + lazy val EqualsPatternClass = { + val clazz = newClass(ScalaPackageClass, tpnme.EQUALS_PATTERN_NAME, Nil) + clazz setInfo polyType(List(newTypeParam(clazz, 0)), ClassInfoType(anyparam, new Scope, clazz)) + } + + // collections classes + lazy val ConsClass = getClass("scala.collection.immutable.$colon$colon") + lazy val IterableClass = getClass("scala.collection.Iterable") + lazy val IteratorClass = getClass("scala.collection.Iterator") + lazy val ListClass = getClass("scala.collection.immutable.List") + lazy val SeqClass = getClass("scala.collection.Seq") + lazy val StringBuilderClass = getClass("scala.collection.mutable.StringBuilder") + lazy val TraversableClass = getClass("scala.collection.Traversable") + + lazy val ListModule = getModule("scala.collection.immutable.List") + lazy val List_apply = getMember(ListModule, nme.apply) + lazy val NilModule = getModule("scala.collection.immutable.Nil") + lazy val SeqModule = getModule("scala.collection.Seq") + + // arrays and their members + lazy val ArrayModule = getModule("scala.Array") + def ArrayModule_overloadedApply = getMember(ArrayModule, nme.apply) + lazy val ArrayClass = getClass("scala.Array") + def Array_apply = getMember(ArrayClass, nme.apply) + def Array_update = getMember(ArrayClass, nme.update) + def Array_length = getMember(ArrayClass, nme.length) + lazy val Array_clone = getMember(ArrayClass, nme.clone_) + + // reflection / structural types + lazy val SoftReferenceClass = getClass("java.lang.ref.SoftReference") + lazy val WeakReferenceClass = getClass("java.lang.ref.WeakReference") + lazy val MethodClass = getClass(sn.MethodAsObject) + def methodClass_setAccessible = getMember(MethodClass, nme.setAccessible) + lazy val EmptyMethodCacheClass = getClass("scala.runtime.EmptyMethodCache") + lazy val MethodCacheClass = getClass("scala.runtime.MethodCache") + def methodCache_find = getMember(MethodCacheClass, nme.find_) + def methodCache_add = getMember(MethodCacheClass, nme.add_) + + // scala.reflect + lazy val PartialManifestClass = getClass("scala.reflect.ClassManifest") + lazy val PartialManifestModule = getModule("scala.reflect.ClassManifest") + lazy val FullManifestClass = getClass("scala.reflect.Manifest") + lazy val FullManifestModule = getModule("scala.reflect.Manifest") + lazy val OptManifestClass = getClass("scala.reflect.OptManifest") + lazy val NoManifest = getModule("scala.reflect.NoManifest") + lazy val CodeClass = getClass(sn.Code) + lazy val CodeModule = getModule(sn.Code) + def Code_lift = getMember(CodeModule, nme.lift_) + + lazy val ScalaSignatureAnnotation = getClass("scala.reflect.ScalaSignature") + lazy val ScalaLongSignatureAnnotation = getClass("scala.reflect.ScalaLongSignature") + + // invoke dynamic support + lazy val LinkageModule = getModule("java.dyn.Linkage") + lazy val Linkage_invalidateCallerClass = getMember(LinkageModule, "invalidateCallerClass") + lazy val DynamicDispatchClass = getModule("scala.runtime.DynamicDispatch") + lazy val DynamicDispatch_DontSetTarget = getMember(DynamicDispatchClass, "DontSetTarget") + + // Option classes + lazy val OptionClass: Symbol = getClass("scala.Option") + lazy val SomeClass: Symbol = getClass("scala.Some") + lazy val NoneModule: Symbol = getModule("scala.None") + + def isOptionType(tp: Type) = cond(tp.normalize) { case TypeRef(_, OptionClass, List(_)) => true } + def isSomeType(tp: Type) = cond(tp.normalize) { case TypeRef(_, SomeClass, List(_)) => true } + def isNoneType(tp: Type) = cond(tp.normalize) { case TypeRef(_, NoneModule, List(_)) => true } + + def optionType(tp: Type) = typeRef(OptionClass.typeConstructor.prefix, OptionClass, List(tp)) + def someType(tp: Type) = typeRef(SomeClass.typeConstructor.prefix, SomeClass, List(tp)) + def symbolType = typeRef(SymbolClass.typeConstructor.prefix, SymbolClass, List()) + def longType = typeRef(LongClass.typeConstructor.prefix, LongClass, List()) + + // Product, Tuple, Function + private def mkArityArray(name: String, arity: Int, countFrom: Int = 1): Array[Symbol] = { + val list = countFrom to arity map (i => getClass("scala." + name + i)) + if (countFrom == 0) list.toArray + else (NoSymbol +: list).toArray + } + + val MaxTupleArity, MaxProductArity, MaxFunctionArity = 22 + /** The maximal dimensions of a generic array creation. + * I.e. new Array[Array[Array[Array[Array[T]]]]] creates a 5 times + * nested array. More is not allowed. + */ + val MaxArrayDims = 5 + lazy val TupleClass = mkArityArray("Tuple", MaxTupleArity) + lazy val ProductClass = mkArityArray("Product", MaxProductArity) + lazy val FunctionClass = mkArityArray("Function", MaxFunctionArity, 0) + lazy val AbstractFunctionClass = mkArityArray("runtime.AbstractFunction", MaxFunctionArity, 0) + + def tupleField(n: Int, j: Int) = getMember(TupleClass(n), "_" + j) + def isTupleType(tp: Type): Boolean = isTupleType(tp, false) + def isTupleTypeOrSubtype(tp: Type): Boolean = isTupleType(tp, true) + private def isTupleType(tp: Type, subtypeOK: Boolean) = tp.normalize match { + case TypeRef(_, sym, args) if args.nonEmpty => + val len = args.length + len <= MaxTupleArity && { + val tsym = TupleClass(len) + (sym == tsym) || (subtypeOK && !tp.isHigherKinded && sym.isSubClass(tsym)) + } + case _ => false + } + + def tupleType(elems: List[Type]) = { + val len = elems.length + if (len <= MaxTupleArity) { + val sym = TupleClass(len) + typeRef(sym.typeConstructor.prefix, sym, elems) + } else NoType + } + + lazy val ProductRootClass: Symbol = getClass("scala.Product") + def Product_productArity = getMember(ProductRootClass, nme.productArity) + def Product_productElement = getMember(ProductRootClass, nme.productElement) + // def Product_productElementName = getMember(ProductRootClass, nme.productElementName) + def Product_productPrefix = getMember(ProductRootClass, nme.productPrefix) + def Product_canEqual = getMember(ProductRootClass, nme.canEqual_) + + def productProj(z:Symbol, j: Int): Symbol = getMember(z, nme.productAccessorName(j)) + def productProj(n: Int, j: Int): Symbol = productProj(ProductClass(n), j) + + /** returns true if this type is exactly ProductN[T1,...,Tn], not some subclass */ + def isExactProductType(tp: Type): Boolean = cond(tp.normalize) { + case TypeRef(_, sym, elems) => + val len = elems.length + len <= MaxProductArity && sym == ProductClass(len) + } + + def productType(elems: List[Type]) = { + if (elems.isEmpty) UnitClass.tpe + else { + val len = elems.length + if (len <= MaxProductArity) { + val sym = ProductClass(len) + typeRef(sym.typeConstructor.prefix, sym, elems) + } + else NoType + } + } + + /** if tpe <: ProductN[T1,...,TN], returns Some(T1,...,TN) else None */ + def getProductArgs(tpe: Type): Option[List[Type]] = + tpe.baseClasses collectFirst { case x if isExactProductType(x.tpe) => tpe.baseType(x).typeArgs } + + def unapplyUnwrap(tpe:Type) = (tpe match { + case PolyType(_,MethodType(_, res)) => res + case MethodType(_, res) => res + case tpe => tpe + }).normalize + + def functionApply(n: Int) = getMember(FunctionClass(n), nme.apply) + def functionType(formals: List[Type], restpe: Type) = { + val len = formals.length + if (len <= MaxFunctionArity) { + val sym = FunctionClass(len) + typeRef(sym.typeConstructor.prefix, sym, formals :+ restpe) + } else NoType + } + + def abstractFunctionForFunctionType(tp: Type) = tp.normalize match { + case tr @ TypeRef(_, _, args) if isFunctionType(tr) => + val sym = AbstractFunctionClass(args.length - 1) + typeRef(sym.typeConstructor.prefix, sym, args) + case _ => + NoType + } + + def isFunctionType(tp: Type): Boolean = tp.normalize match { + case TypeRef(_, sym, args) if args.nonEmpty => + val len = args.length + len < MaxFunctionArity && sym == FunctionClass(len - 1) + case _ => + false + } + + def isSeqType(tp: Type) = elementType(SeqClass, tp.normalize) != NoType + + def elementType(container: Symbol, tp: Type): Type = tp match { + case TypeRef(_, `container`, arg :: Nil) => arg + case _ => NoType + } + + def seqType(arg: Type) = typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(arg)) + def arrayType(arg: Type) = typeRef(ArrayClass.typeConstructor.prefix, ArrayClass, List(arg)) + def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg)) + + def ClassType(arg: Type) = + if (phase.erasedTypes || forMSIL) ClassClass.tpe + else appliedType(ClassClass.tpe, List(arg)) + + // + // .NET backend + // + + lazy val ComparatorClass = getClass("scala.runtime.Comparator") + // System.ValueType + lazy val ValueTypeClass: Symbol = getClass(sn.ValueType) + // System.MulticastDelegate + lazy val DelegateClass: Symbol = getClass(sn.Delegate) + var Delegate_scalaCallers: List[Symbol] = List() + // Symbol -> (Symbol, Type): scalaCaller -> (scalaMethodSym, DelegateType) + // var Delegate_scalaCallerInfos: HashMap[Symbol, (Symbol, Type)] = _ + lazy val Delegate_scalaCallerTargets: HashMap[Symbol, Symbol] = new HashMap() + + def isCorrespondingDelegate(delegateType: Type, functionType: Type): Boolean = { + isSubType(delegateType, DelegateClass.tpe) && + (delegateType.member(nme.apply).tpe match { + case MethodType(delegateParams, delegateReturn) => + isFunctionType(functionType) && + (functionType.normalize match { + case TypeRef(_, _, args) => + (delegateParams.map(pt => { + if (pt.tpe == AnyClass.tpe) definitions.ObjectClass.tpe else pt}) + ::: List(delegateReturn)) == args + case _ => false + }) + case _ => false + }) + } + + // members of class scala.Any + var Any_== : Symbol = _ + var Any_!= : Symbol = _ + var Any_equals : Symbol = _ + var Any_hashCode : Symbol = _ + var Any_toString : Symbol = _ + var Any_isInstanceOf: Symbol = _ + var Any_asInstanceOf: Symbol = _ + var Any_## : Symbol = _ + + // members of class java.lang.{Object, String} + var Object_eq : Symbol = _ + var Object_ne : Symbol = _ + var Object_== : Symbol = _ + var Object_!= : Symbol = _ + var Object_## : Symbol = _ + var Object_synchronized: Symbol = _ + lazy val Object_isInstanceOf = newPolyMethod( + ObjectClass, "$isInstanceOf", + tparam => MethodType(List(), booltype)) setFlag (FINAL | SYNTHETIC) + lazy val Object_asInstanceOf = newPolyMethod( + ObjectClass, "$asInstanceOf", + tparam => MethodType(List(), tparam.typeConstructor)) setFlag (FINAL | SYNTHETIC) + + def Object_getClass = getMember(ObjectClass, nme.getClass_) + def Object_clone = getMember(ObjectClass, nme.clone_) + def Object_finalize = getMember(ObjectClass, nme.finalize_) + def Object_notify = getMember(ObjectClass, nme.notify_) + def Object_notifyAll = getMember(ObjectClass, nme.notifyAll_) + def Object_equals = getMember(ObjectClass, nme.equals_) + def Object_hashCode = getMember(ObjectClass, nme.hashCode_) + def Object_toString = getMember(ObjectClass, nme.toString_) + + var String_+ : Symbol = _ + + // boxed classes + lazy val ObjectRefClass = getClass("scala.runtime.ObjectRef") + lazy val VolatileObjectRefClass = getClass("scala.runtime.VolatileObjectRef") + lazy val BoxesRunTimeClass = getModule("scala.runtime.BoxesRunTime") + lazy val BoxedNumberClass = getClass(sn.BoxedNumber) + lazy val BoxedCharacterClass = getClass(sn.BoxedCharacter) + lazy val BoxedBooleanClass = getClass(sn.BoxedBoolean) + lazy val BoxedByteClass = getClass("java.lang.Byte") + lazy val BoxedShortClass = getClass("java.lang.Short") + lazy val BoxedIntClass = getClass("java.lang.Integer") + lazy val BoxedLongClass = getClass("java.lang.Long") + lazy val BoxedFloatClass = getClass("java.lang.Float") + lazy val BoxedDoubleClass = getClass("java.lang.Double") + + lazy val BoxedUnitClass = getClass("scala.runtime.BoxedUnit") + lazy val BoxedUnitModule = getModule("scala.runtime.BoxedUnit") + def BoxedUnit_UNIT = getMember(BoxedUnitModule, "UNIT") + def BoxedUnit_TYPE = getMember(BoxedUnitModule, "TYPE") + + // special attributes + lazy val BeanPropertyAttr: Symbol = getClass(sn.BeanProperty) + lazy val BooleanBeanPropertyAttr: Symbol = getClass(sn.BooleanBeanProperty) + lazy val CloneableAttr: Symbol = getClass("scala.cloneable") + lazy val DeprecatedAttr: Symbol = getClass("scala.deprecated") + lazy val DeprecatedNameAttr: Symbol = getClass("scala.deprecatedName") + lazy val MigrationAnnotationClass: Symbol = getClass("scala.annotation.migration") + lazy val NativeAttr: Symbol = getClass("scala.native") + lazy val RemoteAttr: Symbol = getClass("scala.remote") + lazy val ScalaNumberClass: Symbol = getClass("scala.math.ScalaNumber") + lazy val ScalaStrictFPAttr: Symbol = getClass("scala.annotation.strictfp") + lazy val SerialVersionUIDAttr: Symbol = getClass("scala.SerialVersionUID") + lazy val SerializableAttr: Symbol = getClass("scala.annotation.serializable") // @serializable is deprecated + lazy val TraitSetterAnnotationClass: Symbol = getClass("scala.runtime.TraitSetter") + lazy val TransientAttr: Symbol = getClass("scala.transient") + lazy val VolatileAttr: Symbol = getClass("scala.volatile") + + lazy val AnnotationDefaultAttr: Symbol = { + val attr = newClass(RuntimePackageClass, tpnme.AnnotationDefaultATTR, List(AnnotationClass.typeConstructor)) + // This attribute needs a constructor so that modifiers in parsed Java code make sense + attr.info.decls enter (attr newConstructor NoPosition setInfo MethodType(Nil, attr.tpe)) + attr + } + + def getModule(fullname: Name): Symbol = + getModuleOrClass(fullname.toTermName) + + def getClass(fullname: Name): Symbol = { + var result = getModuleOrClass(fullname.toTypeName) + while (result.isAliasType) result = result.info.typeSymbol + result + } + + def getClassIfDefined(fullname: Name): Symbol = + try { + getClass(fullname) + } catch { + case ex: MissingRequirementError => NoSymbol + } + + def getMember(owner: Symbol, name: Name): Symbol = { + if (owner == NoSymbol) NoSymbol + else owner.info.nonPrivateMember(name) match { + case NoSymbol => throw new FatalError(owner + " does not have a member " + name) + case result => result + } + } + def packageExists(packageName: String): Boolean = { + try getModuleOrClass(newTermName(packageName)).isPackage + catch { case _: MissingRequirementError => false } + } + + /** If you're looking for a class, pass a type name. + * If a module, a term name. + */ + private def getModuleOrClass(path: Name): Symbol = { + val module = path.isTermName + val fullname = path.toTermName + if (fullname == nme.NO_NAME) + return NoSymbol + + var sym: Symbol = RootClass + var i = 0 + var j = fullname.pos('.', i) + while (j < fullname.length) { + sym = sym.info.member(fullname.subName(i, j)) + i = j + 1 + j = fullname.pos('.', i) + } + val result = + if (module) sym.info.member(fullname.subName(i, j)).suchThat(_ hasFlag MODULE) + else sym.info.member(fullname.subName(i, j).toTypeName) + if (result == NoSymbol) { + if (settings.debug.value) + { log(sym.info); log(sym.info.members) }//debug + throw new MissingRequirementError((if (module) "object " else "class ") + fullname) + } + result + } + + private def newClass(owner: Symbol, name: TypeName, parents: List[Type]): Symbol = { + val clazz = owner.newClass(NoPosition, name) + clazz.setInfo(ClassInfoType(parents, new Scope, clazz)) + owner.info.decls.enter(clazz) + clazz + } + + private def newCovariantPolyClass(owner: Symbol, name: TypeName, parent: Symbol => Type): Symbol = { + val clazz = newClass(owner, name, List()) + val tparam = newTypeParam(clazz, 0) setFlag COVARIANT + val p = parent(tparam) +/* p.typeSymbol.initialize + println(p.typeSymbol + " flags: " + Flags.flagsToString(p.typeSymbol.flags)) + val parents = /*if (p.typeSymbol.isTrait) + List(definitions.AnyRefClass.tpe, p) + else*/ List(p) + println("creating " + name + " with parents " + parents) */ + clazz.setInfo( + polyType( + List(tparam), + ClassInfoType(List(AnyRefClass.tpe, p), new Scope, clazz))) + } + + private def newAlias(owner: Symbol, name: TypeName, alias: Type): Symbol = { + val tpsym = owner.newAliasType(NoPosition, name) + tpsym.setInfo(alias) + owner.info.decls.enter(tpsym) + tpsym + } + + private def newMethod(owner: Symbol, name: TermName): Symbol = { + val msym = owner.newMethod(NoPosition, name.encode) + owner.info.decls.enter(msym) + msym + } + + private[Definitions] def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type): Symbol = { + val msym = newMethod(owner, name) + val params = msym.newSyntheticValueParams(formals) + msym.setInfo(MethodType(params, restpe)) + } + + /** tcon receives the type parameter symbol as argument */ + private def newPolyMethod(owner: Symbol, name: TermName, tcon: Symbol => Type): Symbol = + newPolyMethodCon(owner, name, tparam => msym => tcon(tparam)) + + /** tcon receives the type parameter symbol and the method symbol as arguments */ + private def newPolyMethodCon(owner: Symbol, name: TermName, tcon: Symbol => Symbol => Type): Symbol = { + val msym = newMethod(owner, name) + val tparam = newTypeParam(msym, 0) + msym.setInfo(polyType(List(tparam), tcon(tparam)(msym))) + } + + private def newParameterlessMethod(owner: Symbol, name: TermName, restpe: Type) = + newMethod(owner, name).setInfo(NullaryMethodType(restpe)) + + private def newTypeParam(owner: Symbol, index: Int): Symbol = + owner.newTypeParameter(NoPosition, newTypeName("T" + index)) setInfo TypeBounds.empty + + lazy val boxedClassValues = boxedClass.values.toSet + lazy val isUnbox = unboxMethod.values.toSet + lazy val isBox = boxMethod.values.toSet + + /** Is symbol a phantom class for which no runtime representation exists? */ + lazy val isPhantomClass = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) + + private lazy val scalaValueClassesSet = ScalaValueClasses.toSet + private lazy val boxedValueClassesSet = boxedClass.values.toSet + BoxedUnitClass + + /** Is symbol a value class? */ + def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) + def isNonUnitValueClass(sym: Symbol) = (sym != UnitClass) && isValueClass(sym) + + /** Is symbol a boxed value class, e.g. java.lang.Integer? */ + def isBoxedValueClass(sym: Symbol) = boxedValueClassesSet(sym) + + /** If symbol is a value class, return the value class, with the exception + * that BoxedUnit remains BoxedUnit. If not a value class, NoSymbol. + */ + def unboxedValueClass(sym: Symbol): Symbol = + if (isValueClass(sym)) sym + else if (sym == BoxedUnitClass) sym + else boxedClass.map(_.swap).getOrElse(sym, NoSymbol) + + /** Is type's symbol a numeric value class? */ + def isNumericValueType(tp: Type): Boolean = tp match { + case TypeRef(_, sym, _) => isNumericValueClass(sym) + case _ => false + } + + // todo: reconcile with javaSignature!!! + def signature(tp: Type): String = { + def erasure(tp: Type): Type = tp match { + case st: SubType => erasure(st.supertype) + case RefinedType(parents, _) => erasure(parents.head) + case _ => tp + } + def flatNameString(sym: Symbol, separator: Char): String = + if (sym == NoSymbol) "" // be more resistant to error conditions, e.g. neg/t3222.scala + else if (sym.owner.isPackageClass) sym.fullName('.') + (if (sym.isModuleClass) "$" else "") + else flatNameString(sym.owner, separator) + "$" + sym.simpleName; + def signature1(etp: Type): String = { + if (etp.typeSymbol == ArrayClass) "[" + signature1(erasure(etp.normalize.typeArgs.head)) + else if (isValueClass(etp.typeSymbol)) abbrvTag(etp.typeSymbol).toString() + else "L" + flatNameString(etp.typeSymbol, '/') + ";" + } + val etp = erasure(tp) + if (etp.typeSymbol == ArrayClass) signature1(etp) + else flatNameString(etp.typeSymbol, '.') + } + + /** getModule2/getClass2 aren't needed at present but may be again, + * so for now they're mothballed. + */ + // def getModule2(name1: Name, name2: Name) = { + // try getModuleOrClass(name1.toTermName) + // catch { case ex1: FatalError => + // try getModuleOrClass(name2.toTermName) + // catch { case ex2: FatalError => throw ex1 } + // } + // } + // def getClass2(name1: Name, name2: Name) = { + // try { + // val result = getModuleOrClass(name1.toTypeName) + // if (result.isAliasType) getClass(name2) else result + // } + // catch { case ex1: FatalError => + // try getModuleOrClass(name2.toTypeName) + // catch { case ex2: FatalError => throw ex1 } + // } + // } + + /** Surgery on the value classes. Without this, AnyVals defined in source + * files end up with an AnyRef parent. It is likely there is a better way + * to evade that AnyRef. + */ + private def setParents(sym: Symbol, parents: List[Type]): Symbol = sym.rawInfo match { + case ClassInfoType(_, scope, clazz) => + sym setInfo ClassInfoType(parents, scope, clazz) + case _ => + sym + } + + def init() { + if (isInitialized) return + + EmptyPackageClass setInfo ClassInfoType(Nil, new Scope, EmptyPackageClass) + EmptyPackage setInfo EmptyPackageClass.tpe + + RootClass.info.decls enter EmptyPackage + RootClass.info.decls enter RootPackage + + // members of class scala.Any + Any_== = newMethod(AnyClass, nme.EQ, anyparam, booltype) setFlag FINAL + Any_!= = newMethod(AnyClass, nme.NE, anyparam, booltype) setFlag FINAL + Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype) + Any_hashCode = newMethod(AnyClass, nme.hashCode_, Nil, inttype) + Any_toString = newMethod(AnyClass, nme.toString_, Nil, stringtype) + Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype) setFlag FINAL + + Any_isInstanceOf = newPolyMethod( + AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL + Any_asInstanceOf = newPolyMethod( + AnyClass, nme.asInstanceOf_, tparam => NullaryMethodType(tparam.typeConstructor)) setFlag FINAL + + // members of class java.lang.{ Object, String } + Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype) setFlag FINAL + Object_== = newMethod(ObjectClass, nme.EQ, anyrefparam, booltype) setFlag FINAL + Object_!= = newMethod(ObjectClass, nme.NE, anyrefparam, booltype) setFlag FINAL + Object_eq = newMethod(ObjectClass, nme.eq, anyrefparam, booltype) setFlag FINAL + Object_ne = newMethod(ObjectClass, nme.ne, anyrefparam, booltype) setFlag FINAL + Object_synchronized = newPolyMethodCon( + ObjectClass, nme.synchronized_, + tparam => msym => MethodType(msym.newSyntheticValueParams(List(tparam.typeConstructor)), tparam.typeConstructor)) setFlag FINAL + + String_+ = newMethod( + StringClass, "+", anyparam, stringtype) setFlag FINAL + + val forced = List( // force initialization of every symbol that is entered as a side effect + AnnotationDefaultAttr, // #2264 + RepeatedParamClass, + JavaRepeatedParamClass, + ByNameParamClass, + AnyClass, + AnyRefClass, + AnyValClass, + NullClass, + NothingClass, + SingletonClass, + EqualsPatternClass, + Object_isInstanceOf, + Object_asInstanceOf + ) + + /** Removing the anyref parent they acquire from having a source file. + */ + setParents(AnyValClass, anyparam) + ScalaValueClasses foreach { sym => + setParents(sym, anyvalparam) + } + + isInitialized = true + } //init + + var nbScalaCallers: Int = 0 + def newScalaCaller(delegateType: Type): Symbol = { + assert(forMSIL, "scalaCallers can only be created if target is .NET") + // object: reference to object on which to call (scala-)method + val paramTypes: List[Type] = List(ObjectClass.tpe) + val name: String = "$scalaCaller$$" + nbScalaCallers + // tparam => resultType, which is the resultType of PolyType, i.e. the result type after applying the + // type parameter =-> a MethodType in this case + // TODO: set type bounds manually (-> MulticastDelegate), see newTypeParam + val newCaller = newMethod(DelegateClass, name, paramTypes, delegateType) setFlag (FINAL | STATIC) + // val newCaller = newPolyMethod(DelegateClass, name, + // tparam => MethodType(paramTypes, tparam.typeConstructor)) setFlag (FINAL | STATIC) + Delegate_scalaCallers = Delegate_scalaCallers ::: List(newCaller) + nbScalaCallers += 1 + newCaller + } + + // def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol, delType: Type) { + // assert(Delegate_scalaCallers contains scalaCaller) + // Delegate_scalaCallerInfos += (scalaCaller -> (methSym, delType)) + // } + + def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol) { + assert(Delegate_scalaCallers contains scalaCaller) + Delegate_scalaCallerTargets += (scalaCaller -> methSym) + } + } +} diff --git a/src/compiler/scala/reflect/common/Flags.scala b/src/compiler/scala/reflect/common/Flags.scala index 756ea3077b..8cdecd0da1 100644 --- a/src/compiler/scala/reflect/common/Flags.scala +++ b/src/compiler/scala/reflect/common/Flags.scala @@ -250,6 +250,8 @@ class Flags extends ModifierFlags { private final val PKL_MASK = 0x00000FFF + final val PickledFlags: Long = 0xFFFFFFFFL + private def rawPickledCorrespondence = Array( (IMPLICIT, IMPLICIT_PKL), (FINAL, FINAL_PKL), diff --git a/src/compiler/scala/reflect/common/HasFlags.scala b/src/compiler/scala/reflect/common/HasFlags.scala new file mode 100644 index 0000000000..cbdd20b122 --- /dev/null +++ b/src/compiler/scala/reflect/common/HasFlags.scala @@ -0,0 +1,231 @@ +package scala.reflect +package common + +/** ISSUE #1: Flag names vs. Test method names + * + * The following methods from Symbol have a name of + * the form isFoo where FOO is the name of a flag, but where the method + * body tests for more than whether the flag is set. + * + * There are two possibilities with such methods. Either the extra + * tests are strictly to partition among overloaded flags (which is + * the case we can live with in the short term, if each such flag's + * partitioning assumptions are documented) or they aren't. + * + * The second case implies that "x hasFlag FOO" and "x.isFoo" have + * different semantics, and this we can't live with, because even if + * we're smart enough to avoid being tripped up by that, the next guy isn't. + * + * No extreme measures necessary, only renaming isFoo to something + * which hews more closely to its implementation. (Or renaming the flag.) + * + // Defined in the compiler Symbol + // + final def isLabel = isMethod && !hasAccessorFlag && hasFlag(LABEL) + final def isLocal: Boolean = owner.isTerm + final def isModuleVar: Boolean = isVariable && hasFlag(MODULEVAR) + final def isStable = + isTerm && + !hasTraitFlag && + (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) && + !(tpe.isVolatile && !hasAnnotation(uncheckedStableClass)) + final def isStatic: Boolean = + hasFlag(STATIC) || isRoot || owner.isStaticOwner + override final def isTrait: Boolean = + isClass && hasFlag(TRAIT | notDEFERRED) // A virtual class becomes a trait (part of DEVIRTUALIZE) + + // Defined in the library Symbol + // + def isTrait: Boolean = isClass && hasFlag(TRAIT) // refined later for virtual classes. + final def isContravariant = isType && hasFlag(CONTRAVARIANT) + final def isCovariant = isType && hasFlag(COVARIANT) + final def isMethod = isTerm && hasFlag(METHOD) + final def isModule = isTerm && hasFlag(MODULE) + final def isPackage = isModule && hasFlag(PACKAGE) + * + */ + +/** ISSUE #2: Implicit flag relationships must be made explicit. + * + * For instance, every time the MODULE flag is set, the FINAL flag is + * set along with it: + * + .setFlag(FINAL | MODULE | PACKAGE | JAVA) + .setFlag(FINAL | MODULE | PACKAGE | JAVA).setInfo(rootLoader) + new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + val m = new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + setFlag(module.getFlag(ModuleToClassFlags) | MODULE | FINAL) + sourceModule.flags = MODULE | FINAL + + * However the same is not true of when the MODULE flag is cleared: + + sym.resetFlag(MODULE) + .setFlag(sym.flags | STABLE).resetFlag(MODULE) + sym.resetFlag(MODULE | FINAL | CASE) + + * It's not relevant whether this example poses any issues: we must + * not tolerate these uncertainties. If the flags are to move together + * then both setting and clearing have to be encapsulated. If there + * is a useful and used distinction between the various permutations + * of on and off, then it must be documented. It's the only way! + */ + +import Flags._ + +/** Common code utilized by Modifiers (which carry the flags associated + * with Trees) and Symbol. + */ +trait HasFlags { + type FlagsType + type AccessBoundaryType + type AnnotationType + + /** Though both Symbol and Modifiers widen this method to public, it's + * defined protected here to give us the option in the future to route + * flag methods through accessors and disallow raw flag manipulation. + * And after that, perhaps, on some magical day: a typesafe enumeration. + */ + protected def flags: FlagsType + + /** The printable representation of this entity's flags and access boundary, + * restricted to flags in the given mask. + */ + def hasFlagsToString(mask: FlagsType): String + + /** Access level encoding: there are three scala flags (PRIVATE, PROTECTED, + * and LOCAL) which combine with value privateWithin (the "foo" in private[foo]) + * to define from where an entity can be accessed. The meanings are as follows: + * + * PRIVATE access restricted to class only. + * PROTECTED access restricted to class and subclasses only. + * LOCAL can only be set in conjunction with PRIVATE or PROTECTED. + * Further restricts access to the same object instance. + * + * In addition, privateWithin can be used to set a visibility barrier. + * When set, everything contained in the named enclosing package or class + * has access. It is incompatible with PRIVATE or LOCAL, but is additive + * with PROTECTED (i.e. if either the flags or privateWithin allow access, + * then it is allowed.) + * + * The java access levels translate as follows: + * + * java private: hasFlag(PRIVATE) && !hasAccessBoundary + * java package: !hasFlag(PRIVATE | PROTECTED) && (privateWithin == enclosing package) + * java protected: hasFlag(PROTECTED) && (privateWithin == enclosing package) + * java public: !hasFlag(PRIVATE | PROTECTED) && !hasAccessBoundary + */ + def privateWithin: AccessBoundaryType + + /** A list of annotations attached to this entity. + */ + def annotations: List[AnnotationType] + + /** Whether this entity has a "privateWithin" visibility barrier attached. + */ + def hasAccessBoundary: Boolean + + /** Whether this entity has ANY of the flags in the given mask. + */ + def hasFlag(flag: Long): Boolean + + /** Whether this entity has ALL of the flags in the given mask. + */ + def hasAllFlags(mask: Long): Boolean + + /** Whether this entity has NONE of the flags in the given mask. + */ + def hasNoFlags(mask: Long): Boolean = !hasFlag(mask) + + // Tests which come through cleanly: both Symbol and Modifiers use these + // identically, testing for a single flag. + def isCase = hasFlag(CASE ) + def isFinal = hasFlag(FINAL ) + def isImplicit = hasFlag(IMPLICIT ) + def isLazy = hasFlag(LAZY ) + def isMutable = hasFlag(MUTABLE ) // in Modifiers, formerly isVariable + def isOverride = hasFlag(OVERRIDE ) + def isPrivate = hasFlag(PRIVATE ) + def isProtected = hasFlag(PROTECTED) + def isSynthetic = hasFlag(SYNTHETIC) + def isInterface = hasFlag(INTERFACE) + + // Newly introduced based on having a reasonably obvious clean translation. + def isPrivateLocal = hasAllFlags(PRIVATE | LOCAL) + def isProtectedLocal = hasAllFlags(PROTECTED | LOCAL) + def isParamAccessor = hasFlag(PARAMACCESSOR) + def isCaseAccessor = hasFlag(CASEACCESSOR) + def isSuperAccessor = hasFlag(SUPERACCESSOR) + def isLifted = hasFlag(LIFTED) + + // Formerly the Modifiers impl did not include the access boundary check, + // which must have been a bug. + def isPublic = hasNoFlags(PRIVATE | PROTECTED) && !hasAccessBoundary + + // Renamed the Modifiers impl from isArgument. + def isParameter = hasFlag(PARAM) + + // Removed isClass qualification since the flag isn't overloaded and + // sym.isClass is enforced in Namers#validate. + def isSealed = hasFlag(SEALED) + + // Removed !isClass qualification since the flag isn't overloaded. + def isDeferred = hasFlag(DEFERRED ) + + // Dropped isTerm condition because flag isn't overloaded. + def isAbstractOverride = hasFlag(ABSOVERRIDE) + + def isDefaultInit = hasFlag(DEFAULTINIT) + + // Disambiguating: DEFAULTPARAM, TRAIT + def hasDefault = hasAllFlags(DEFAULTPARAM | PARAM) + def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM) + def hasTraitFlag = hasFlag(TRAIT) + def hasDefaultFlag = hasFlag(DEFAULTPARAM) + + // Straightforwardly named accessors already being used differently. + // These names are most likely temporary. + def hasAbstractFlag = hasFlag(ABSTRACT) + def hasAccessorFlag = hasFlag(ACCESSOR) + def hasLocalFlag = hasFlag(LOCAL) + def hasModuleFlag = hasFlag(MODULE) + def hasPackageFlag = hasFlag(PACKAGE) + def hasPreSuperFlag = hasFlag(PRESUPER) + def hasStableFlag = hasFlag(STABLE) + def hasStaticFlag = hasFlag(STATIC) + + // Disambiguating: BYNAMEPARAM, CAPTURED, COVARIANT. + def isByNameParam = hasAllFlags(BYNAMEPARAM | PARAM) + // Nope, these aren't going to fly: + // def isCapturedVariable = hasAllFlags(CAPTURED | MUTABLE) + // def isCovariant = hasFlag(COVARIANT) && hasNoFlags(PARAM | MUTABLE) + + // Disambiguating: LABEL, CONTRAVARIANT, INCONSTRUCTOR + def isLabel = hasAllFlags(LABEL | METHOD) && !hasAccessorFlag + // Cannot effectively disambiguate the others at this level. + def hasContravariantFlag = hasFlag(CONTRAVARIANT) + def hasInConstructorFlag = hasFlag(INCONSTRUCTOR) + + // Name + def isJavaDefined = hasFlag(JAVA) + + // Keeping some potentially ambiguous names around so as not to break + // the rest of the world + @deprecated("", "2.9.0") + def isAbstract = hasFlag(ABSTRACT) + // Problematic: + // ABSTRACT and DEFERRED too easy to confuse, and + // ABSTRACT + OVERRIDE ==> ABSOVERRIDE adds to it. + // + // final def isAbstractClass = isClass && hasFlag(ABSTRACT) + // def isAbstractType = false // to be overridden + + // Question: + // Which name? All other flags are isFlag so it's probably a mistake to + // vary from that, but isAccessor does sound like it includes the other + // *ACCESSOR flags. Perhaps something like isSimpleAccessor. + // + // def isAccessor = hasFlag(ACCESSOR ) + // final def isGetterOrSetter = hasAccessorFlag +} + diff --git a/src/compiler/scala/reflect/common/InfoTransformers.scala b/src/compiler/scala/reflect/common/InfoTransformers.scala new file mode 100644 index 0000000000..12fd57caee --- /dev/null +++ b/src/compiler/scala/reflect/common/InfoTransformers.scala @@ -0,0 +1,47 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +trait InfoTransformers { + self: SymbolTable => + + abstract class InfoTransformer { + var prev: InfoTransformer = this + var next: InfoTransformer = this + + val pid: Phase#Id + val changesBaseClasses: Boolean + def transform(sym: Symbol, tpe: Type): Type + + def insert(that: InfoTransformer) { + assert(this.pid != that.pid) + if (that.pid < this.pid) { + prev insert that + } else if (next.pid <= that.pid && next.pid != NoPhase.id) { + next insert that + } else { + that.next = next + that.prev = this + next.prev = that + this.next = that + } + } + + /** The InfoTransformer whose (pid == from). + * If no such exists, the InfoTransformer with the next + * higher pid. + */ + def nextFrom(from: Phase#Id): InfoTransformer = + if (from == this.pid) this + else if (from < this.pid) + if (prev.pid < from) this + else prev.nextFrom(from); + else if (next.pid == NoPhase.id) next + else next.nextFrom(from) + } +} + diff --git a/src/compiler/scala/reflect/common/NameManglers.scala b/src/compiler/scala/reflect/common/NameManglers.scala new file mode 100644 index 0000000000..2f603d36ed --- /dev/null +++ b/src/compiler/scala/reflect/common/NameManglers.scala @@ -0,0 +1,172 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import java.security.MessageDigest +import scala.io.Codec +import Chars.isOperatorPart + +/** A trait to encapsulate name mangling. It's intended for the + * values and methods involved in assembling names out of other names, + * and not for simple synthetically named locals. + */ +trait NameManglers { + self: SymbolTable => + + trait NameManglingCommon { + self: CommonNames => + + def flattenedName(segments: Name*): NameType = compactedString(segments mkString "$") + + /** + * COMPACTIFY + * + * The hashed name has the form (prefix + marker + md5 + marker + suffix), where + * - prefix/suffix.length = MaxNameLength / 4 + * - md5.length = 32 + * + * We obtain the formula: + * + * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6 + * + * (+6 for ".class"). MaxNameLength can therefore be computed as follows: + */ + private final val marker = "$$$$" + private final val MaxNameLength = math.min( + settings.maxClassfileName.value - 6, + 2 * (settings.maxClassfileName.value - 6 - 2*marker.length - 32) + ) + private lazy val md5 = MessageDigest.getInstance("MD5") + private def toMD5(s: String, edge: Int) = { + val prefix = s take edge + val suffix = s takeRight edge + + val cs = s.toArray + val bytes = Codec toUTF8 cs + md5 update bytes + val md5chars = md5.digest() map (b => (b & 0xFF).toHexString) mkString + + prefix + marker + md5chars + marker + suffix + } + private def compactedString(s: String) = + if (s.length <= MaxNameLength) s + else toMD5(s, MaxNameLength / 4) + } + + trait TypeNameMangling extends NameManglingCommon { + self: tpnme.type => + + } + + trait TermNameMangling extends NameManglingCommon { + self: nme.type => + + val IMPL_CLASS_SUFFIX = "$class" + val SINGLETON_SUFFIX = ".type" + val LOCALDUMMY_PREFIX = "<local " // owner of local blocks + val PROTECTED_PREFIX = "protected$" + val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" + val SELECTOR_DUMMY = "<unapply-selector>" + val SETTER_SUFFIX = encode("_=") + val SUPER_PREFIX_STRING = "super$" + val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" + + def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR + def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX + /** !!! Foo$class$1 is an implClassName, I think. */ + def isImplClassName(name: Name) = name endsWith IMPL_CLASS_SUFFIX + def isLocalDummyName(name: Name) = name startsWith LOCALDUMMY_PREFIX + def isLocalName(name: Name) = name endsWith LOCAL_SUFFIX_STRING + def isLoopHeaderLabel(name: Name) = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) + def isProtectedAccessorName(name: Name) = name startsWith PROTECTED_PREFIX + def isReplWrapperName(name: Name) = name containsName INTERPRETER_IMPORT_WRAPPER + def isSetterName(name: Name) = name endsWith SETTER_SUFFIX + def isTraitSetterName(name: Name) = isSetterName(name) && (name containsName TRAIT_SETTER_SEPARATOR_STRING) + def isSingletonName(name: Name) = name endsWith SINGLETON_SUFFIX + + def isOpAssignmentName(name: Name) = name match { + case raw.NE | raw.LE | raw.GE | EMPTY => false + case _ => + name.endChar == '=' && name.startChar != '=' && isOperatorPart(name.startChar) + } + + /** The expanded setter name of `name' relative to this class `base` + */ + def expandedSetterName(name: TermName, base: Symbol): TermName = + expandedName(name, base, separator = TRAIT_SETTER_SEPARATOR_STRING) + + /** If `name' is an expandedName name, the original name. + * Otherwise `name' itself. + */ + def originalName(name: Name): Name = { + var i = name.length + while (i >= 2 && !(name(i - 1) == '$' && name(i - 2) == '$')) i -= 1 + if (i >= 2) { + while (i >= 3 && name(i - 3) == '$') i -= 1 + name.subName(i, name.length) + } else name + } + + /** Return the original name and the types on which this name + * is specialized. For example, + * {{{ + * splitSpecializedName("foo$mIcD$sp") == ('foo', "I", "D") + * }}} + * `foo$mIcD$sp` is the name of a method specialized on two type + * parameters, the first one belonging to the method itself, on Int, + * and another one belonging to the enclosing class, on Double. + */ + def splitSpecializedName(name: Name): (Name, String, String) = + if (name.endsWith("$sp")) { + val name1 = name stripEnd "$sp" + val idxC = name1 lastIndexOf 'c' + val idxM = name1 lastIndexOf 'm' + + (name1.subName(0, idxM - 1), + name1.subName(idxC + 1, name1.length).toString, + name1.subName(idxM + 1, idxC).toString) + } else + (name, "", "") + + def getterName(name: TermName): TermName = if (isLocalName(name)) localToGetter(name) else name + def getterToLocal(name: TermName): TermName = name.toTermName append LOCAL_SUFFIX_STRING + def getterToSetter(name: TermName): TermName = name.toTermName append SETTER_SUFFIX + def localToGetter(name: TermName): TermName = name stripEnd LOCAL_SUFFIX_STRING toTermName + + def setterToGetter(name: TermName): TermName = { + val p = name.pos(TRAIT_SETTER_SEPARATOR_STRING) + if (p < name.length) + setterToGetter(name.subName(p + TRAIT_SETTER_SEPARATOR_STRING.length, name.length)) + else + name stripEnd SETTER_SUFFIX toTermName + } + + def defaultGetterName(name: Name, pos: Int): TermName = { + val prefix = if (isConstructorName(name)) "init" else name + newTermName(prefix + DEFAULT_GETTER_STRING + pos) + } + def defaultGetterToMethod(name: Name): TermName = { + val p = name.pos(DEFAULT_GETTER_STRING) + if (p < name.length) name.subName(0, p) + else name + } + + def dropSingletonName(name: Name): TypeName = name stripEnd SINGLETON_SUFFIX toTypeName + def singletonName(name: Name): TypeName = name append SINGLETON_SUFFIX toTypeName + def implClassName(name: Name): TypeName = name append IMPL_CLASS_SUFFIX toTypeName + def interfaceName(implname: Name): TypeName = implname stripEnd IMPL_CLASS_SUFFIX toTypeName + def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">") + def productAccessorName(i: Int): TermName = newTermName("_" + i) + def superName(name: Name): TermName = newTermName(SUPER_PREFIX_STRING + name) + + /** The name of an accessor for protected symbols. */ + def protName(name: Name): TermName = newTermName(PROTECTED_PREFIX + name) + + /** The name of a setter for protected symbols. Used for inherited Java fields. */ + def protSetterName(name: Name): TermName = newTermName(PROTECTED_SET_PREFIX + name) + } +} diff --git a/src/compiler/scala/reflect/common/Names.scala b/src/compiler/scala/reflect/common/Names.scala new file mode 100644 index 0000000000..4122d7ba2d --- /dev/null +++ b/src/compiler/scala/reflect/common/Names.scala @@ -0,0 +1,434 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import scala.reflect.NameTransformer +import scala.io.Codec +import java.security.MessageDigest + +/** The class Names ... + * + * @author Martin Odersky + * @version 1.0, 05/02/2005 + */ +trait Names /*extends reflect.generic.Names*/ { + +// Operations ------------------------------------------------------------- + + private final val HASH_SIZE = 0x8000 + private final val HASH_MASK = 0x7FFF + private final val NAME_SIZE = 0x20000 + + final val nameDebug = false + + /** memory to store all names sequentially + */ + var chrs: Array[Char] = new Array[Char](NAME_SIZE) + private var nc = 0 + + /** hashtable for finding term names quickly + */ + private val termHashtable = new Array[TermName](HASH_SIZE) + + /** hashtable for finding type names quickly + */ + private val typeHashtable = new Array[TypeName](HASH_SIZE) + + /** the hashcode of a name + */ + private def hashValue(cs: Array[Char], offset: Int, len: Int): Int = + if (len > 0) + (len * (41 * 41 * 41) + + cs(offset) * (41 * 41) + + cs(offset + len - 1) * 41 + + cs(offset + (len >> 1))) + else 0; + + /** Is (the ASCII representation of) name at given index equal to + * cs[offset..offset+len-1]? + */ + private def equals(index: Int, cs: Array[Char], offset: Int, len: Int): Boolean = { + var i = 0 + while ((i < len) && (chrs(index + i) == cs(offset + i))) + i += 1; + i == len + } + + /** enter characters into chrs array + */ + private def enterChars(cs: Array[Char], offset: Int, len: Int) { + var i = 0 + while (i < len) { + if (nc + i == chrs.length) { + val newchrs = new Array[Char](chrs.length * 2) + compat.Platform.arraycopy(chrs, 0, newchrs, 0, chrs.length) + chrs = newchrs + } + chrs(nc + i) = cs(offset + i) + i += 1 + } + if (len == 0) nc += 1 + else nc = nc + len + } + + /** Create a term name from the characters in cs[offset..offset+len-1]. + */ + def newTermName(cs: Array[Char], offset: Int, len: Int): TermName = { + val h = hashValue(cs, offset, len) & HASH_MASK + var n = termHashtable(h) + while ((n ne null) && (n.length != len || !equals(n.start, cs, offset, len))) + n = n.next; + if (n eq null) { + n = new TermName(nc, len, h) + enterChars(cs, offset, len) + } + n + } + + /** create a term name from string + */ + def newTermName(s: String): TermName = + newTermName(s.toCharArray(), 0, s.length()) + + /** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. + */ + def newTermName(bs: Array[Byte], offset: Int, len: Int): TermName = + newTermName(Codec fromUTF8 bs.slice(offset, offset + len) mkString) + + /** Create a type name from the characters in cs[offset..offset+len-1]. + */ + def newTypeName(cs: Array[Char], offset: Int, len: Int): TypeName = + newTermName(cs, offset, len).toTypeName + + /** Create a type name from string + */ + def newTypeName(s: String): TypeName = + newTermName(s).toTypeName + + /** Create a type name from the UTF8 encoded bytes in bs[offset..offset+len-1]. + */ + def newTypeName(bs: Array[Byte], offset: Int, len: Int): TypeName = + newTermName(bs, offset, len).toTypeName + + def mkTermName(name: Name): TermName = name.toTermName + def mkTypeName(name: Name): TypeName = name.toTypeName + def isTermName(name: Name): Boolean = name.isTermName + def isTypeName(name: Name): Boolean = name.isTypeName + + def nameChars: Array[Char] = chrs + @deprecated("", "2.9.0") def view(s: String): TermName = newTermName(s) + + /** An implicit conversion from names to term names + */ + implicit def promoteTermNamesAsNecessary(name: Name): TermName = mkTermName(name) + + +// Classes ---------------------------------------------------------------------- + + /** The name class. */ + sealed abstract class Name(protected val index: Int, protected val len: Int) extends Function1[Int, Char] { + /** Index into name table */ + def start: Int = index + + /** next name in the same hash bucket + */ + def next: Name + + /** return the length of this name + */ + final def length: Int = len + final def isEmpty = length == 0 + final def nonEmpty = !isEmpty + + def isTermName: Boolean + def isTypeName: Boolean + def toTermName: TermName + def toTypeName: TypeName + def companionName: Name + def bothNames: List[Name] = List(toTermName, toTypeName) + + /** Copy bytes of this name to buffer cs, starting at position `offset`. + */ + final def copyChars(cs: Array[Char], offset: Int) = + compat.Platform.arraycopy(chrs, index, cs, offset, len) + + /** return the ascii representation of this name + */ + final def toChars: Array[Char] = { + val cs = new Array[Char](len) + copyChars(cs, 0) + cs + } + + /** return the string representation of this name + */ + final override def toString(): String = new String(chrs, index, len) + def debugString() = NameTransformer.decode(toString) + (if (isTypeName) "!" else "") + + /** Write to UTF8 representation of this name to given character array. + * Start copying to index `to'. Return index of next free byte in array. + * Array must have enough remaining space for all bytes + * (i.e. maximally 3*length bytes). + */ + final def copyUTF8(bs: Array[Byte], offset: Int): Int = { + val bytes = Codec toUTF8 chrs.slice(index, index + len) + compat.Platform.arraycopy(bytes, 0, bs, offset, bytes.length) + offset + bytes.length + } + + /** return the hash value of this name + */ + final override def hashCode(): Int = index + + // Presently disabled. + // override def equals(other: Any) = paranoidEquals(other) + private def paranoidEquals(other: Any): Boolean = { + val cmp = this eq other.asInstanceOf[AnyRef] + if (cmp || !nameDebug) + return cmp + + other match { + case x: String => + Console.println("Compared " + debugString + " and String '" + x + "'") + case x: Name => + if (this.isTermName != x.isTermName) { + val panic = this.toTermName == x.toTermName + Console.println("Compared '%s' and '%s', one term, one type.%s".format(this, x, + if (panic) " And they contain the same name string!" + else "" + )) + } + case _ => + } + false + } + + /** return the i'th Char of this name + */ + final def apply(i: Int): Char = chrs(index + i) + + /** return the index of first occurrence of char c in this name, length if not found */ + final def pos(c: Char): Int = pos(c, 0) + + /** return the index of first occurrence of char c in this name, length if not found */ + final def pos(s: String): Int = pos(s, 0) + + /** return the index of the first occurrence of character c in + * this name from start, length if not found. + * + * @param c the character + * @param start ... + * @return the index of the first occurrence of c + */ + final def pos(c: Char, start: Int): Int = { + var i = start + while (i < len && chrs(index + i) != c) i += 1 + i + } + + /** return the index of the first occurrence of nonempty string s + * in this name from start, length if not found. + * + * @param s the string + * @param start ... + * @return the index of the first occurrence of s + */ + final def pos(s: String, start: Int): Int = { + var i = pos(s.charAt(0), start) + while (i + s.length() <= len) { + var j = 1 + while (s.charAt(j) == chrs(index + i + j)) { + j += 1 + if (j == s.length()) return i + } + i = pos(s.charAt(0), i + 1) + } + len + } + + /** return the index of last occurrence of char c in this + * name, -1 if not found. + * + * @param c the character + * @return the index of the last occurrence of c + */ + final def lastPos(c: Char): Int = lastPos(c, len - 1) + + final def lastPos(s: String): Int = lastPos(s, len - s.length) + + /** return the index of the last occurrence of char c in this + * name from start, -1 if not found. + * + * @param c the character + * @param start ... + * @return the index of the last occurrence of c + */ + final def lastPos(c: Char, start: Int): Int = { + var i = start + while (i >= 0 && chrs(index + i) != c) i -= 1 + i + } + + /** return the index of the last occurrence of string s in this + * name from start, -1 if not found. + * + * @param s the string + * @param start ... + * @return the index of the last occurrence of s + */ + final def lastPos(s: String, start: Int): Int = { + var i = lastPos(s.charAt(0), start) + while (i >= 0) { + var j = 1; + while (s.charAt(j) == chrs(index + i + j)) { + j += 1 + if (j == s.length()) return i; + } + i = lastPos(s.charAt(0), i - 1) + } + -s.length() + } + + /** does this name start with prefix? + */ + final def startsWith(prefix: Name): Boolean = startsWith(prefix, 0) + + /** does this name start with prefix at given start index? + */ + final def startsWith(prefix: Name, start: Int): Boolean = { + var i = 0 + while (i < prefix.length && start + i < len && + chrs(index + start + i) == chrs(prefix.start + i)) + i += 1; + i == prefix.length + } + + /** does this name end with suffix? + */ + final def endsWith(suffix: Name): Boolean = endsWith(suffix, len) + + /** does this name end with suffix just before given end index? + */ + final def endsWith(suffix: Name, end: Int): Boolean = { + var i = 1 + while (i <= suffix.length && i <= end && + chrs(index + end - i) == chrs(suffix.start + suffix.length - i)) + i += 1; + i > suffix.length + } + + final def containsName(subname: String): Boolean = containsName(newTermName(subname)) + final def containsName(subname: Name): Boolean = { + var start = 0 + val last = len - subname.length + while (start <= last && !startsWith(subname, start)) start += 1 + start <= last + } + + /** Some thoroughly self-explanatory convenience functions. They + * assume that what they're being asked to do is known to be valid. + */ + final def startChar: Char = apply(0) + final def endChar: Char = apply(len - 1) + final def startsWith(char: Char): Boolean = len > 0 && startChar == char + final def startsWith(name: String): Boolean = startsWith(newTermName(name)) + final def endsWith(char: Char): Boolean = len > 0 && endChar == char + final def endsWith(name: String): Boolean = endsWith(newTermName(name)) + final def stripStart(prefix: Name): Name = subName(prefix.length, len) + final def stripStart(prefix: String): Name = subName(prefix.length, len) + final def stripEnd(suffix: Name): Name = subName(0, len - suffix.length) + final def stripEnd(suffix: String): Name = subName(0, len - suffix.length) + + def lastIndexOf(ch: Char) = toChars lastIndexOf ch + + /** Return the subname with characters from start to end-1. + */ + def subName(from: Int, to: Int): Name + + /** Replace all occurrences of `from` by `to` in + * name; result is always a term name. + */ + def replace(from: Char, to: Char): Name = { + val cs = new Array[Char](len) + var i = 0 + while (i < len) { + val ch = this(i) + cs(i) = if (ch == from) to else ch + i += 1 + } + newTermName(cs, 0, len) + } + + /** Replace operator symbols by corresponding $op_name. + */ + def encode: Name = { + val str = toString() + val res = NameTransformer.encode(str) + if (res == str) this + else if (isTypeName) newTypeName(res) + else newTermName(res) + } + + def append(suffix: String): Name + def append(suffix: Name): Name + + /** Replace $op_name by corresponding operator symbol. + */ + def decode: String = ( + NameTransformer.decode(toString()) + + (if (nameDebug && isTypeName) "!" else ""))//debug + + def isOperatorName: Boolean = decode != toString + def nameKind: String = if (isTypeName) "type" else "term" + def longString: String = nameKind + " " + NameTransformer.decode(toString) + } + + final class TermName(_index: Int, _len: Int, hash: Int) extends Name(_index, _len) { + var next: TermName = termHashtable(hash) + termHashtable(hash) = this + def isTermName: Boolean = true + def isTypeName: Boolean = false + def toTermName: TermName = this + def toTypeName: TypeName = { + val h = hashValue(chrs, index, len) & HASH_MASK + var n = typeHashtable(h) + while ((n ne null) && n.start != index) + n = n.next; + if (n eq null) + n = new TypeName(index, len, h); + n + } + def append(suffix: String): TermName = newTermName(this + suffix) + def append(suffix: Name): TermName = append(suffix.toString) + def companionName: TypeName = toTypeName + def subName(from: Int, to: Int): TermName = + newTermName(chrs, start + from, to - from) + } + + final class TypeName(_index: Int, _len: Int, hash: Int) extends Name(_index, _len) { + var next: TypeName = typeHashtable(hash) + typeHashtable(hash) = this + def isTermName: Boolean = false + def isTypeName: Boolean = true + def toTermName: TermName = { + val h = hashValue(chrs, index, len) & HASH_MASK + var n = termHashtable(h) + while ((n ne null) && n.start != index) + n = n.next; + if (n eq null) + n = new TermName(index, len, h); + n + } + def toTypeName: TypeName = this + + def append(suffix: String): TypeName = newTypeName(this + suffix) + def append(suffix: Name): TypeName = append(suffix.toString) + def companionName: TermName = toTermName + def subName(from: Int, to: Int): TypeName = + newTypeName(chrs, start + from, to - from) + } +} diff --git a/src/compiler/scala/reflect/common/Positions.scala b/src/compiler/scala/reflect/common/Positions.scala new file mode 100644 index 0000000000..e8048a65c9 --- /dev/null +++ b/src/compiler/scala/reflect/common/Positions.scala @@ -0,0 +1,33 @@ +package scala.reflect +package common + +trait Positions { self: SymbolTable => + + type Position + val NoPosition: Position + + def focusPos(pos: Position): Position + def isRangePos(pos: Position): Boolean + def showPos(pos: Position): String + + /** A position that wraps a set of trees. + * The point of the wrapping position is the point of the default position. + * If some of the trees are ranges, returns a range position enclosing all ranges + * Otherwise returns default position. + */ + def wrappingPos(default: Position, trees: List[Tree]): Position = default + + /** A position that wraps the non-empty set of trees. + * The point of the wrapping position is the point of the first trees' position. + * If all some the trees are non-synthetic, returns a range position enclosing the non-synthetic trees + * Otherwise returns a synthetic offset position to point. + */ + def wrappingPos(trees: List[Tree]): Position = trees.head.pos + + /** Ensure that given tree has no positions that overlap with + * any of the positions of `others`. This is done by + * shortening the range or assigning TransparentPositions + * to some of the nodes in `tree`. + */ + def ensureNonOverlapping(tree: Tree, others: List[Tree]) {} +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/common/Required.scala b/src/compiler/scala/reflect/common/Required.scala new file mode 100644 index 0000000000..a7d33fa8db --- /dev/null +++ b/src/compiler/scala/reflect/common/Required.scala @@ -0,0 +1,17 @@ +package scala.reflect +package common + +import settings.MutableSettings + +trait Required { self: SymbolTable => + + type AbstractFile >: Null <: { def path: String } + + def picklerPhase: Phase + + val treePrinter: TreePrinter + + val gen: TreeGen { val global: Required.this.type } + + def settings: MutableSettings +} diff --git a/src/compiler/scala/reflect/common/Scopes.scala b/src/compiler/scala/reflect/common/Scopes.scala new file mode 100644 index 0000000000..4fbfa1760e --- /dev/null +++ b/src/compiler/scala/reflect/common/Scopes.scala @@ -0,0 +1,326 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +trait Scopes { self: SymbolTable => + + class ScopeEntry(val sym: Symbol, val owner: Scope) { + /** the next entry in the hash bucket + */ + var tail: ScopeEntry = null + + /** the next entry in this scope + */ + var next: ScopeEntry = null + + override def hashCode(): Int = sym.name.start + override def toString(): String = sym.toString() + } + + /** + * @param sym ... + * @param owner ... + * @return ... + */ + private def newScopeEntry(sym: Symbol, owner: Scope): ScopeEntry = { + val e = new ScopeEntry(sym, owner) + e.next = owner.elems + owner.elems = e + e + } + + class Scope(initElems: ScopeEntry) extends Iterable[Symbol] { + + var elems: ScopeEntry = initElems + + /** The number of times this scope is nested in another + */ + private var nestinglevel = 0 + + /** the hash table + */ + private var hashtable: Array[ScopeEntry] = null + + /** a cache for all elements, to be used by symbol iterator. + */ + private var elemsCache: List[Symbol] = null + + /** size and mask of hash tables + * todo: make hashtables grow? + */ + private val HASHSIZE = 0x80 + private val HASHMASK = 0x7f + + /** the threshold number of entries from which a hashtable is constructed. + */ + private val MIN_HASH = 8 + + if (size >= MIN_HASH) createHash() + + def this() = this(null: ScopeEntry) + + def this(base: Scope) = { + this(base.elems) + nestinglevel = base.nestinglevel + 1 + } + + def this(decls: List[Symbol]) = { + this() + decls foreach enter + } + + /** Returns a new scope with the same content as this one. */ + def cloneScope: Scope = { + val clone = new Scope() + this.toList foreach (clone enter _) + clone + } + + /** is the scope empty? */ + override def isEmpty: Boolean = elems eq null + + /** the number of entries in this scope */ + override def size: Int = { + var s = 0 + var e = elems + while (e ne null) { + s += 1 + e = e.next + } + s + } + + /** enter a scope entry + * + * @param e ... + */ + def enter(e: ScopeEntry) { + elemsCache = null + if (hashtable ne null) { + val i = e.sym.name.start & HASHMASK + elems.tail = hashtable(i) + hashtable(i) = elems + } else if (size >= MIN_HASH) { + createHash() + } + } + + /** enter a symbol + * + * @param sym ... + */ + def enter(sym: Symbol): Symbol = { enter(newScopeEntry(sym, this)); sym } + + /** enter a symbol, asserting that no symbol with same name exists in scope + * + * @param sym ... + */ + def enterUnique(sym: Symbol) { + assert(lookup(sym.name) == NoSymbol) + enter(sym) + } + + private def createHash() { + hashtable = new Array[ScopeEntry](HASHSIZE) + enterInHash(elems) + } + + private def enterInHash(e: ScopeEntry) { + if (e ne null) { + enterInHash(e.next) + val i = e.sym.name.start & HASHMASK + e.tail = hashtable(i) + hashtable(i) = e + } + } + + def rehash(sym: Symbol, newname: Name) { + if (hashtable ne null) { + val index = sym.name.start & HASHMASK + var e1 = hashtable(index) + var e: ScopeEntry = null + if (e1 != null) { + if (e1.sym == sym) { + hashtable(index) = e1.tail + e = e1 + } else { + while (e1.tail != null && e1.tail.sym != sym) e1 = e1.tail + if (e1.tail != null) { + e = e1.tail + e1.tail = e.tail + } + } + } + if (e != null) { + val newindex = newname.start & HASHMASK + e.tail = hashtable(newindex) + hashtable(newindex) = e + } + } + } + + /** remove entry + * + * @param e ... + */ + def unlink(e: ScopeEntry) { + if (elems == e) { + elems = e.next + } else { + var e1 = elems + while (e1.next != e) e1 = e1.next + e1.next = e.next + } + if (hashtable ne null) { + val index = e.sym.name.start & HASHMASK + var e1 = hashtable(index) + if (e1 == e) { + hashtable(index) = e.tail + } else { + while (e1.tail != e) e1 = e1.tail; + e1.tail = e.tail + } + } + elemsCache = null + } + + /** remove symbol */ + def unlink(sym: Symbol) { + var e = lookupEntry(sym.name) + while (e ne null) { + if (e.sym == sym) unlink(e); + e = lookupNextEntry(e) + } + } + + /** lookup a symbol + * + * @param name ... + * @return ... + */ + def lookup(name: Name): Symbol = { + val e = lookupEntry(name) + if (e eq null) NoSymbol else e.sym + } + + /** Returns an iterator yielding every symbol with given name in this scope. + */ + def lookupAll(name: Name): Iterator[Symbol] = new Iterator[Symbol] { + var e = lookupEntry(name) + def hasNext: Boolean = e ne null + def next: Symbol = { val r = e.sym; e = lookupNextEntry(e); r } + } + + /** lookup a symbol entry matching given name. + * @note from Martin: I believe this is a hotspot or will be one + * in future versions of the type system. I have reverted the previous + * change to use iterators as too costly. + */ + def lookupEntry(name: Name): ScopeEntry = { + var e: ScopeEntry = null + if (hashtable ne null) { + e = hashtable(name.start & HASHMASK) + while ((e ne null) && e.sym.name != name) { + e = e.tail + } + } else { + e = elems + while ((e ne null) && e.sym.name != name) { + e = e.next + } + } + e + } + + /** lookup next entry with same name as this one + * @note from Martin: I believe this is a hotspot or will be one + * in future versions of the type system. I have reverted the previous + * change to use iterators as too costly. + */ + def lookupNextEntry(entry: ScopeEntry): ScopeEntry = { + var e = entry + if (hashtable ne null) + do { e = e.tail } while ((e ne null) && e.sym.name != entry.sym.name) + else + do { e = e.next } while ((e ne null) && e.sym.name != entry.sym.name); + e + } + + /** Return all symbols as a list in the order they were entered in this scope. + */ + override def toList: List[Symbol] = { + if (elemsCache eq null) { + elemsCache = Nil + var e = elems + while ((e ne null) && e.owner == this) { + elemsCache = e.sym :: elemsCache + e = e.next + } + } + elemsCache + } + + /** Return the nesting level of this scope, i.e. the number of times this scope + * was nested in another */ + def nestingLevel = nestinglevel + + /** Return all symbols as an iterator in the order they were entered in this scope. + */ + def iterator: Iterator[Symbol] = toList.iterator + +/* + /** Does this scope contain an entry for `sym`? + */ + def contains(sym: Symbol): Boolean = lookupAll(sym.name) contains sym + + /** A scope that contains all symbols of this scope and that also contains `sym`. + */ + def +(sym: Symbol): Scope = + if (contains(sym)) this + else { + val result = cloneScope + result enter sym + result + } + + /** A scope that contains all symbols of this scope except `sym`. + */ + def -(sym: Symbol): Scope = + if (!contains(sym)) this + else { + val result = cloneScope + result unlink sym + result + } +*/ + override def foreach[U](p: Symbol => U): Unit = toList foreach p + + override def filter(p: Symbol => Boolean): Scope = + if (!(toList forall p)) new Scope(toList filter p) else this + + override def mkString(start: String, sep: String, end: String) = + toList.map(_.defString).mkString(start, sep, end) + + override def toString(): String = mkString("Scope{\n ", ";\n ", "\n}") + + } + + def newScope: Scope = new Scope + + /** The empty scope (immutable). + */ + object EmptyScope extends Scope { + override def enter(e: ScopeEntry) { + abort("EmptyScope.enter") + } + } + + /** The error scope. + */ + class ErrorScope(owner: Symbol) extends Scope(null: ScopeEntry) +} + diff --git a/src/compiler/scala/reflect/common/StdNames.scala b/src/compiler/scala/reflect/common/StdNames.scala new file mode 100644 index 0000000000..5c1efa35ba --- /dev/null +++ b/src/compiler/scala/reflect/common/StdNames.scala @@ -0,0 +1,617 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import scala.collection.immutable + +trait StdNames extends /*reflect.generic.StdNames with*/ NameManglers { self: SymbolTable => + + def encode(str: String): TermName = newTermName(NameTransformer.encode(str)) + + implicit def stringToTermName(s: String): TermName = newTermName(s) + + /** This should be the first trait in the linearization. */ + trait Keywords { + private var kws: Set[TermName] = Set() + private def kw(s: String): TermName = { + val result = newTermName(s) + kws = kws + result + result + } + + final val ABSTRACTkw: TermName = kw("abstract") + final val CASEkw: TermName = kw("case") + final val CLASSkw: TermName = kw("class") + final val CATCHkw: TermName = kw("catch") + final val DEFkw: TermName = kw("def") + final val DOkw: TermName = kw("do") + final val ELSEkw: TermName = kw("else") + final val EXTENDSkw: TermName = kw("extends") + final val FALSEkw: TermName = kw("false") + final val FINALkw: TermName = kw("final") + final val FINALLYkw: TermName = kw("finally") + final val FORkw: TermName = kw("for") + final val FORSOMEkw: TermName = kw("forSome") + final val IFkw: TermName = kw("if") + final val IMPLICITkw: TermName = kw("implicit") + final val IMPORTkw: TermName = kw("import") + final val LAZYkw: TermName = kw("lazy") + final val MATCHkw: TermName = kw("match") + final val NEWkw: TermName = kw("new") + final val NULLkw: TermName = kw("null") + final val OBJECTkw: TermName = kw("object") + final val OVERRIDEkw: TermName = kw("override") + final val PACKAGEkw: TermName = kw("package") + final val PRIVATEkw: TermName = kw("private") + final val PROTECTEDkw: TermName = kw("protected") + final val RETURNkw: TermName = kw("return") + final val SEALEDkw: TermName = kw("sealed") + final val SUPERkw: TermName = kw("super") + final val THISkw: TermName = kw("this") + final val THROWkw: TermName = kw("throw") + final val TRAITkw: TermName = kw("trait") + final val TRUEkw: TermName = kw("true") + final val TRYkw: TermName = kw("try") + final val TYPEkw: TermName = kw("type") + final val VALkw: TermName = kw("val") + final val VARkw: TermName = kw("var") + final val WITHkw: TermName = kw("with") + final val WHILEkw: TermName = kw("while") + final val YIELDkw: TermName = kw("yield") + final val DOTkw: TermName = kw(".") + final val USCOREkw: TermName = kw("_") + final val COLONkw: TermName = kw(":") + final val EQUALSkw: TermName = kw("=") + final val ARROWkw: TermName = kw("=>") + final val LARROWkw: TermName = kw("<-") + final val SUBTYPEkw: TermName = kw("<:") + final val VIEWBOUNDkw: TermName = kw("<%") + final val SUPERTYPEkw: TermName = kw(">:") + final val HASHkw: TermName = kw("#") + final val ATkw: TermName = kw("@") + + final val keywords = { + val result = kws.toSet + kws = null + result + } + + final val javaKeywords = new JavaKeywords() + } + + trait CommonNames /*extends LibraryCommonNames*/ { + + type NameType <: Name + implicit def createNameType(name: String): NameType + + val EMPTY: NameType = "" + val ANON_FUN_NAME: NameType = "$anonfun" + val EMPTY_PACKAGE_NAME: NameType = "<empty>" + val IMPORT: NameType = "<import>" + val MODULE_SUFFIX: NameType = "$module" + val ROOT: NameType = "<root>" + + // value types are all used as terms as well + final val Boolean: NameType = "Boolean" + final val Byte: NameType = "Byte" + final val Char: NameType = "Char" + final val Double: NameType = "Double" + final val Float: NameType = "Float" + final val Int: NameType = "Int" + final val Long: NameType = "Long" + final val Short: NameType = "Short" + final val Unit: NameType = "Unit" + + final val ScalaValueNames: scala.List[NameType] = + scala.List(Byte, Char, Short, Int, Long, Float, Double, Boolean, Unit) + + // types whose companions we utilize + final val Array: NameType = "Array" + final val List: NameType = "List" + final val Seq: NameType = "Seq" + final val Symbol: NameType = "Symbol" + + // fictions we use as both types and terms + final val ERROR: NameType = "<error>" + final val NO_NAME: NameType = "<none>" // formerly NOSYMBOL + final val WILDCARD: NameType = "_" + } + + trait TypeNames extends CommonNames { + final val BYNAME_PARAM_CLASS_NAME: NameType = "<byname>" + final val EQUALS_PATTERN_NAME: NameType = "<equals>" + final val JAVA_REPEATED_PARAM_CLASS_NAME: NameType = "<repeated...>" + final val LOCAL_CHILD: NameType = "<local child>" + final val REPEATED_PARAM_CLASS_NAME: NameType = "<repeated>" + final val WILDCARD_STAR: NameType = "_*" + + final val Any: NameType = "Any" + final val AnyRef: NameType = "AnyRef" + final val AnyVal: NameType = "AnyVal" + final val Nothing: NameType = "Nothing" + final val Null: NameType = "Null" + final val Object: NameType = "Object" + final val PartialFunction: NameType = "PartialFunction" + final val Product: NameType = "Product" + final val ScalaObject: NameType = "ScalaObject" + final val Serializable: NameType = "Serializable" + final val Singleton: NameType = "Singleton" + final val String: NameType = "String" + final val Throwable: NameType = "Throwable" + + // Annotation types + final val AnnotationDefaultATTR: NameType = "AnnotationDefault" + final val BridgeATTR: NameType = "Bridge" + final val ClassfileAnnotationATTR: NameType = "RuntimeInvisibleAnnotations" // RetentionPolicy.CLASS. Currently not used (Apr 2009). + final val CodeATTR: NameType = "Code" + final val ConstantValueATTR: NameType = "ConstantValue" + final val DeprecatedATTR: NameType = "Deprecated" + final val ExceptionsATTR: NameType = "Exceptions" + final val InnerClassesATTR: NameType = "InnerClasses" + final val JacoMetaATTR: NameType = "JacoMeta" + final val LineNumberTableATTR: NameType = "LineNumberTable" + final val LocalVariableTableATTR: NameType = "LocalVariableTable" + final val RuntimeAnnotationATTR: NameType = "RuntimeVisibleAnnotations" // RetentionPolicy.RUNTIME + final val RuntimeParamAnnotationATTR: NameType = "RuntimeVisibleParameterAnnotations" // RetentionPolicy.RUNTIME (annotations on parameters) + final val ScalaATTR: NameType = "Scala" + final val ScalaSignatureATTR: NameType = "ScalaSig" + final val SignatureATTR: NameType = "Signature" + final val SourceFileATTR: NameType = "SourceFile" + final val SyntheticATTR: NameType = "Synthetic" + } + + + trait TermNames extends Keywords with CommonNames { + // Compiler internal names + val ANYNAME: NameType = "<anyname>" + val CONSTRUCTOR: NameType = "<init>" + val FAKE_LOCAL_THIS: NameType = "this$" + val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? + val MIXIN_CONSTRUCTOR: NameType = "$init$" + val MODULE_INSTANCE_FIELD: NameType = "MODULE$" + val OUTER: NameType = "$outer" + val OUTER_LOCAL: NameType = "$outer " // note the space + val SELF: NameType = "$this" + val SPECIALIZED_INSTANCE: NameType = "specInstance$" + val STAR: NameType = "*" + val THIS: NameType = "_$this" + + final val Nil: NameType = "Nil" + final val Predef: NameType = "Predef" + final val ScalaRunTime: NameType = "ScalaRunTime" + final val Some: NameType = "Some" + + // Compiler utilized names + // val productElementName: NameType = "productElementName" + val TYPE_ : NameType = "TYPE" + val add_ : NameType = "add" + val apply: NameType = "apply" + val arrayValue: NameType = "arrayValue" + val arraycopy: NameType = "arraycopy" + val asInstanceOf_ : NameType = "asInstanceOf" + val assert_ : NameType = "assert" + val assume_ : NameType = "assume" + val box: NameType = "box" + val bytes: NameType = "bytes" + val canEqual_ : NameType = "canEqual" + val checkInitialized: NameType = "checkInitialized" + val classOf: NameType = "classOf" + val clone_ : NameType = if (forMSIL) "MemberwiseClone" else "clone" // sn.OClone causes checkinit failure + val conforms: NameType = "conforms" + val copy: NameType = "copy" + val delayedInit: NameType = "delayedInit" + val delayedInitArg: NameType = "delayedInit$body" + val drop: NameType = "drop" + val elem: NameType = "elem" + val eq: NameType = "eq" + val equals_ : NameType = if (forMSIL) "Equals" else "equals" + val error: NameType = "error" + val ex: NameType = "ex" + val false_ : NameType = "false" + val filter: NameType = "filter" + val finalize_ : NameType = if (forMSIL) "Finalize" else "finalize" + val find_ : NameType = "find" + val flatMap: NameType = "flatMap" + val foreach: NameType = "foreach" + val genericArrayOps: NameType = "genericArrayOps" + val get: NameType = "get" + val hasNext: NameType = "hasNext" + val hashCode_ : NameType = if (forMSIL) "GetHashCode" else "hashCode" + val hash_ : NameType = "hash" + val head: NameType = "head" + val identity: NameType = "identity" + val inlinedEquals: NameType = "inlinedEquals" + val applyDynamic: NameType = "applyDynamic" + val isArray: NameType = "isArray" + val isDefinedAt: NameType = "isDefinedAt" + val isEmpty: NameType = "isEmpty" + val isInstanceOf_ : NameType = "isInstanceOf" + val java: NameType = "java" + val lang: NameType = "lang" + val length: NameType = "length" + val lengthCompare: NameType = "lengthCompare" + val lift_ : NameType = "lift" + val main: NameType = "main" + val map: NameType = "map" + val ne: NameType = "ne" + val newArray: NameType = "newArray" + val next: NameType = "next" + val notifyAll_ : NameType = "notifyAll" + val notify_ : NameType = "notify" + val null_ : NameType = "null" + val ofDim: NameType = "ofDim" + val productArity: NameType = "productArity" + val productElement: NameType = "productElement" + val productPrefix: NameType = "productPrefix" + val readResolve: NameType = "readResolve" + val sameElements: NameType = "sameElements" + val scala_ : NameType = "scala" + val self: NameType = "self" + val setAccessible: NameType = "setAccessible" + val synchronized_ : NameType = "synchronized" + val tail: NameType = "tail" + val this_ : NameType = "this" + val throw_ : NameType = "throw" + val toArray: NameType = "toArray" + val toList: NameType = "toList" + val toSeq: NameType = "toSeq" + val toString_ : NameType = if (forMSIL) "ToString" else "toString" + val true_ : NameType = "true" + val unapply: NameType = "unapply" + val unapplySeq: NameType = "unapplySeq" + val unbox: NameType = "unbox" + val update: NameType = "update" + val value: NameType = "value" + val view_ : NameType = "view" + val wait_ : NameType = "wait" + val withFilter: NameType = "withFilter" + val wrapRefArray: NameType = "wrapRefArray" + val zip: NameType = "zip" + + // unencoded operators + object raw { + final val AMP : NameType = "&" + final val BANG : NameType = "!" + final val BAR : NameType = "|" + final val DOLLAR: NameType = "$" + final val GE: NameType = ">=" + final val LE: NameType = "<=" + final val MINUS: NameType = "-" + final val NE: NameType = "!=" + final val PLUS : NameType = "+" + final val SLASH: NameType = "/" + final val STAR : NameType = "*" + final val TILDE: NameType = "~" + + final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG) + } + + // value-conversion methods + val toByte: NameType = "toByte" + val toShort: NameType = "toShort" + val toChar: NameType = "toChar" + val toInt: NameType = "toInt" + val toLong: NameType = "toLong" + val toFloat: NameType = "toFloat" + val toDouble: NameType = "toDouble" + } + + object tpnme extends TypeNames /*with LibraryTypeNames*/ with TypeNameMangling { + type NameType = TypeName + implicit def createNameType(name: String): TypeName = newTypeName(name) + + val REFINE_CLASS_NAME: NameType = "<refinement>" + val ANON_CLASS_NAME: NameType = "$anon" + } + + val javanme = nme.javaKeywords + + object nme extends TermNames /*with LibraryTermNames*/ with TermNameMangling { + type NameType = TermName + def createNameType(name: String): TermName = newTermName(name) + + /** Translate a String into a list of simple TypeNames and TermNames. + * In all segments before the last, type/term is determined by whether + * the following separator char is '.' or '#'. In the last segment, + * the argument "assumeTerm" determines it. Examples: + * + * package foo { + * object Lorax { object Wog ; class Wog } + * class Lorax { object Zax ; class Zax } + * } + * + * f("foo.Lorax", true) == List("foo": Term, "Lorax": Term) // object Lorax + * f("foo.Lorax", false) == List("foo": Term, "Lorax": Type) // class Lorax + * f("Lorax.Wog", true) == List("Lorax": Term, "Wog": Term) // object Wog + * f("Lorax.Wog", false) == List("Lorax": Term, "Wog": Type) // class Wog + * f("Lorax#Zax", true) == List("Lorax": Type, "Zax": Term) // object Zax + * f("Lorax#Zax", false) == List("Lorax": Type, "Zax": Type) // class Zax + * + * Note that in actual scala syntax you cannot refer to object Zax without an + * instance of Lorax, so Lorax#Zax could only mean the type. One might think + * that Lorax#Zax.type would work, but this is not accepted by the parser. + * For the purposes of referencing that object, the syntax is allowed. + */ + def segments(name: String, assumeTerm: Boolean): List[Name] = { + def mkName(str: String, term: Boolean): Name = + if (term) newTermName(str) else newTypeName(str) + + name.indexWhere(ch => ch == '.' || ch == '#') match { + // it's the last segment: the parameter tells us whether type or term + case -1 => if (name == "") scala.Nil else scala.List(mkName(name, assumeTerm)) + // otherwise, we can tell based on whether '#' or '.' is the following char. + case idx => + val (simple, div, rest) = (name take idx, name charAt idx, name drop (idx + 1)) + mkName(simple, div == '.') :: segments(rest, assumeTerm) + } + } + private def bitmapName(n: Int, suffix: String): TermName = + newTermName(BITMAP_PREFIX + suffix + n) + + /** The name of bitmaps for initialized (public or protected) lazy vals. */ + def bitmapName(n: Int): TermName = bitmapName(n, "") + + /** The name of bitmaps for initialized transient lazy vals. */ + def bitmapNameForTransient(n: Int): TermName = bitmapName(n, "trans$") + + /** The name of bitmaps for initialized private lazy vals. */ + def bitmapNameForPrivate(n: Int): TermName = bitmapName(n, "priv$") + + /** The name of bitmaps for checkinit values */ + def bitmapNameForCheckinit(n: Int): TermName = bitmapName(n, "init$") + + /** The name of bitmaps for checkinit values that have transient flag*/ + def bitmapNameForCheckinitTransient(n: Int): TermName = bitmapName(n, "inittrans$") + + /** The expanded name of `name' relative to this class `base` with given `separator` + */ + def expandedName(name: TermName, base: Symbol, separator: String = EXPAND_SEPARATOR_STRING): TermName = + newTermName(base.fullName('$') + separator + name) + + def moduleVarName(name: TermName): TermName = newTermName("" + name + MODULE_SUFFIX) + + val EXPAND_SEPARATOR_STRING = "$$" + val LOCAL_SUFFIX_STRING = " " + val ROOTPKG: TermName = "_root_" + + /** Base strings from which synthetic names are derived. */ + val BITMAP_PREFIX = "bitmap$" + val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$" + val DEFAULT_GETTER_STRING = "$default$" + val DO_WHILE_PREFIX = "doWhile$" + val EQEQ_LOCAL_VAR = "eqEqTemp$" + val EVIDENCE_PARAM_PREFIX = "evidence$" + val EXCEPTION_RESULT_PREFIX = "exceptionResult" + val INTERPRETER_IMPORT_WRAPPER = "$iw" + val INTERPRETER_LINE_PREFIX = "line" + val INTERPRETER_SYNTHVAR_PREFIX = "synthvar$" + val INTERPRETER_VAR_PREFIX = "res" + val INTERPRETER_WRAPPER_SUFFIX = "$object" + val WHILE_PREFIX = "while$" + + def getCause = sn.GetCause + def getClass_ = sn.GetClass + def getMethod_ = sn.GetMethod + def invoke_ = sn.Invoke + + val ADD = encode("+") + val AND = encode("&") + val ASR = encode(">>") + val DIV = encode("/") + val EQ = encode("==") + val EQL = encode("=") + val GE = encode(">=") + val GT = encode(">") + val HASHHASH = encode("##") + val LE = encode("<=") + val LSL = encode("<<") + val LSR = encode(">>>") + val LT = encode("<") + val MINUS = encode("-") + val MOD = encode("%") + val MUL = encode("*") + val NE = encode("!=") + val OR = encode("|") + val PLUS = encode("+") + val SUB = encode("-") + val XOR = encode("^") + val ZAND = encode("&&") + val ZOR = encode("||") + + // unary operators + val UNARY_~ = encode("unary_~") + val UNARY_+ = encode("unary_+") + val UNARY_- = encode("unary_-") + val UNARY_! = encode("unary_!") + } + + abstract class SymbolNames { + protected implicit def stringToTypeName(s: String): TypeName = newTypeName(s) + + val BeanProperty : TypeName + val BooleanBeanProperty : TypeName + val BoxedBoolean : TypeName + val BoxedCharacter : TypeName + val BoxedNumber : TypeName + val Class : TypeName + val Code : TypeName + val Delegate : TypeName + val IOOBException : TypeName // IndexOutOfBoundsException + val InvTargetException : TypeName // InvocationTargetException + val JavaSerializable : TypeName + val MethodAsObject : TypeName + val NPException : TypeName // NullPointerException + val Object : TypeName + val String : TypeName + val Throwable : TypeName + val ValueType : TypeName + + val ForName : TermName + val GetCause : TermName + val GetClass : TermName + val GetMethod : TermName + val Invoke : TermName + val JavaLang : TermName + + val Boxed: immutable.Map[TypeName, TypeName] + } + + class JavaKeywords { + private var kws: Set[TermName] = Set() + private def kw(s: String): TermName = { + val result = newTermName(s) + kws = kws + result + result + } + + final val ABSTRACTkw: TermName = kw("abstract") + final val ASSERTkw: TermName = kw("assert") + final val BOOLEANkw: TermName = kw("boolean") + final val BREAKkw: TermName = kw("break") + final val BYTEkw: TermName = kw("byte") + final val CASEkw: TermName = kw("case") + final val CATCHkw: TermName = kw("catch") + final val CHARkw: TermName = kw("char") + final val CLASSkw: TermName = kw("class") + final val CONSTkw: TermName = kw("const") + final val CONTINUEkw: TermName = kw("continue") + final val DEFAULTkw: TermName = kw("default") + final val DOkw: TermName = kw("do") + final val DOUBLEkw: TermName = kw("double") + final val ELSEkw: TermName = kw("else") + final val ENUMkw: TermName = kw("enum") + final val EXTENDSkw: TermName = kw("extends") + final val FINALkw: TermName = kw("final") + final val FINALLYkw: TermName = kw("finally") + final val FLOATkw: TermName = kw("float") + final val FORkw: TermName = kw("for") + final val IFkw: TermName = kw("if") + final val GOTOkw: TermName = kw("goto") + final val IMPLEMENTSkw: TermName = kw("implements") + final val IMPORTkw: TermName = kw("import") + final val INSTANCEOFkw: TermName = kw("instanceof") + final val INTkw: TermName = kw("int") + final val INTERFACEkw: TermName = kw("interface") + final val LONGkw: TermName = kw("long") + final val NATIVEkw: TermName = kw("native") + final val NEWkw: TermName = kw("new") + final val PACKAGEkw: TermName = kw("package") + final val PRIVATEkw: TermName = kw("private") + final val PROTECTEDkw: TermName = kw("protected") + final val PUBLICkw: TermName = kw("public") + final val RETURNkw: TermName = kw("return") + final val SHORTkw: TermName = kw("short") + final val STATICkw: TermName = kw("static") + final val STRICTFPkw: TermName = kw("strictfp") + final val SUPERkw: TermName = kw("super") + final val SWITCHkw: TermName = kw("switch") + final val SYNCHRONIZEDkw: TermName = kw("synchronized") + final val THISkw: TermName = kw("this") + final val THROWkw: TermName = kw("throw") + final val THROWSkw: TermName = kw("throws") + final val TRANSIENTkw: TermName = kw("transient") + final val TRYkw: TermName = kw("try") + final val VOIDkw: TermName = kw("void") + final val VOLATILEkw: TermName = kw("volatile") + final val WHILEkw: TermName = kw("while") + + final val keywords = { + val result = kws.toSet + kws = null + result + } + } + + private abstract class JavaNames extends SymbolNames { + final val BoxedBoolean: TypeName = "java.lang.Boolean" + final val BoxedByte: TypeName = "java.lang.Byte" + final val BoxedCharacter: TypeName = "java.lang.Character" + final val BoxedDouble: TypeName = "java.lang.Double" + final val BoxedFloat: TypeName = "java.lang.Float" + final val BoxedInteger: TypeName = "java.lang.Integer" + final val BoxedLong: TypeName = "java.lang.Long" + final val BoxedNumber: TypeName = "java.lang.Number" + final val BoxedShort: TypeName = "java.lang.Short" + final val Class: TypeName = "java.lang.Class" + final val Delegate: TypeName = tpnme.NO_NAME + final val IOOBException: TypeName = "java.lang.IndexOutOfBoundsException" + final val InvTargetException: TypeName = "java.lang.reflect.InvocationTargetException" + final val MethodAsObject: TypeName = "java.lang.reflect.Method" + final val NPException: TypeName = "java.lang.NullPointerException" + final val Object: TypeName = "java.lang.Object" + final val String: TypeName = "java.lang.String" + final val Throwable: TypeName = "java.lang.Throwable" + final val ValueType: TypeName = tpnme.NO_NAME + + final val ForName: TermName = "forName" + final val GetCause: TermName = "getCause" + final val GetClass: TermName = "getClass" + final val GetMethod: TermName = "getMethod" + final val Invoke: TermName = "invoke" + final val JavaLang: TermName = "java.lang" + + val Boxed = immutable.Map[TypeName, TypeName]( + tpnme.Boolean -> BoxedBoolean, + tpnme.Byte -> BoxedByte, + tpnme.Char -> BoxedCharacter, + tpnme.Short -> BoxedShort, + tpnme.Int -> BoxedInteger, + tpnme.Long -> BoxedLong, + tpnme.Float -> BoxedFloat, + tpnme.Double -> BoxedDouble + ) + } + + private class MSILNames extends SymbolNames { + final val BeanProperty: TypeName = tpnme.NO_NAME + final val BooleanBeanProperty: TypeName = tpnme.NO_NAME + final val BoxedBoolean: TypeName = "System.IConvertible" + final val BoxedCharacter: TypeName = "System.IConvertible" + final val BoxedNumber: TypeName = "System.IConvertible" + final val Class: TypeName = "System.Type" + final val Code: TypeName = tpnme.NO_NAME + final val Delegate: TypeName = "System.MulticastDelegate" + final val IOOBException: TypeName = "System.IndexOutOfRangeException" + final val InvTargetException: TypeName = "System.Reflection.TargetInvocationException" + final val JavaSerializable: TypeName = tpnme.NO_NAME + final val MethodAsObject: TypeName = "System.Reflection.MethodInfo" + final val NPException: TypeName = "System.NullReferenceException" + final val Object: TypeName = "System.Object" + final val String: TypeName = "System.String" + final val Throwable: TypeName = "System.Exception" + final val ValueType: TypeName = "System.ValueType" + + final val ForName: TermName = "GetType" + final val GetCause: TermName = "InnerException" /* System.Reflection.TargetInvocationException.InnerException */ + final val GetClass: TermName = "GetType" + final val GetMethod: TermName = "GetMethod" + final val Invoke: TermName = "Invoke" + final val JavaLang: TermName = "System" + + val Boxed = immutable.Map[TypeName, TypeName]( + tpnme.Boolean -> "System.Boolean", + tpnme.Byte -> "System.SByte", // a scala.Byte is signed and a System.SByte too (unlike a System.Byte) + tpnme.Char -> "System.Char", + tpnme.Short -> "System.Int16", + tpnme.Int -> "System.Int32", + tpnme.Long -> "System.Int64", + tpnme.Float -> "System.Single", + tpnme.Double -> "System.Double" + ) + } + + private class J2SENames extends JavaNames { + final val BeanProperty: TypeName = "scala.reflect.BeanProperty" + final val BooleanBeanProperty: TypeName = "scala.reflect.BooleanBeanProperty" + final val Code: TypeName = "scala.reflect.Code" + final val JavaSerializable: TypeName = "java.io.Serializable" + } + + lazy val sn: SymbolNames = + if (forMSIL) new MSILNames + else new J2SENames +} diff --git a/src/compiler/scala/reflect/common/SymbolTable.scala b/src/compiler/scala/reflect/common/SymbolTable.scala new file mode 100644 index 0000000000..4bbe0b5201 --- /dev/null +++ b/src/compiler/scala/reflect/common/SymbolTable.scala @@ -0,0 +1,112 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import util._ + +abstract class SymbolTable extends /*reflect.generic.Universe + with*/ Names + with Symbols + with Types + with Scopes + with Caches + with Definitions + with Constants + with BaseTypeSeqs + with InfoTransformers + with StdNames + with AnnotationInfos + with AnnotationCheckers + with Trees + with TreePrinters + with Positions + with TypeDebugging + with Required +{ + def rootLoader: LazyType + def log(msg: => AnyRef) + def abort(msg: String): Nothing = throw new Error(msg) + def abort(): Nothing = throw new Error() + + /** Are we compiling for Java SE ? */ + // def forJVM: Boolean + + /** Are we compiling for .NET ? */ + def forMSIL: Boolean = false + + /** A period is an ordinal number for a phase in a run. + * Phases in later runs have higher periods than phases in earlier runs. + * Later phases have higher periods than earlier phases in the same run. + */ + type Period = Int + final val NoPeriod = 0 + + /** An ordinal number for compiler runs. First run has number 1. */ + type RunId = Int + final val NoRunId = 0 + + private var ph: Phase = NoPhase + private var per = NoPeriod + + final def phase: Phase = ph + + final def phase_=(p: Phase) { + //System.out.println("setting phase to " + p) + assert((p ne null) && p != NoPhase) + ph = p + per = (currentRunId << 8) + p.id + } + + /** The current compiler run identifier. */ + def currentRunId: RunId + + /** The run identifier of the given period */ + final def runId(period: Period): RunId = period >> 8 + + /** The phase identifier of the given period */ + final def phaseId(period: Period): Phase#Id = period & 0xFF + + /** The period at the start of run that includes `period' */ + final def startRun(period: Period): Period = period & 0xFFFFFF00 + + /** The current period */ + final def currentPeriod: Period = { + //assert(per == (currentRunId << 8) + phase.id) + per + } + + /** The phase associated with given period */ + final def phaseOf(period: Period): Phase = phaseWithId(phaseId(period)) + + final def period(rid: RunId, pid: Phase#Id): Period = + (currentRunId << 8) + pid + + /** Perform given operation at given phase */ + final def atPhase[T](ph: Phase)(op: => T): T = { + val current = phase + phase = ph + try op + finally phase = current + } + final def afterPhase[T](ph: Phase)(op: => T): T = + atPhase(ph.next)(op) + + /** Break into repl debugger if assertion is true */ + // def breakIf(assertion: => Boolean, args: Any*): Unit = + // if (assertion) + // ILoop.break(args.toList) + + /** The set of all installed infotransformers */ + var infoTransformers = new InfoTransformer { + val pid = NoPhase.id + val changesBaseClasses = true + def transform(sym: Symbol, tpe: Type): Type = tpe + } + + /** The phase which has given index as identifier */ + val phaseWithId: Array[Phase] +} diff --git a/src/compiler/scala/reflect/common/Symbols.scala b/src/compiler/scala/reflect/common/Symbols.scala new file mode 100644 index 0000000000..573871fa8b --- /dev/null +++ b/src/compiler/scala/reflect/common/Symbols.scala @@ -0,0 +1,2275 @@ + /* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + + +package scala.reflect +package common + +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import util.Statistics._ +import Flags._ + +trait Symbols /* extends reflect.generic.Symbols*/ { self: SymbolTable => + import definitions._ + + private var ids = 0 + def symbolCount = ids // statistics + + val emptySymbolArray = new Array[Symbol](0) + + /** Used for deciding in the IDE whether we can interrupt the compiler */ + //protected var activeLocks = 0 + + /** Used for debugging only */ + //protected var lockedSyms = collection.immutable.Set[Symbol]() + + /** Used to keep track of the recursion depth on locked symbols */ + private var recursionTable = immutable.Map.empty[Symbol, Int] + + private var nextexid = 0 + private def freshExistentialName(suffix: String) = { + nextexid += 1 + newTypeName("_" + nextexid + suffix) + } + + /** The original owner of a class. Used by the backend to generate + * EnclosingMethod attributes. + */ + val originalOwner = mutable.HashMap[Symbol, Symbol]() + + /** The class for all symbols */ + abstract class Symbol(initOwner: Symbol, initPos: Position, initName: Name) extends HasFlags /*AbsSymbol */ { + + type FlagsType = Long + type AccessBoundaryType = Symbol + type AnnotationType = AnnotationInfo + + var rawowner = initOwner + var rawname = initName + var rawflags = 0L + + private var rawpos = initPos + val id = { ids += 1; ids } // identity displayed when -uniqid + + var validTo: Period = NoPeriod + + def pos = rawpos + def setPos(pos: Position): this.type = { this.rawpos = pos; this } + +// ------ creators ------------------------------------------------------------------- + + final def newValue(pos: Position, name: TermName) = + new TermSymbol(this, pos, name) + final def newValue(name: TermName, pos: Position = NoPosition) = + new TermSymbol(this, pos, name) + final def newVariable(pos: Position, name: TermName) = + newValue(pos, name).setFlag(MUTABLE) + final def newValueParameter(pos: Position, name: TermName) = + newValue(pos, name).setFlag(PARAM) + /** Create local dummy for template (owner of local blocks) */ + final def newLocalDummy(pos: Position) = + newValue(pos, nme.localDummyName(this)).setInfo(NoType) + final def newMethod(pos: Position, name: TermName) = + new MethodSymbol(this, pos, name).setFlag(METHOD) + final def newMethod(name: TermName, pos: Position = NoPosition) = + new MethodSymbol(this, pos, name).setFlag(METHOD) + final def newLabel(pos: Position, name: TermName) = + newMethod(pos, name).setFlag(LABEL) + final def newConstructor(pos: Position) = + newMethod(pos, nme.CONSTRUCTOR) + final def newModule(pos: Position, name: TermName, clazz: ClassSymbol) = + new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + .setModuleClass(clazz) + final def newModule(name: TermName, clazz: Symbol, pos: Position = NoPosition) = + new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + .setModuleClass(clazz.asInstanceOf[ClassSymbol]) + final def newModule(pos: Position, name: TermName) = { + val m = new ModuleSymbol(this, pos, name).setFlag(MODULE | FINAL) + m.setModuleClass(new ModuleClassSymbol(m)) + } + final def newPackage(pos: Position, name: TermName) = { + assert(name == nme.ROOT || isPackageClass) + val m = newModule(pos, name).setFlag(JAVA | PACKAGE) + m.moduleClass.setFlag(JAVA | PACKAGE) + m + } + final def newThisSym(pos: Position) = + newValue(pos, nme.this_).setFlag(SYNTHETIC) + final def newImport(pos: Position) = + newValue(pos, nme.IMPORT) + + /** @param pre type relative to which alternatives are seen. + * for instance: + * class C[T] { + * def m(x: T): T + * def m'(): T + * } + * val v: C[Int] + * + * Then v.m has symbol TermSymbol(flags = {OVERLOADED}, + * tpe = OverloadedType(C[Int], List(m, m'))) + * You recover the type of m doing a + * + * m.tpe.asSeenFrom(pre, C) (generally, owner of m, which is C here). + * + * or: + * + * pre.memberType(m) + */ + final def newOverloaded(pre: Type, alternatives: List[Symbol]): Symbol = + newValue(alternatives.head.pos, alternatives.head.name.toTermName) + .setFlag(OVERLOADED) + .setInfo(OverloadedType(pre, alternatives)) + + /** for explicit outer phase */ + final def newOuterAccessor(pos: Position) = { + val sym = newMethod(pos, nme.OUTER) + sym setFlag (STABLE | SYNTHETIC) + if (isTrait) sym setFlag DEFERRED + sym.expandName(this) + sym.referenced = this + sym + } + + final def newErrorValue(name: TermName) = + newValue(pos, name).setFlag(SYNTHETIC | IS_ERROR).setInfo(ErrorType) + + /** Symbol of a type definition type T = ... + */ + final def newAliasType(pos: Position, name: TypeName) = + new TypeSymbol(this, pos, name) + final def newAliasType(name: TypeName, pos: Position = NoPosition) = + new TypeSymbol(this, pos, name) + + /** Symbol of an abstract type type T >: ... <: ... + */ + final def newAbstractType(pos: Position, name: TypeName) = + new TypeSymbol(this, pos, name).setFlag(DEFERRED) + final def newAbstractType(name: TypeName, pos: Position = NoPosition) = + new TypeSymbol(this, pos, name).setFlag(DEFERRED) + + /** Symbol of a type parameter + */ + final def newTypeParameter(pos: Position, name: TypeName) = + newAbstractType(pos, name).setFlag(PARAM) + + /** Synthetic value parameters when parameter symbols are not available + */ + final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[Symbol]] = { + var cnt = 0 + def freshName() = { cnt += 1; newTermName("x$" + cnt) } + def param(tp: Type) = + newValueParameter(focusPos(owner.pos), freshName()).setFlag(SYNTHETIC).setInfo(tp) + argtypess map (_.map(param)) + } + + final def newExistential(pos: Position, name: TypeName): Symbol = + newAbstractType(pos, name).setFlag(EXISTENTIAL) + + final def freshExistential(suffix: String): Symbol = + newExistential(pos, freshExistentialName(suffix)) + + /** Synthetic value parameters when parameter symbols are not available. + * Calling this method multiple times will re-use the same parameter names. + */ + final def newSyntheticValueParams(argtypes: List[Type]): List[Symbol] = + newSyntheticValueParamss(List(argtypes)).head + + /** Synthetic value parameter when parameter symbol is not available. + * Calling this method multiple times will re-use the same parameter name. + */ + final def newSyntheticValueParam(argtype: Type): Symbol = + newSyntheticValueParams(List(argtype)).head + + /** Type skolems are type parameters ``seen from the inside'' + * Assuming a polymorphic method m[T], its type is a PolyType which has a TypeParameter + * with name `T' in its typeParams list. While type checking the parameters, result type and + * body of the method, there's a local copy of `T' which is a TypeSkolem. + */ + final def newTypeSkolem: Symbol = + new TypeSkolem(owner, pos, name.toTypeName, this) + .setFlag(flags) + + final def newClass(pos: Position, name: TypeName) = + new ClassSymbol(this, pos, name) + final def newClass(name: TypeName, pos: Position = NoPosition) = + new ClassSymbol(this, pos, name) + + final def newModuleClass(pos: Position, name: TypeName) = + new ModuleClassSymbol(this, pos, name) + final def newModuleClass(name: TypeName, pos: Position = NoPosition) = + new ModuleClassSymbol(this, pos, name) + + final def newAnonymousClass(pos: Position) = + newClass(pos, tpnme.ANON_CLASS_NAME) + final def newAnonymousFunctionClass(pos: Position) = + newClass(pos, tpnme.ANON_FUN_NAME) + + /** Refinement types P { val x: String; type T <: Number } + * also have symbols, they are refinementClasses + */ + final def newRefinementClass(pos: Position) = + newClass(pos, tpnme.REFINE_CLASS_NAME) + + /** Create a new getter for current symbol (which must be a field) + */ + final def newGetter: Symbol = { + val getter = owner.newMethod(focusPos(pos), nme.getterName(name)).setFlag(getterFlags(flags)) + getter.privateWithin = privateWithin + getter.setInfo(MethodType(List(), tpe)) + } + + final def newErrorClass(name: TypeName) = { + val clazz = newClass(pos, name).setFlag(SYNTHETIC | IS_ERROR) + clazz.setInfo(ClassInfoType(List(), new ErrorScope(this), clazz)) + clazz + } + + final def newErrorSymbol(name: Name): Symbol = name match { + case x: TypeName => newErrorClass(x) + case x: TermName => newErrorValue(x) + } + +// ----- locking and unlocking ------------------------------------------------------ + + // True if the symbol is unlocked. + // True if the symbol is locked but still below the allowed recursion depth. + // False otherwise + def lockOK: Boolean = { + ((rawflags & LOCKED) == 0L) || + ((settings.Yrecursion.value != 0) && + (recursionTable get this match { + case Some(n) => (n <= settings.Yrecursion.value) + case None => true })) + } + + // Lock a symbol, using the handler if the recursion depth becomes too great. + def lock(handler: => Unit) = { + if ((rawflags & LOCKED) != 0L) { + if (settings.Yrecursion.value != 0) { + recursionTable get this match { + case Some(n) => + if (n > settings.Yrecursion.value) { + handler + } else { + recursionTable += (this -> (n + 1)) + } + case None => + recursionTable += (this -> 1) + } + } else { handler } + } else { + rawflags |= LOCKED +// activeLocks += 1 +// lockedSyms += this + } + } + + // Unlock a symbol + def unlock() = { + if ((rawflags & LOCKED) != 0L) { +// activeLocks -= 1 +// lockedSyms -= this + rawflags = rawflags & ~LOCKED + if (settings.Yrecursion.value != 0) + recursionTable -= this + } + } + +// ----- tests ---------------------------------------------------------------------- + + def isTerm = false // to be overridden + def isType = false // to be overridden + def isClass = false // to be overridden + def isAliasType = false // to be overridden + def isAbstractType = false // to be overridden + private[scala] def isSkolem = false // to be overridden + + /** Is this symbol a type but not a class? */ + def isNonClassType = false // to be overridden + + override final def isTrait: Boolean = isClass && hasFlag(TRAIT | notDEFERRED) // A virtual class becomes a trait (part of DEVIRTUALIZE) + final def isAbstractClass = isClass && hasFlag(ABSTRACT) + final def isBridge = hasFlag(BRIDGE) + final def isContravariant = isType && hasFlag(CONTRAVARIANT) + final def isCovariant = isType && hasFlag(COVARIANT) + final def isEarlyInitialized: Boolean = isTerm && hasFlag(PRESUPER) + final def isExistentiallyBound = isType && hasFlag(EXISTENTIAL) + final def isImplClass = isClass && hasFlag(IMPLCLASS) // Is this symbol an implementation class for a mixin? + final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol + final def isMethod = isTerm && hasFlag(METHOD) + final def isVarargsMethod = isMethod && hasFlag(VARARGS) + final def isModule = isTerm && hasFlag(MODULE) + final def isModuleClass = isClass && hasFlag(MODULE) + final def isOverloaded = hasFlag(OVERLOADED) + final def isRefinementClass = isClass && name == tpnme.REFINE_CLASS_NAME + final def isSourceMethod = isMethod && !hasFlag(STABLE) // exclude all accessors!!! + final def isTypeParameter = isType && isParameter && !isSkolem + + /** Package tests */ + final def isEmptyPackage = isPackage && name == nme.EMPTY_PACKAGE_NAME + final def isEmptyPackageClass = isPackageClass && name == tpnme.EMPTY_PACKAGE_NAME + final def isPackage = isModule && hasFlag(PACKAGE) + final def isPackageClass = isClass && hasFlag(PACKAGE) + final def isRoot = isPackageClass && owner == NoSymbol + final def isRootPackage = isPackage && owner == NoSymbol + + /** Does this symbol denote a wrapper created by the repl? */ + final def isInterpreterWrapper = (isModule || isModuleClass) && nme.isReplWrapperName(name) + + /** Is this symbol an effective root for fullname string? + */ + def isEffectiveRoot = isRoot || isEmptyPackageClass || isInterpreterWrapper + + /** Term symbols with the exception of static parts of Java classes and packages. + */ + final def isValue = isTerm && !(isModule && hasFlag(PACKAGE | JAVA)) + + final def isVariable = isTerm && isMutable && !isMethod + + // interesting only for lambda lift. Captured variables are accessed from inner lambdas. + final def isCapturedVariable = isVariable && hasFlag(CAPTURED) + + final def isGetter = isTerm && hasAccessorFlag && !nme.isSetterName(name) + // todo: make independent of name, as this can be forged. + final def isSetter = isTerm && hasAccessorFlag && nme.isSetterName(name) + def isSetterParameter = isValueParameter && owner.isSetter + + final def hasGetter = isTerm && nme.isLocalName(name) + + final def isValueParameter = isTerm && hasFlag(PARAM) + final def isLocalDummy = isTerm && nme.isLocalDummyName(name) + final def isInitializedToDefault = !isType && hasAllFlags(DEFAULTINIT | ACCESSOR) + final def isClassConstructor = isTerm && (name == nme.CONSTRUCTOR) + final def isMixinConstructor = isTerm && (name == nme.MIXIN_CONSTRUCTOR) + final def isConstructor = isTerm && nme.isConstructorName(name) + final def isStaticModule = isModule && isStatic && !isMethod + final def isThisSym = isTerm && owner.thisSym == this + final def isError = hasFlag(IS_ERROR) + final def isErroneous = isError || isInitialized && tpe.isErroneous + final def isTypeParameterOrSkolem = isType && hasFlag(PARAM) + final def isHigherOrderTypeParameter = owner.isTypeParameterOrSkolem + final def isTypeSkolem = isSkolem && hasFlag(PARAM) + // a type symbol bound by an existential type, for instance the T in + // List[T] forSome { type T } + final def isExistentialSkolem = isExistentiallyBound && isSkolem + final def isExistentialQuantified = isExistentiallyBound && !isSkolem + + // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor + final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) + + final def isAnonymousClass = isClass && (name containsName tpnme.ANON_CLASS_NAME) + final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME) + final def isAnonOrRefinementClass = isAnonymousClass || isRefinementClass + + final def isPackageObject = isModule && name == nme.PACKAGEkw && owner.isPackageClass + final def isPackageObjectClass = isModuleClass && name.toTermName == nme.PACKAGEkw && owner.isPackageClass + final def definedInPackage = owner.isPackageClass || owner.isPackageObjectClass + final def isJavaInterface = isJavaDefined && isTrait + final def needsFlatClasses: Boolean = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass + + // not printed as prefixes + final def isPredefModule = this == PredefModule + final def isScalaPackage = (this == ScalaPackage) || (isPackageObject && owner == ScalaPackageClass) + final def isScalaPackageClass = skipPackageObject == ScalaPackageClass + + /** If this is a package object or package object class, its owner: otherwise this. + */ + final def skipPackageObject: Symbol = if (isPackageObjectClass) owner else this + + /** If this is a constructor, its owner: otherwise this. + */ + final def skipConstructor: Symbol = if (isConstructor) owner else this + + /** Conditions where we omit the prefix when printing a symbol, to avoid + * unpleasantries like Predef.String, $iw.$iw.Foo and <empty>.Bippy. + */ + final def printWithoutPrefix = !settings.debug.value && ( + isScalaPackageClass || isPredefModule || isEffectiveRoot || isAnonOrRefinementClass || isInterpreterWrapper + ) + + /** Is symbol a monomorphic type? + * assumption: if a type starts out as monomorphic, it will not acquire + * type parameters in later phases. + */ + final def isMonomorphicType = + isType && { + var is = infos + (is eq null) || { + while (is.prev ne null) { is = is.prev } + is.info.isComplete && is.info.typeParams.isEmpty + } + } + + def isStrictFP = hasAnnotation(ScalaStrictFPAttr) || (enclClass hasAnnotation ScalaStrictFPAttr) + def isSerializable = info.baseClasses.exists(p => p == SerializableClass || p == JavaSerializableClass) || hasAnnotation(SerializableAttr) // last part can be removed, @serializable annotation is deprecated + def isDeprecated = hasAnnotation(DeprecatedAttr) + def hasBridgeAnnotation = hasAnnotation(BridgeClass) + def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0) + def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1) + // !!! when annotation arguments are not literal strings, but any sort of + // assembly of strings, there is a fair chance they will turn up here not as + // Literal(const) but some arbitrary AST. However nothing in the compiler + // prevents someone from writing a @migration annotation with a calculated + // string. So this needs attention. For now the fact that migration is + // private[scala] ought to provide enough protection. + def migrationMessage = getAnnotation(MigrationAnnotationClass) flatMap { _.stringArg(2) } + def elisionLevel = getAnnotation(ElidableMethodClass) flatMap { _.intArg(0) } + def implicitNotFoundMsg = getAnnotation(ImplicitNotFoundClass) flatMap { _.stringArg(0) } + + /** Is this symbol an accessor method for outer? */ + final def isOuterAccessor = { + hasFlag(STABLE | SYNTHETIC) && + originalName == nme.OUTER + } + + /** Is this symbol an accessor method for outer? */ + final def isOuterField = { + hasFlag(SYNTHETIC) && + originalName == nme.OUTER_LOCAL + } + + /** Does this symbol denote a stable value? */ + final def isStable = + isTerm && + !isMutable && + (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) && + !(tpe.isVolatile && !hasAnnotation(uncheckedStableClass)) + + def isVirtualClass = + hasFlag(DEFERRED) && isClass + + def isVirtualTrait = + hasFlag(DEFERRED) && isTrait + + def isLiftedMethod = isMethod && hasFlag(LIFTED) + def isCaseClass = isClass && isCase + + /** Does this symbol denote the primary constructor of its enclosing class? */ + final def isPrimaryConstructor = + isConstructor && owner.primaryConstructor == this + + /** Does this symbol denote an auxiliary constructor of its enclosing class? */ + final def isAuxiliaryConstructor = + isConstructor && !isPrimaryConstructor + + /** Is this symbol a synthetic apply or unapply method in a companion object of a case class? */ + final def isCaseApplyOrUnapply = + isMethod && isCase && isSynthetic + + /** Is this symbol a trait which needs an implementation class? */ + final def needsImplClass: Boolean = + isTrait && (!isInterface || hasFlag(lateINTERFACE)) && !isImplClass + + /** Is this a symbol which exists only in the implementation class, not in its trait? */ + final def isImplOnly: Boolean = + hasFlag(PRIVATE) || + (owner.isImplClass || owner.isTrait) && + ((hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR | MODULE) || isConstructor) || + (hasFlag(LIFTED) && isModule && isMethod)) + + /** Is this symbol a module variable? + * This used to have to test for MUTABLE to distinguish the overloaded + * MODULEVAR/SYNTHETICMETH flag, but now SYNTHETICMETH is gone. + */ + final def isModuleVar = hasFlag(MODULEVAR) + + /** Is this symbol static (i.e. with no outer instance)? */ + final def isStatic: Boolean = + hasFlag(STATIC) || isRoot || owner.isStaticOwner + + /** Is this symbol a static constructor? */ + final def isStaticConstructor: Boolean = + isStaticMember && isClassConstructor + + /** Is this symbol a static member of its class? (i.e. needs to be implemented as a Java static?) */ + final def isStaticMember: Boolean = + hasFlag(STATIC) || owner.isImplClass + + /** Does this symbol denote a class that defines static symbols? */ + final def isStaticOwner: Boolean = + isPackageClass || isModuleClass && isStatic + + /** Is this symbol effectively final? I.e, it cannot be overridden */ + final def isEffectivelyFinal: Boolean = isFinal || isTerm && ( + hasFlag(PRIVATE) || isLocal || owner.isClass && owner.hasFlag(FINAL | MODULE)) + + /** Is this symbol locally defined? I.e. not accessed from outside `this' instance */ + final def isLocal: Boolean = owner.isTerm + + /** Is this symbol a constant? */ + final def isConstant: Boolean = + isStable && (tpe match { + case ConstantType(_) => true + case PolyType(_, ConstantType(_)) => true + case MethodType(_, ConstantType(_)) => true + case NullaryMethodType(ConstantType(_)) => true + case _ => false + }) + + /** Is this class nested in another class or module (not a package)? */ + final def isNestedClass: Boolean = + isClass && !isRoot && !owner.isPackageClass + + /** Is this class locally defined? + * A class is local, if + * - it is anonymous, or + * - its owner is a value + * - it is defined within a local class + */ + final def isLocalClass: Boolean = + isClass && (isAnonOrRefinementClass || isLocal || + !owner.isPackageClass && owner.isLocalClass) + +/* code for fixing nested objects + override final def isModuleClass: Boolean = + super.isModuleClass && !isExpandedModuleClass +*/ + /** Is this class or type defined as a structural refinement type? + */ + final def isStructuralRefinement: Boolean = + (isClass || isType || isModule) && info.normalize/*.underlying*/.isStructuralRefinement + + + /** Is this symbol a member of class `clazz' + */ + def isMemberOf(clazz: Symbol) = + clazz.info.member(name).alternatives contains this + + /** A a member of class `base' is incomplete if + * (1) it is declared deferred or + * (2) it is abstract override and its super symbol in `base' is + * nonexistent or incomplete. + * + * @param base ... + * @return ... + */ + final def isIncompleteIn(base: Symbol): Boolean = + this.isDeferred || + (this hasFlag ABSOVERRIDE) && { + val supersym = superSymbol(base) + supersym == NoSymbol || supersym.isIncompleteIn(base) + } + + // Does not always work if the rawInfo is a SourcefileLoader, see comment + // in "def coreClassesFirst" in Global. + final def exists: Boolean = + this != NoSymbol && (!owner.isPackageClass || { rawInfo.load(this); rawInfo != NoType }) + + final def isInitialized: Boolean = + validTo != NoPeriod + + final def isStableClass: Boolean = { + def hasNoAbstractTypeMember(clazz: Symbol): Boolean = + (clazz hasFlag STABLE) || { + var e = clazz.info.decls.elems + while ((e ne null) && !(e.sym.isAbstractType && info.member(e.sym.name) == e.sym)) + e = e.next + e == null + } + def checkStable() = + (info.baseClasses forall hasNoAbstractTypeMember) && { setFlag(STABLE); true } + isClass && (hasFlag(STABLE) || checkStable()) + } + + + /** The variance of this symbol as an integer */ + final def variance: Int = + if (isCovariant) 1 + else if (isContravariant) -1 + else 0 + +// ------ owner attribute -------------------------------------------------------------- + + def owner: Symbol = rawowner + final def owner_=(owner: Symbol) { + if (originalOwner contains this) () + else originalOwner(this) = rawowner + + rawowner = owner + } + private[Symbols] def flattenName(): Name = { + // TODO: this assertion causes me a lot of trouble in the interpeter in situations + // where everything proceeds smoothly if there's no assert. I don't think calling "name" + // on a symbol is the right place to throw fatal exceptions if things don't look right. + // It really hampers exploration. + assert(rawowner.isClass, "fatal: %s has non-class owner %s after flatten.".format(rawname + idString, rawowner)) + nme.flattenedName(rawowner.name, rawname) + } + + def ownerChain: List[Symbol] = this :: owner.ownerChain + def enclClassChain: List[Symbol] = { + if (this eq NoSymbol) Nil + else if (isClass && !isPackageClass) this :: owner.enclClassChain + else owner.enclClassChain + } + + def ownersIterator: Iterator[Symbol] = new Iterator[Symbol] { + private var current = Symbol.this + def hasNext = current ne NoSymbol + def next = { val r = current; current = current.owner; r } + } + + /** same as ownerChain contains sym, but more efficient, and + * with a twist for refinement classes. A refinement class + * has a transowner X if an of its parents has transowner X. + */ + def hasTransOwner(sym: Symbol): Boolean = { + var o = this + while ((o ne sym) && (o ne NoSymbol)) o = o.owner + (o eq sym) || + isRefinementClass && (info.parents exists (_.typeSymbol.hasTransOwner(sym))) + } + +// ------ name attribute -------------------------------------------------------------- + + def name: Name = rawname + + final def name_=(name: Name) { + if (name != rawname) { + if (owner.isClass) { + var ifs = owner.infos + while (ifs != null) { + ifs.info.decls.rehash(this, name) + ifs = ifs.prev + } + } + rawname = name + } + } + + /** If this symbol has an expanded name, its original name, otherwise its name itself. + * @see expandName + */ + def originalName = nme.originalName(name) + + /** The name of the symbol before decoding, e.g. `\$eq\$eq` instead of `==`. + */ + def encodedName: String = name.toString + + /** The decoded name of the symbol, e.g. `==` instead of `\$eq\$eq`. + */ + def decodedName: String = stripLocalSuffix(NameTransformer.decode(encodedName)) + + /** The encoded full path name of this symbol, where outer names and inner names + * are separated by `separator` characters. + * Never translates expansions of operators back to operator symbol. + * Never adds id. + */ + final def fullName(separator: Char): String = stripLocalSuffix { + if (isRoot || isRootPackage || this == NoSymbol) this.toString + else if (owner.isEffectiveRoot) encodedName + else owner.enclClass.fullName(separator) + separator + encodedName + } + + private def stripLocalSuffix(s: String) = s stripSuffix nme.LOCAL_SUFFIX_STRING + + /** The encoded full path name of this symbol, where outer names and inner names + * are separated by periods. + */ + final def fullName: String = fullName('.') + +// ------ flags attribute -------------------------------------------------------------- + + final def flags: Long = { + val fs = rawflags & phase.flagMask + (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) + } + final def flags_=(fs: Long) = rawflags = fs + final def setFlag(mask: Long): this.type = { rawflags = rawflags | mask; this } + final def resetFlag(mask: Long): this.type = { rawflags = rawflags & ~mask; this } + final def getFlag(mask: Long): Long = flags & mask + final def resetFlags { rawflags = rawflags & TopLevelCreationFlags } + + /** Does symbol have ANY flag in `mask` set? */ + final def hasFlag(mask: Long): Boolean = (flags & mask) != 0L + + /** Does symbol have ALL the flags in `mask` set? */ + final def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask + + /** The class or term up to which this symbol is accessible, + * or RootClass if it is public. + */ + def accessBoundary(base: Symbol): Symbol = { + if (hasFlag(PRIVATE) || isLocal) owner + else if (hasAccessBoundary && !phase.erasedTypes) privateWithin + else if (hasFlag(PROTECTED)) base + else RootClass + } + + def isLessAccessibleThan(other: Symbol): Boolean = { + val tb = this.accessBoundary(owner) + val ob1 = other.accessBoundary(owner) + val ob2 = ob1.linkedClassOfClass + var o = tb + while (o != NoSymbol && o != ob1 && o != ob2) { + o = o.owner + } + o != NoSymbol && o != tb + } + + /** See comment in HasFlags for how privateWithin combines with flags. + */ + private[this] var _privateWithin: Symbol = _ + def privateWithin = _privateWithin + def privateWithin_=(sym: Symbol) { _privateWithin = sym } + + /** Does symbol have a private or protected qualifier set? */ + final def hasAccessBoundary = (privateWithin != null) && (privateWithin != NoSymbol) + +// ------ info and type ------------------------------------------------------------------- + + private[Symbols] var infos: TypeHistory = null + + /** Get type. The type of a symbol is: + * for a type symbol, the type corresponding to the symbol itself, + * @M you should use tpeHK for a type symbol with type parameters if + * the kind of the type need not be *, as tpe introduces dummy arguments + * to generate a type of kind * + * for a term symbol, its usual type + */ + def tpe: Type = info + + /** Get type info associated with symbol at current phase, after + * ensuring that symbol is initialized (i.e. type is completed). + */ + def info: Type = try { + var cnt = 0 + while (validTo == NoPeriod) { + //if (settings.debug.value) System.out.println("completing " + this);//DEBUG + assert(infos ne null, this.name) + assert(infos.prev eq null, this.name) + val tp = infos.info + //if (settings.debug.value) System.out.println("completing " + this.rawname + tp.getClass());//debug + + if ((rawflags & LOCKED) != 0L) { // rolled out once for performance + lock { + setInfo(ErrorType) + throw CyclicReference(this, tp) + } + } else { + rawflags |= LOCKED +// activeLocks += 1 + // lockedSyms += this + } + val current = phase + try { + phase = phaseOf(infos.validFrom) + tp.complete(this) + } finally { + unlock() + phase = current + } + cnt += 1 + // allow for two completions: + // one: sourceCompleter to LazyType, two: LazyType to completed type + if (cnt == 3) abort("no progress in completing " + this + ":" + tp) + } + val result = rawInfo + result + } catch { + case ex: CyclicReference => + if (settings.debug.value) println("... trying to complete "+this) + throw ex + } + + def info_=(info: Type) { + assert(info ne null) + infos = TypeHistory(currentPeriod, info, null) + unlock() + validTo = if (info.isComplete) currentPeriod else NoPeriod + } + + /** Set initial info. */ + def setInfo(info: Type): this.type = { info_=(info); this } + + def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info.atOwner(this)) + + /** Set new info valid from start of this phase. */ + final def updateInfo(info: Type): Symbol = { + assert(phaseId(infos.validFrom) <= phase.id) + if (phaseId(infos.validFrom) == phase.id) infos = infos.prev + infos = TypeHistory(currentPeriod, info, infos) + validTo = if (info.isComplete) currentPeriod else NoPeriod + this + } + + def hasRawInfo: Boolean = infos ne null + + /** Return info without checking for initialization or completing */ + def rawInfo: Type = { + var infos = this.infos + assert(infos != null) + val curPeriod = currentPeriod + val curPid = phaseId(curPeriod) + + if (validTo != NoPeriod) { + // skip any infos that concern later phases + while (curPid < phaseId(infos.validFrom) && infos.prev != null) + infos = infos.prev + + if (validTo < curPeriod) { + // adapt any infos that come from previous runs + val current = phase + try { + infos = adaptInfos(infos) + + //assert(runId(validTo) == currentRunId, name) + //assert(runId(infos.validFrom) == currentRunId, name) + + if (validTo < curPeriod) { + var itr = infoTransformers.nextFrom(phaseId(validTo)) + infoTransformers = itr; // caching optimization + while (itr.pid != NoPhase.id && itr.pid < current.id) { + phase = phaseWithId(itr.pid) + val info1 = itr.transform(this, infos.info) + if (info1 ne infos.info) { + infos = TypeHistory(currentPeriod + 1, info1, infos) + this.infos = infos + } + validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform + itr = itr.next + } + validTo = if (itr.pid == NoPhase.id) curPeriod + else period(currentRunId, itr.pid) + } + } finally { + phase = current + } + } + } + infos.info + } + + // adapt to new run in fsc. + private def adaptInfos(infos: TypeHistory): TypeHistory = + if (infos == null || runId(infos.validFrom) == currentRunId) { + infos + } else { + val prev1 = adaptInfos(infos.prev) + if (prev1 ne infos.prev) prev1 + else { + def adaptToNewRun(info: Type): Type = + if (isPackageClass) info else adaptToNewRunMap(info) + val pid = phaseId(infos.validFrom) + validTo = period(currentRunId, pid) + phase = phaseWithId(pid) + val info1 = adaptToNewRun(infos.info) + if (info1 eq infos.info) { + infos.validFrom = validTo + infos + } else { + this.infos = TypeHistory(validTo, info1, prev1) + this.infos + } + } + } + + /** Initialize the symbol */ + final def initialize: this.type = { + if (!isInitialized) info + this + } + + /** Was symbol's type updated during given phase? */ + final def isUpdatedAt(pid: Phase#Id): Boolean = { + var infos = this.infos + while ((infos ne null) && phaseId(infos.validFrom) != pid + 1) infos = infos.prev + infos ne null + } + + /** Was symbol's type updated during given phase? */ + final def hasTypeAt(pid: Phase#Id): Boolean = { + var infos = this.infos + while ((infos ne null) && phaseId(infos.validFrom) > pid) infos = infos.prev + infos ne null + } + + /** Modify term symbol's type so that a raw type C is converted to an existential C[_] + * + * This is done in checkAccessible and overriding checks in refchecks + * We can't do this on class loading because it would result in infinite cycles. + */ + final def cookJavaRawInfo() { + if (hasFlag(TRIEDCOOKING)) return else setFlag(TRIEDCOOKING) // only try once... + val oldInfo = info + doCookJavaRawInfo() + } + + protected def doCookJavaRawInfo(): Unit + + + /** The type constructor of a symbol is: + * For a type symbol, the type corresponding to the symbol itself, + * excluding parameters. + * Not applicable for term symbols. + */ + def typeConstructor: Type = + abort("typeConstructor inapplicable for " + this) + + /** @M -- tpe vs tpeHK: + * Symbol::tpe creates a TypeRef that has dummy type arguments to get a type of kind * + * Symbol::tpeHK creates a TypeRef without type arguments, but with type params --> higher-kinded if non-empty list of tpars + * calling tpe may hide errors or introduce spurious ones + * (e.g., when deriving a type from the symbol of a type argument that must be higher-kinded) + * as far as I can tell, it only makes sense to call tpe in conjunction with a substitution that replaces the generated dummy type arguments by their actual types + */ + def tpeHK = if (isType) typeConstructor else tpe // @M! used in memberType + + /** The type parameters of this symbol, without ensuring type completion. + * assumption: if a type starts out as monomorphic, it will not acquire + * type parameters later. + */ + def unsafeTypeParams: List[Symbol] = + if (isMonomorphicType) List() + else { + val current = phase + try { + while ((phase.prev ne NoPhase) && phase.prev.keepsTypeParams) phase = phase.prev + if (phase ne current) phase = phase.next + if (settings.debug.value && settings.verbose.value && (phase ne current)) + log("checking unsafeTypeParams(" + this + ") at: " + current + " reading at: " + phase) + rawInfo.typeParams + } finally { + phase = current + } + } + + /** The type parameters of this symbol. + * assumption: if a type starts out as monomorphic, it will not acquire + * type parameters later. + */ + def typeParams: List[Symbol] = + if (isMonomorphicType) + List() + else { + if (validTo == NoPeriod) { + val current = phase + try { + phase = phaseOf(infos.validFrom) + rawInfo.load(this) + } finally { + phase = current + } + } + rawInfo.typeParams + } + + /** The value parameter sections of this symbol. + */ + def paramss: List[List[Symbol]] = info.paramss + def hasParamWhich(cond: Symbol => Boolean) = paramss exists (_ exists cond) + + /** The least proper supertype of a class; includes all parent types + * and refinement where needed. You need to compute that in a situation like this: + * { + * class C extends P { ... } + * new C + * } + */ + def classBound: Type = { + val tp = refinedType(info.parents, owner) + val thistp = tp.typeSymbol.thisType + val oldsymbuf = new ListBuffer[Symbol] + val newsymbuf = new ListBuffer[Symbol] + for (sym <- info.decls.toList) { + // todo: what about public references to private symbols? + if (sym.isPublic && !sym.isConstructor) { + oldsymbuf += sym + newsymbuf += ( + if (sym.isClass) + tp.typeSymbol.newAbstractType(sym.pos, sym.name.toTypeName).setInfo(sym.existentialBound) + else + sym.cloneSymbol(tp.typeSymbol)) + } + } + val oldsyms = oldsymbuf.toList + val newsyms = newsymbuf.toList + for (sym <- newsyms) { + addMember(thistp, tp, sym.setInfo(sym.info.substThis(this, thistp).substSym(oldsyms, newsyms))) + } + tp + } + + /** If we quantify existentially over this symbol, + * the bound of the type variable that stands for it + * pre: symbol is a term, a class, or an abstract type (no alias type allowed) + */ + def existentialBound: Type = + if (this.isClass) + polyType(this.typeParams, TypeBounds.upper(this.classBound)) + else if (this.isAbstractType) + this.info + else if (this.isTerm) + TypeBounds.upper(intersectionType(List(this.tpe, SingletonClass.tpe))) + else + abort("unexpected alias type: "+this) + + /** Reset symbol to initial state + */ + def reset(completer: Type) { + resetFlags + infos = null + validTo = NoPeriod + //limit = NoPhase.id + setInfo(completer) + } + + /** + * Adds the interface scala.Serializable to the parents of a ClassInfoType. + * Note that the tree also has to be updated accordingly. + */ + def makeSerializable() { + info match { + case ci @ ClassInfoType(_, _, _) => + updateInfo(ci.copy(parents = ci.parents ::: List(SerializableClass.tpe))) + case i => + abort("Only ClassInfoTypes can be made serializable: "+ i) + } + } + +// ----- setters implemented in selected subclasses ------------------------------------- + + def typeOfThis_=(tp: Type) { throw new UnsupportedOperationException("typeOfThis_= inapplicable for " + this) } + def sourceModule_=(sym: Symbol) { throw new UnsupportedOperationException("sourceModule_= inapplicable for " + this) } + def addChild(sym: Symbol) { throw new UnsupportedOperationException("addChild inapplicable for " + this) } + +// ----- annotations ------------------------------------------------------------ + + private var rawannots: List[AnnotationInfoBase] = Nil + def rawAnnotations = rawannots + + /* Used in namer to check whether annotations were already assigned or not */ + def hasAssignedAnnotations = rawannots.nonEmpty + + /** After the typer phase (before, look at the definition's Modifiers), contains + * the annotations attached to member a definition (class, method, type, field). + */ + def annotations: List[AnnotationInfo] = { + // .initialize: the type completer of the symbol parses the annotations, + // see "def typeSig" in Namers + val annots1 = initialize.rawannots map { + case x: LazyAnnotationInfo => x.annot() + case x: AnnotationInfo => x + } filterNot (_.atp.isError) + rawannots = annots1 + annots1 + } + + def setAnnotations(annots: List[AnnotationInfoBase]): this.type = { + this.rawannots = annots + this + } + + def addAnnotation(annot: AnnotationInfo) { + setAnnotations(annot :: this.rawannots) + } + + /** Does this symbol have an annotation of the given class? */ + def hasAnnotation(cls: Symbol) = + getAnnotation(cls).isDefined + + def getAnnotation(cls: Symbol): Option[AnnotationInfo] = + annotations find (_.atp.typeSymbol == cls) + + /** Remove all annotations matching the given class. */ + def removeAnnotation(cls: Symbol): Unit = + setAnnotations(annotations filterNot (_.atp.typeSymbol == cls)) + +// ------ comparisons ---------------------------------------------------------------- + + /** A total ordering between symbols that refines the class + * inheritance graph (i.e. subclass.isLess(superclass) always holds). + * the ordering is given by: (_.isType, -_.baseTypeSeq.length) for type symbols, followed by `id'. + */ + final def isLess(that: Symbol): Boolean = { + def baseTypeSeqLength(sym: Symbol) = + if (sym.isAbstractType) 1 + sym.info.bounds.hi.baseTypeSeq.length + else sym.info.baseTypeSeq.length + if (this.isType) + (that.isType && + { val diff = baseTypeSeqLength(this) - baseTypeSeqLength(that) + diff > 0 || diff == 0 && this.id < that.id }) + else + that.isType || this.id < that.id + } + + /** A partial ordering between symbols. + * (this isNestedIn that) holds iff this symbol is defined within + * a class or method defining that symbol + */ + final def isNestedIn(that: Symbol): Boolean = + owner == that || owner != NoSymbol && (owner isNestedIn that) + + /** Is this class symbol a subclass of that symbol? */ + final def isNonBottomSubClass(that: Symbol): Boolean = + this == that || this.isError || that.isError || + info.baseTypeIndex(that) >= 0 + + final def isSubClass(that: Symbol): Boolean = { + isNonBottomSubClass(that) || + this == NothingClass || + this == NullClass && + (that == AnyClass || + that != NothingClass && (that isSubClass AnyRefClass)) + } + final def isNumericSubClass(that: Symbol): Boolean = + definitions.isNumericSubClass(this, that) + +// ------ overloaded alternatives ------------------------------------------------------ + + def alternatives: List[Symbol] = + if (hasFlag(OVERLOADED)) info.asInstanceOf[OverloadedType].alternatives + else List(this) + + def filter(cond: Symbol => Boolean): Symbol = + if (hasFlag(OVERLOADED)) { + //assert(info.isInstanceOf[OverloadedType], "" + this + ":" + info);//DEBUG + val alts = alternatives + val alts1 = alts filter cond + if (alts1 eq alts) this + else if (alts1.isEmpty) NoSymbol + else if (alts1.tail.isEmpty) alts1.head + else owner.newOverloaded(info.prefix, alts1) + } else if (this == NoSymbol || cond(this)) { + this + } else NoSymbol + + def suchThat(cond: Symbol => Boolean): Symbol = { + val result = filter(cond) + assert(!(result hasFlag OVERLOADED), result.alternatives) + result + } + +// ------ cloneing ------------------------------------------------------------------- + + /** A clone of this symbol */ + final def cloneSymbol: Symbol = + cloneSymbol(owner) + + /** A clone of this symbol, but with given owner */ + final def cloneSymbol(owner: Symbol): Symbol = { + val newSym = cloneSymbolImpl(owner) + newSym.privateWithin = privateWithin + newSym.setInfo(info.cloneInfo(newSym)) + .setFlag(this.rawflags).setAnnotations(this.annotations) + } + + /** Internal method to clone a symbol's implementation without flags or type + */ + def cloneSymbolImpl(owner: Symbol): Symbol + +// ------ access to related symbols -------------------------------------------------- + + /** The next enclosing class */ + def enclClass: Symbol = if (isClass) this else owner.enclClass + + /** The next enclosing method */ + def enclMethod: Symbol = if (isSourceMethod) this else owner.enclMethod + + /** The primary constructor of a class */ + def primaryConstructor: Symbol = { + var c = info.decl( + if (isTrait || isImplClass) nme.MIXIN_CONSTRUCTOR + else nme.CONSTRUCTOR) + c = if (c hasFlag OVERLOADED) c.alternatives.head else c + //assert(c != NoSymbol) + c + } + + /** The self symbol of a class with explicit self type, or else the + * symbol itself. + */ + def thisSym: Symbol = this + + /** The type of `this' in a class, or else the type of the symbol itself. */ + def typeOfThis = thisSym.tpe + + /** If symbol is a class, the type <code>this.type</code> in this class, + * otherwise <code>NoPrefix</code>. + * We always have: thisType <:< typeOfThis + */ + def thisType: Type = NoPrefix + + /** Return every accessor of a primary constructor parameter in this case class. + * The scope declarations may be out of order because fields with less than private + * access are first given a regular getter, then a new renamed getter which comes + * later in the declaration list. For this reason we have to pinpoint the + * right accessors by starting with the original fields (which will be in the right + * order) and looking for getters with applicable names. The getters may have the + * standard name "foo" or may have been renamed to "foo$\d+" in SyntheticMethods. + * See ticket #1373. + */ + final def caseFieldAccessors: List[Symbol] = { + val allWithFlag = info.decls.toList filter (_.isCaseAccessor) + val (accessors, fields) = allWithFlag partition (_.isMethod) + + def findAccessor(field: Symbol): Symbol = { + // There is another renaming the field may have undergone, for instance as in + // ticket #2175: case class Property[T](private var t: T), t becomes Property$$t. + // So we use the original name everywhere. + val getterName = nme.getterName(field.originalName) + + // Note this is done in two passes intentionally, to ensure we pick up the original + // getter if present before looking for the renamed getter. + def origGetter = accessors find (_.originalName == getterName) + def renamedGetter = accessors find (_.originalName startsWith (getterName + "$")) + val accessorName = origGetter orElse renamedGetter + + // This fails more gracefully rather than throw an Error as it used to because + // as seen in #2625, we can reach this point with an already erroneous tree. + accessorName getOrElse NoSymbol + // throw new Error("Could not find case accessor for %s in %s".format(field, this)) + } + + fields map findAccessor + } + + final def constrParamAccessors: List[Symbol] = + info.decls.toList filter (sym => !sym.isMethod && sym.isParamAccessor) + + /** The symbol accessed by this accessor (getter or setter) function. */ + final def accessed: Symbol = accessed(owner.info) + + /** The symbol accessed by this accessor function, but with given owner type */ + final def accessed(ownerTp: Type): Symbol = { + assert(hasAccessorFlag) + ownerTp.decl(nme.getterToLocal(if (isSetter) nme.setterToGetter(name) else name)) + } + + /** The module corresponding to this module class (note that this + * is not updated when a module is cloned), or NoSymbol if this is not a ModuleClass + */ + def sourceModule: Symbol = NoSymbol + + /** The implementation class of a trait */ + final def implClass: Symbol = owner.info.decl(nme.implClassName(name)) + + /** The class that is logically an outer class of given `clazz'. + * This is the enclosing class, except for classes defined locally to constructors, + * where it is the outer class of the enclosing class + */ + final def outerClass: Symbol = + if (owner.isClass) owner + else if (isClassLocalToConstructor) owner.enclClass.outerClass + else owner.outerClass + + /** For a paramaccessor: a superclass paramaccessor for which this symbol + * is an alias, NoSymbol for all others + */ + def alias: Symbol = NoSymbol + + /** For a lazy value, its lazy accessor. NoSymbol for all others */ + def lazyAccessor: Symbol = NoSymbol + + /** If this is a lazy value, the lazy accessor; otherwise this symbol. */ + def lazyAccessorOrSelf: Symbol = if (isLazy) lazyAccessor else this + + /** For an outer accessor: The class from which the outer originates. + * For all other symbols: NoSymbol + */ + def outerSource: Symbol = NoSymbol + + /** The superclass of this class */ + def superClass: Symbol = if (info.parents.isEmpty) NoSymbol else info.parents.head.typeSymbol + + /** The directly or indirectly inherited mixins of this class + * except for mixin classes inherited by the superclass. Mixin classes appear + * in linearization order. + */ + def mixinClasses: List[Symbol] = { + val sc = superClass + ancestors takeWhile (sc ne) + } + + /** All directly or indirectly inherited classes. + */ + def ancestors: List[Symbol] = info.baseClasses drop 1 + + /** The package class containing this symbol, or NoSymbol if there + * is not one. */ + def enclosingPackageClass: Symbol = + if (this == NoSymbol) this else { + var packSym = this.owner + while (packSym != NoSymbol && !packSym.isPackageClass) + packSym = packSym.owner + packSym + } + + /** The package containing this symbol, or NoSymbol if there + * is not one. */ + def enclosingPackage: Symbol = { + val packSym = enclosingPackageClass + if (packSym != NoSymbol) packSym.companionModule + else packSym + } + + /** Return the original enclosing method of this symbol. It should return + * the same thing as enclMethod when called before lambda lift, + * but it preserves the original nesting when called afterwards. + */ + def originalEnclosingMethod: Symbol = { + if (isMethod) this + else { + val owner = originalOwner.getOrElse(this, rawowner) + if (isLocalDummy) owner.enclClass.primaryConstructor + else owner.originalEnclosingMethod + } + } + + /** The method or class which logically encloses the current symbol. + * If the symbol is defined in the initialization part of a template + * this is the template's primary constructor, otherwise it is + * the physically enclosing method or class. + * + * Example 1: + * + * def f() { val x = { def g() = ...; g() } } + * + * In this case the owner chain of `g' is `x', followed by `f' and + * `g.logicallyEnclosingMember == f`. + * + * Example 2: + * + * class C { + * def <init> = { ... } + * val x = { def g() = ...; g() } } + * } + * + * In this case the owner chain of `g' is `x', followed by `C' but + * g.logicallyEnclosingMember is the primary constructor symbol `<init>' + * (or, for traits: `$init') of `C'. + * + */ + def logicallyEnclosingMember: Symbol = + if (isLocalDummy) enclClass.primaryConstructor + else if (isMethod || isClass) this + else owner.logicallyEnclosingMember + + /** The top-level class containing this symbol */ + def toplevelClass: Symbol = + if (owner.isPackageClass) { + if (isClass) this else moduleClass + } else owner.toplevelClass + + /** Is this symbol defined in the same scope and compilation unit as `that' symbol? + */ + def isCoDefinedWith(that: Symbol) = ( + (this.rawInfo ne NoType) && + (this.owner == that.owner) && { + !this.owner.isPackageClass || + (this.sourceFile eq null) || + (that.sourceFile eq null) || + (this.sourceFile == that.sourceFile) || { + // recognize companion object in separate file and fail, else compilation + // appears to succeed but highly opaque errors come later: see bug #1286 + if (this.sourceFile.path != that.sourceFile.path) + throw InvalidCompanions(this, that) + + false + } + } + ) + + /** The internal representation of classes and objects: + * + * class Foo is "the class" or sometimes "the plain class" + * object Foo is "the module" + * class Foo$ is "the module class" (invisible to the user: it implements object Foo) + * + * class Foo < + * ^ ^ (2) \ + * | | | \ + * | (5) | (3) + * | | | \ + * (1) v v \ + * object Foo (4)-> > class Foo$ + * + * (1) companionClass + * (2) companionModule + * (3) linkedClassOfClass + * (4) moduleClass + * (5) companionSymbol + */ + + /** For a module or case factory: the class with the same name in the same package. + * For all others: NoSymbol + * Note: does not work for classes owned by methods, see Namers.companionClassOf + * + * object Foo . companionClass --> class Foo + */ + final def companionClass: Symbol = { + if (this != NoSymbol) + flatOwnerInfo.decl(name.toTypeName).suchThat(_ isCoDefinedWith this) + else NoSymbol + } + + /** A helper method that factors the common code used the discover a + * companion module of a class. If a companion module exists, its symbol is + * returned, otherwise, `NoSymbol` is returned. The method assumes that + * `this` symbol has already been checked to be a class (using `isClass`). + */ + private final def companionModule0: Symbol = + flatOwnerInfo.decl(name.toTermName).suchThat( + sym => sym.hasFlag(MODULE) && (sym isCoDefinedWith this) && !sym.isMethod) + + /** For a class: the module or case class factory with the same name in the same package. + * For all others: NoSymbol + * Note: does not work for modules owned by methods, see Namers.companionModuleOf + * + * class Foo . companionModule --> object Foo + */ + final def companionModule: Symbol = + if (isClass && !isRefinementClass) companionModule0 + else NoSymbol + + /** For a module: its linked class + * For a plain class: its linked module or case factory. + * Note: does not work for modules owned by methods, see Namers.companionSymbolOf + * + * class Foo <-- companionSymbol --> object Foo + */ + final def companionSymbol: Symbol = + if (isTerm) companionClass + else if (isClass) companionModule0 + else NoSymbol + + /** For a module class: its linked class + * For a plain class: the module class of its linked module. + * + * class Foo <-- linkedClassOfClass --> class Foo$ + */ + final def linkedClassOfClass: Symbol = + if (isModuleClass) companionClass else companionModule.moduleClass + + /** + * Returns the rawInfo of the owner. If the current phase has flat classes, + * it first applies all pending type maps to this symbol. + * + * assume this is the ModuleSymbol for B in the following definition: + * package p { class A { object B { val x = 1 } } } + * + * The owner after flatten is "package p" (see "def owner"). The flatten type map enters + * symbol B in the decls of p. So to find a linked symbol ("object B" or "class B") + * we need to apply flatten to B first. Fixes #2470. + */ + private final def flatOwnerInfo: Type = { + if (needsFlatClasses) + info + owner.rawInfo + } + + /** If this symbol is an implementation class, its interface, otherwise the symbol itself + * The method follows two strategies to determine the interface. + * - during or after erasure, it takes the last parent of the implementation class + * (which is always the interface, by convention) + * - before erasure, it looks up the interface name in the scope of the owner of the class. + * This only works for implementation classes owned by other classes or traits. + */ + final def toInterface: Symbol = + if (isImplClass) { + val result = + if (phase.next.erasedTypes) { + assert(!tpe.parents.isEmpty, this) + tpe.parents.last.typeSymbol + } else { + owner.info.decl(nme.interfaceName(name)) + } + assert(result != NoSymbol, this) + result + } else this + + /** The module class corresponding to this module. + */ + def moduleClass: Symbol = NoSymbol + + /** The non-private symbol whose type matches the type of this symbol + * in in given class. + * + * @param ofclazz The class containing the symbol's definition + * @param site The base type from which member types are computed + */ + final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = + ofclazz.info.nonPrivateDecl(name).filter(sym => + !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) + + /** The non-private member of `site' whose type and name match the type of this symbol + */ + final def matchingSymbol(site: Type, admit: Long = 0L): Symbol = + site.nonPrivateMemberAdmitting(name, admit).filter(sym => + !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) + + /** The symbol overridden by this symbol in given class `ofclazz'. + * @pre 'ofclazz' is a base class of this symbol's owner. + */ + final def overriddenSymbol(ofclazz: Symbol): Symbol = + if (isClassConstructor) NoSymbol else matchingSymbol(ofclazz, owner.thisType) + + /** The symbol overriding this symbol in given subclass `ofclazz' + * @pre: `ofclazz' is a subclass of this symbol's owner + */ + final def overridingSymbol(ofclazz: Symbol): Symbol = + if (isClassConstructor) NoSymbol else matchingSymbol(ofclazz, ofclazz.thisType) + + /** Returns all symbols overriden by this symbol + */ + final def allOverriddenSymbols: List[Symbol] = + if (!owner.isClass) Nil + else owner.ancestors map overriddenSymbol filter (_ != NoSymbol) + + /** Returns all symbols overridden by this symbol, plus all matching symbols + * defined in parents of the selftype + */ + final def extendedOverriddenSymbols: List[Symbol] = + if (!owner.isClass) Nil + else owner.thisSym.ancestors map overriddenSymbol filter (_ != NoSymbol) + + /** The symbol accessed by a super in the definition of this symbol when + * seen from class `base'. This symbol is always concrete. + * pre: `this.owner' is in the base class sequence of `base'. + */ + final def superSymbol(base: Symbol): Symbol = { + var bcs = base.info.baseClasses.dropWhile(owner !=).tail + var sym: Symbol = NoSymbol + while (!bcs.isEmpty && sym == NoSymbol) { + if (!bcs.head.isImplClass) + sym = matchingSymbol(bcs.head, base.thisType).suchThat(!_.isDeferred) + bcs = bcs.tail + } + sym + } + + /** The getter of this value or setter definition in class `base', or NoSymbol if + * none exists. + */ + final def getter(base: Symbol): Symbol = { + val getterName = if (isSetter) nme.setterToGetter(name) else nme.getterName(name) + base.info.decl(getterName) filter (_.hasAccessorFlag) + } + + /** The setter of this value or getter definition, or NoSymbol if none exists */ + final def setter(base: Symbol): Symbol = setter(base, false) + + final def setter(base: Symbol, hasExpandedName: Boolean): Symbol = { + var sname = nme.getterToSetter(nme.getterName(name)) + if (hasExpandedName) sname = nme.expandedSetterName(sname, base) + base.info.decl(sname) filter (_.hasAccessorFlag) + } + + /** The case module corresponding to this case class + * @pre case class is a member of some other class or package + */ + final def caseModule: Symbol = { + var modname = name.toTermName + if (privateWithin.isClass && !privateWithin.isModuleClass && !hasFlag(EXPANDEDNAME)) + modname = nme.expandedName(modname, privateWithin) + initialize.owner.info.decl(modname).suchThat(_.isModule) + } + + /** If this symbol is a type parameter skolem (not an existential skolem!) + * its corresponding type parameter, otherwise this */ + def deSkolemize: Symbol = this + + /** If this symbol is an existential skolem the location (a Tree or null) + * where it was unpacked. Resulttype is AnyRef because trees are not visible here. */ + def unpackLocation: AnyRef = null + + /** Remove private modifier from symbol `sym's definition. If `sym' is a + * term symbol rename it by expanding its name to avoid name clashes + */ + final def makeNotPrivate(base: Symbol) { + if (this hasFlag PRIVATE) { + setFlag(notPRIVATE) + if (isMethod && !isDeferred) setFlag(lateFINAL) + if (!isStaticModule && !isClassConstructor) { + expandName(base) + if (isModule) moduleClass.makeNotPrivate(base) + } + } + } + + /** change name by appending $$<fully-qualified-name-of-class `base'> + * Do the same for any accessed symbols or setters/getters + */ + def expandName(base: Symbol) { + if (this.isTerm && this != NoSymbol && !hasFlag(EXPANDEDNAME)) { + setFlag(EXPANDEDNAME) + if (hasAccessorFlag && !isDeferred) { + accessed.expandName(base) + } else if (hasGetter) { + getter(owner).expandName(base) + setter(owner).expandName(base) + } + name = nme.expandedName(name, base) + if (isType) name = name + } + } +/* code for fixing nested objects + def expandModuleClassName() { + name = newTypeName(name.toString + "$") + } + + def isExpandedModuleClass: Boolean = name(name.length - 1) == '$' +*/ + def sourceFile: AbstractFile = + if (isModule) moduleClass.sourceFile + else toplevelClass.sourceFile + + def sourceFile_=(f: AbstractFile) { + abort("sourceFile_= inapplicable for " + this) + } + + /** If this is a sealed class, its known direct subclasses. + * Otherwise, the empty set. + */ + def children: Set[Symbol] = Set() + + /** Recursively assemble all children of this symbol. + */ + def sealedDescendants: Set[Symbol] = children.flatMap(_.sealedDescendants) + this + + def orElse[T](alt: => Symbol): Symbol = if (this ne NoSymbol) this else alt + +// ------ toString ------------------------------------------------------------------- + + /** A tag which (in the ideal case) uniquely identifies class symbols */ + final def tag = fullName.## + + /** The simple name of this Symbol */ + final def simpleName: Name = name + + /** The String used to order otherwise identical sealed symbols. + * This uses data which is stable across runs and variable classpaths + * (the initial Name) before falling back on id, which varies depending + * on exactly when a symbol is loaded. + */ + final def sealedSortName = initName + "#" + id + + /** String representation of symbol's definition key word */ + final def keyString: String = + if (isJavaInterface) "interface" + else if (isTrait) "trait" + else if (isClass) "class" + else if (isType && !isParameter) "type" + else if (isVariable) "var" + else if (isPackage) "package" + else if (isModule) "object" + else if (isSourceMethod) "def" + else if (isTerm && (!isParameter || isParamAccessor)) "val" + else "" + + /** Accurate string representation of symbols' kind, suitable for developers. */ + final def accurateKindString: String = + if (isPackage) "package" + else if (isPackageClass) "package class" + else if (isPackageObject) "package object" + else if (isPackageObjectClass) "package object class" + else if (isRefinementClass) "refinement class" + else if (isModule) "module" + else if (isModuleClass) "module class" + else sanitizedKindString + + /** String representation of symbol's kind, suitable for the masses. */ + private def sanitizedKindString: String = + if (isPackage || isPackageClass) "package" + else if (isModule || isModuleClass) "object" + else if (isAnonymousClass) "anonymous class" + else if (isRefinementClass) "" + else if (isTrait) "trait" + else if (isClass) "class" + else if (isType) "type" + else if (isTerm && isLazy) "lazy value" + else if (isVariable) "variable" + else if (isClassConstructor) "constructor" + else if (isSourceMethod) "method" + else if (isTerm) "value" + else "" + + final def kindString: String = + if (settings.debug.value) accurateKindString + else sanitizedKindString + + /** If the name of the symbol's owner should be used when you care about + * seeing an interesting name: in such cases this symbol is e.g. a method + * parameter with a synthetic name, a constructor named "this", an object + * "package", etc. The kind string, if non-empty, will be phrased relative + * to the name of the owner. + */ + def hasMeaninglessName = ( + isSetterParameter // x$1 + || isClassConstructor // this + || isPackageObject // package + || isPackageObjectClass // package$ + || isRefinementClass // <refinement> + ) + + /** String representation of symbol's simple name. + * If !settings.debug translates expansions of operators back to operator symbol. + * E.g. $eq => =. + * If settings.uniqid, adds id. + */ + def nameString = decodedName + idString + + /** If settings.uniqid is set, the symbol's id, else "" */ + final def idString = if (settings.uniqid.value) "#"+id else "" + + /** String representation, including symbol's kind e.g., "class Foo", "method Bar". + * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. + */ + override def toString = compose( + kindString, + if (hasMeaninglessName) owner.nameString else nameString + ) + + /** String representation of location. + */ + def ownsString = { + val owns = owner.skipPackageObject + if (owns.isClass && !owns.printWithoutPrefix && !isScalaPackageClass) "" + owns + else "" + } + + /** String representation of location, plus a preposition. Doesn't do much, + * for backward compatibility reasons. + */ + def locationString = ownsString match { + case "" => "" + case s => " in " + s + } + def fullLocationString = toString + locationString + + /** String representation of symbol's definition following its name */ + final def infoString(tp: Type): String = { + def typeParamsString: String = tp match { + case PolyType(tparams, _) if tparams.nonEmpty => + (tparams map (_.defString)).mkString("[", ",", "]") + case _ => + "" + } + if (isClass) + typeParamsString + " extends " + tp.resultType + else if (isAliasType) + typeParamsString + " = " + tp.resultType + else if (isAbstractType) + typeParamsString + { + tp.resultType match { + case TypeBounds(lo, hi) => + (if (lo.typeSymbol == NothingClass) "" else " >: " + lo) + + (if (hi.typeSymbol == AnyClass) "" else " <: " + hi) + case rtp => + "<: " + rtp + } + } + else if (isModule) + moduleClass.infoString(tp) + else + tp match { + case PolyType(tparams, res) => + typeParamsString + infoString(res) + case NullaryMethodType(res) => + infoString(res) + case MethodType(params, res) => + params.map(_.defString).mkString("(", ",", ")") + infoString(res) + case _ => + ": " + tp + } + } + + def infosString = infos.toString() + + def hasFlagsToString(mask: Long): String = flagsToString( + flags & mask, + if (hasAccessBoundary) privateWithin.toString else "" + ) + + /** String representation of symbol's variance */ + def varianceString: String = + if (variance == 1) "+" + else if (variance == -1) "-" + else "" + + def defaultFlagMask = + if (settings.debug.value) -1L + else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE + else ExplicitFlags + + def defaultFlagString = hasFlagsToString(defaultFlagMask) + + /** String representation of symbol's definition */ + def defString = compose( + defaultFlagString, + keyString, + varianceString + nameString + ( + if (hasRawInfo) infoString(rawInfo) else "<_>" + ) + ) + + /** Concatenate strings separated by spaces */ + private def compose(ss: String*) = ss filter (_ != "") mkString " " + + def isSingletonExistential = + nme.isSingletonName(name) && (info.bounds.hi.typeSymbol isSubClass SingletonClass) + + /** String representation of existentially bound variable */ + def existentialToString = + if (isSingletonExistential && !settings.debug.value) + "val " + nme.dropSingletonName(name) + ": " + dropSingletonType(info.bounds.hi) + else defString + } + + /** A class for term symbols */ + class TermSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + extends Symbol(initOwner, initPos, initName) { + final override def isTerm = true + + override def name: TermName = super.name + privateWithin = NoSymbol + + var referenced: Symbol = NoSymbol + + def cloneSymbolImpl(owner: Symbol): Symbol = + new TermSymbol(owner, pos, name).copyAttrsFrom(this) + + def copyAttrsFrom(original: TermSymbol): this.type = { + referenced = original.referenced + this + } + + private val validAliasFlags = SUPERACCESSOR | PARAMACCESSOR | MIXEDIN | SPECIALIZED + + override def alias: Symbol = + if (hasFlag(validAliasFlags)) initialize.referenced + else NoSymbol + + def setAlias(alias: Symbol): TermSymbol = { + assert(alias != NoSymbol, this) + assert(!alias.isOverloaded, alias) + assert(hasFlag(validAliasFlags), this) + + referenced = alias + this + } + + override def outerSource: Symbol = + if (name endsWith nme.OUTER) initialize.referenced + else NoSymbol + + override def moduleClass: Symbol = + if (hasFlag(MODULE)) referenced else NoSymbol + + def setModuleClass(clazz: Symbol): TermSymbol = { + assert(hasFlag(MODULE)) + referenced = clazz + this + } + + def setLazyAccessor(sym: Symbol): TermSymbol = { + assert(isLazy && (referenced == NoSymbol || referenced == sym), this) + referenced = sym + this + } + + override def lazyAccessor: Symbol = { + assert(isLazy, this) + referenced + } + + protected def doCookJavaRawInfo() { + def cook(sym: Symbol) { + require(sym hasFlag JAVA) + // @M: I think this is more desirable, but Martin prefers to leave raw-types as-is as much as possible + // object rawToExistentialInJava extends TypeMap { + // def apply(tp: Type): Type = tp match { + // // any symbol that occurs in a java sig, not just java symbols + // // see http://lampsvn.epfl.ch/trac/scala/ticket/2454#comment:14 + // case TypeRef(pre, sym, List()) if !sym.typeParams.isEmpty => + // val eparams = typeParamsToExistentials(sym, sym.typeParams) + // existentialAbstraction(eparams, TypeRef(pre, sym, eparams map (_.tpe))) + // case _ => + // mapOver(tp) + // } + // } + val tpe1 = rawToExistential(sym.tpe) + // println("cooking: "+ sym +": "+ sym.tpe +" to "+ tpe1) + if (tpe1 ne sym.tpe) { + sym.setInfo(tpe1) + } + } + + if (isJavaDefined) + cook(this) + else if (hasFlag(OVERLOADED)) + for (sym2 <- alternatives) + if (sym2 hasFlag JAVA) + cook(sym2) + } + } + + /** A class for module symbols */ + class ModuleSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + extends TermSymbol(initOwner, initPos, initName) { + private var flatname: TermName = null + // This method could use a better name from someone clearer on what the condition expresses. + private def isFlatAdjusted = !isMethod && needsFlatClasses + + override def owner: Symbol = + if (isFlatAdjusted) rawowner.owner + else rawowner + + override def name: TermName = + if (isFlatAdjusted) { + if (flatname == null) + flatname = flattenName().toTermName + + flatname + } else rawname + + override def cloneSymbolImpl(owner: Symbol): Symbol = + new ModuleSymbol(owner, pos, name).copyAttrsFrom(this) + } + + /** A class for method symbols */ + class MethodSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + extends TermSymbol(initOwner, initPos, initName) { + private var mtpePeriod = NoPeriod + private var mtpePre: Type = _ + private var mtpeResult: Type = _ + private var mtpeInfo: Type = _ + + override def cloneSymbolImpl(owner: Symbol): Symbol = + new MethodSymbol(owner, pos, name).copyAttrsFrom(this) + + def typeAsMemberOf(pre: Type): Type = { + if (mtpePeriod == currentPeriod) { + if ((mtpePre eq pre) && (mtpeInfo eq info)) return mtpeResult + } else if (isValid(mtpePeriod)) { + mtpePeriod = currentPeriod + if ((mtpePre eq pre) && (mtpeInfo eq info)) return mtpeResult + } + val res = pre.computeMemberType(this) + mtpePeriod = currentPeriod + mtpePre = pre + mtpeInfo = info + mtpeResult = res + res + } + } + + /** A class of type symbols. Alias and abstract types are direct instances + * of this class. Classes are instances of a subclass. + */ + class TypeSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) + extends Symbol(initOwner, initPos, initName) { + privateWithin = NoSymbol + private var tyconCache: Type = null + private var tyconRunId = NoRunId + private var tpeCache: Type = _ + private var tpePeriod = NoPeriod + + override def name: TypeName = super.name.asInstanceOf[TypeName] + final override def isType = true + override def isNonClassType = true + override def isAbstractType = isDeferred + override def isAliasType = !isDeferred + + private def newTypeRef(targs: List[Type]) = { + val pre = if (hasFlag(PARAM | EXISTENTIAL)) NoPrefix else owner.thisType + typeRef(pre, this, targs) + } + + /** Let's say you have a type definition + * + * type T <: Number + * + * and tsym is the symbol corresponding to T. Then + * + * tsym.info = TypeBounds(Nothing, Number) + * tsym.tpe = TypeRef(NoPrefix, T, List()) + */ + override def tpe: Type = { + if (tpeCache eq NoType) throw CyclicReference(this, typeConstructor) + if (tpePeriod != currentPeriod) { + if (isValid(tpePeriod)) { + tpePeriod = currentPeriod + } else { + if (isInitialized) tpePeriod = currentPeriod + tpeCache = NoType + val targs = + if (phase.erasedTypes && this != ArrayClass) List() + else unsafeTypeParams map (_.typeConstructor) //@M! use typeConstructor to generate dummy type arguments, + // sym.tpe should not be called on a symbol that's supposed to be a higher-kinded type + // memberType should be used instead, that's why it uses tpeHK and not tpe + tpeCache = newTypeRef(targs) + } + } + assert(tpeCache ne null/*, "" + this + " " + phase*/)//debug + tpeCache + } + + // needed for experimental code for early types as type parameters + // def refreshType() { tpePeriod = NoPeriod } + + override def typeConstructor: Type = { + if ((tyconCache eq null) || tyconRunId != currentRunId) { + tyconCache = newTypeRef(Nil) + tyconRunId = currentRunId + } + assert(tyconCache ne null) + tyconCache + } + + override def info_=(tp: Type) { + tpePeriod = NoPeriod + tyconCache = null + super.info_=(tp) + } + + override def reset(completer: Type) { + super.reset(completer) + tpePeriod = NoPeriod + tyconRunId = NoRunId + } + + /*** example: + * public class Test3<T> {} + * public class Test1<T extends Test3> {} + * info for T in Test1 should be >: Nothing <: Test3[_] + */ + protected def doCookJavaRawInfo() { + // don't require isJavaDefined, since T in the above example does not have that flag + val tpe1 = rawToExistential(info) + // println("cooking type: "+ this +": "+ info +" to "+ tpe1) + if (tpe1 ne info) { + setInfo(tpe1) + } + } + + def cloneSymbolImpl(owner: Symbol): Symbol = + new TypeSymbol(owner, pos, name) //.toTypeName) + + incCounter(typeSymbolCount) + } + + /** A class for type parameters viewed from inside their scopes + * + * @param origin Can be either a tree, or a symbol, or null. + * If skolem got created from newTypeSkolem (called in Namers), origin denotes + * the type parameter from which the skolem was created. If it got created from + * skolemizeExistential, origin is either null or a Tree. If it is a Tree, it indicates + * where the skolem was introduced (this is important for knowing when to pack it + * again into ab Existential). origin is `null' only in skolemizeExistentials called + * from <:< or isAsSpecific, because here its value does not matter. + * I elieve the following invariant holds: + * + * origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) + */ + class TypeSkolem(initOwner: Symbol, initPos: Position, initName: TypeName, origin: AnyRef) + extends TypeSymbol(initOwner, initPos, initName) { + + /** The skolemization level in place when the skolem was constructed */ + val level = skolemizationLevel + + final override def isSkolem = true + + /** If typeskolem comes from a type parameter, that parameter, otherwise skolem itself */ + override def deSkolemize = origin match { + case s: Symbol => s + case _ => this + } + + /** If type skolem comes from an existential, the tree where it was created */ + override def unpackLocation = origin + + override def typeParams = info.typeParams //@M! (not deSkolemize.typeParams!!), also can't leave superclass definition: use info, not rawInfo + + override def cloneSymbolImpl(owner: Symbol): Symbol = + new TypeSkolem(owner, pos, name, origin) + + override def nameString: String = + if (settings.debug.value) (super.nameString + "&" + level) + else super.nameString + } + + + /** A class for class symbols */ + class ClassSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) + extends TypeSymbol(initOwner, initPos, initName) { + + private var source: AbstractFile = null + private var thissym: Symbol = this + + final override def isClass = true + final override def isNonClassType = false + final override def isAbstractType = false + final override def isAliasType = false + + override def sourceFile = + if (owner.isPackageClass) source + else super.sourceFile + override def sourceFile_=(f: AbstractFile) { source = f } + + override def reset(completer: Type) { + super.reset(completer) + thissym = this + } + + private var flatname: TypeName = null + + override def owner: Symbol = + if (needsFlatClasses) rawowner.owner + else rawowner + + override def name: TypeName = + if (needsFlatClasses) { + if (flatname == null) + flatname = flattenName().toTypeName + flatname + } + else rawname.asInstanceOf[TypeName] + + private var thisTypeCache: Type = _ + private var thisTypePeriod = NoPeriod + + private var typeOfThisCache: Type = _ + private var typeOfThisPeriod = NoPeriod + + /** the type this.type in this class */ + override def thisType: Type = { + val period = thisTypePeriod + if (period != currentPeriod) { + thisTypePeriod = currentPeriod + if (!isValid(period)) thisTypeCache = ThisType(this) + } + thisTypeCache + } + + /** A symbol carrying the self type of the class as its type */ + override def thisSym: Symbol = thissym + + /** the self type of an object foo is foo.type, not class<foo>.this.type + */ + override def typeOfThis: Type = { + if (getFlag(MODULE | IMPLCLASS) == MODULE.toLong && owner != NoSymbol) { + val period = typeOfThisPeriod + if (period != currentPeriod) { + typeOfThisPeriod = currentPeriod + if (!isValid(period)) + typeOfThisCache = singleType(owner.thisType, sourceModule) + } + typeOfThisCache + } + else thissym.tpe + } + + /** Sets the self type of the class */ + override def typeOfThis_=(tp: Type) { + thissym = newThisSym(pos).setInfo(tp) + } + + override def cloneSymbolImpl(owner: Symbol): Symbol = { + val clone = new ClassSymbol(owner, pos, name) + if (thisSym != this) { + clone.typeOfThis = typeOfThis + clone.thisSym.name = thisSym.name + } + clone + } + + override def sourceModule = + if (isModuleClass) companionModule else NoSymbol + + private var childSet: Set[Symbol] = Set() + override def children = childSet + override def addChild(sym: Symbol) { childSet = childSet + sym } + + incCounter(classSymbolCount) + } + + /** A class for module class symbols + * Note: Not all module classes are of this type; when unpickled, we get + * plain class symbols! + */ + class ModuleClassSymbol(owner: Symbol, pos: Position, name: TypeName) + extends ClassSymbol(owner, pos, name) { + private var module: Symbol = null + def this(module: TermSymbol) = { + this(module.owner, module.pos, module.name.toTypeName) + setFlag(module.getFlag(ModuleToClassFlags) | MODULE | FINAL) + sourceModule = module + } + override def sourceModule = module + private var implicitMembersCacheValue: List[Symbol] = List() + private var implicitMembersCacheKey: Type = NoType + def implicitMembers: List[Symbol] = { + val tp = info + if (implicitMembersCacheKey ne tp) { + implicitMembersCacheKey = tp + implicitMembersCacheValue = tp.implicitMembers + } + implicitMembersCacheValue + } + override def sourceModule_=(module: Symbol) { this.module = module } + } + + /** An object representing a missing symbol */ + object NoSymbol extends Symbol(null, NoPosition, nme.NO_NAME) { + setInfo(NoType) + privateWithin = this + override def info_=(info: Type) { + infos = TypeHistory(1, NoType, null) + unlock() + validTo = currentPeriod + } + override def defString: String = toString + override def locationString: String = "" + override def enclClass: Symbol = this + override def toplevelClass: Symbol = this + override def enclMethod: Symbol = this + override def owner: Symbol = abort("no-symbol does not have owner") + override def sourceFile: AbstractFile = null + override def ownerChain: List[Symbol] = List() + override def ownersIterator: Iterator[Symbol] = Iterator.empty + override def alternatives: List[Symbol] = List() + override def reset(completer: Type) {} + override def info: Type = NoType + override def rawInfo: Type = NoType + protected def doCookJavaRawInfo() {} + override def accessBoundary(base: Symbol): Symbol = RootClass + def cloneSymbolImpl(owner: Symbol): Symbol = abort() + override def originalEnclosingMethod = this + } + + def cloneSymbols[T <: Symbol](syms: List[T]): List[T] = { + val syms1 = syms map (_.cloneSymbol.asInstanceOf[T]) + for (sym1 <- syms1) sym1.setInfo(sym1.info.substSym(syms, syms1)) + syms1 + } + + def cloneSymbols[T <: Symbol](syms: List[T], owner: Symbol): List[T] = { + val syms1 = syms map (_.cloneSymbol(owner).asInstanceOf[T]) + for (sym1 <- syms1) sym1.setInfo(sym1.info.substSym(syms, syms1)) + syms1 + } + + /** An exception for cyclic references of symbol definitions */ + case class CyclicReference(sym: Symbol, info: Type) + extends TypeError("illegal cyclic reference involving " + sym) { + // printStackTrace() // debug + } + + case class InvalidCompanions(sym1: Symbol, sym2: Symbol) + extends Throwable("Companions '" + sym1 + "' and '" + sym2 + "' must be defined in same file") { + override def toString = getMessage + } + + /** A class for type histories */ + private sealed case class TypeHistory(var validFrom: Period, info: Type, prev: TypeHistory) { + assert((prev eq null) || phaseId(validFrom) > phaseId(prev.validFrom), this) + assert(validFrom != NoPeriod) + override def toString() = + "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" + } +} diff --git a/src/compiler/scala/reflect/common/TreeGen.scala b/src/compiler/scala/reflect/common/TreeGen.scala new file mode 100644 index 0000000000..5d43e0f26e --- /dev/null +++ b/src/compiler/scala/reflect/common/TreeGen.scala @@ -0,0 +1,234 @@ +package scala.reflect +package common + +abstract class TreeGen { + val global: SymbolTable + + import global._ + import definitions._ + + def rootId(name: Name) = Select(Ident(nme.ROOTPKG), name) + def rootScalaDot(name: Name) = Select(rootId(nme.scala_) setSymbol ScalaPackage, name) + def scalaDot(name: Name) = Select(Ident(nme.scala_) setSymbol ScalaPackage, name) + def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) + def scalaUnitConstr = scalaDot(tpnme.Unit) + def scalaScalaObjectConstr = scalaDot(tpnme.ScalaObject) + def productConstr = scalaDot(tpnme.Product) + def serializableConstr = scalaDot(tpnme.Serializable) + + def scalaFunctionConstr(argtpes: List[Tree], restpe: Tree, abstractFun: Boolean = false): Tree = { + val cls = if (abstractFun) + mkAttributedRef(AbstractFunctionClass(argtpes.length)) + else + mkAttributedRef(FunctionClass(argtpes.length)) + AppliedTypeTree(cls, argtpes :+ restpe) + } + + /** Builds a reference to value whose type is given stable prefix. + * The type must be suitable for this. For example, it + * must not be a TypeRef pointing to an abstract type variable. + */ + def mkAttributedQualifier(tpe: Type): Tree = + mkAttributedQualifier(tpe, NoSymbol) + + /** Builds a reference to value whose type is given stable prefix. + * If the type is unsuitable, e.g. it is a TypeRef for an + * abstract type variable, then an Ident will be made using + * termSym as the Ident's symbol. In that case, termSym must + * not be NoSymbol. + */ + def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = tpe match { + case NoPrefix => + EmptyTree + case ThisType(clazz) => + if (clazz.isEffectiveRoot) EmptyTree + else mkAttributedThis(clazz) + case SingleType(pre, sym) => + applyIfNoArgs(mkAttributedStableRef(pre, sym)) + case TypeRef(pre, sym, args) => + if (sym.isRoot) { + mkAttributedThis(sym) + } else if (sym.isModuleClass) { + applyIfNoArgs(mkAttributedRef(pre, sym.sourceModule)) + } else if (sym.isModule || sym.isClass) { + assert(phase.erasedTypes, tpe) + mkAttributedThis(sym) + } else if (sym.isType) { + assert(termSym != NoSymbol, tpe) + mkAttributedIdent(termSym) setType tpe + } else { + mkAttributedRef(pre, sym) + } + + case ConstantType(value) => + Literal(value) setType tpe + + case AnnotatedType(_, atp, _) => + mkAttributedQualifier(atp) + + case RefinedType(parents, _) => + // I am unclear whether this is reachable, but + // the following implementation looks logical -Lex + val firstStable = parents.find(_.isStable) + assert(!firstStable.isEmpty, tpe) + mkAttributedQualifier(firstStable.get) + + case _ => + abort("bad qualifier: " + tpe) + } + /** If this is a reference to a method with an empty + * parameter list, wrap it in an apply. + */ + private def applyIfNoArgs(qual: Tree) = qual.tpe match { + case MethodType(Nil, restpe) => Apply(qual, Nil) setType restpe + case _ => qual + } + + /** Builds a reference to given symbol with given stable prefix. */ + def mkAttributedRef(pre: Type, sym: Symbol): Tree = { + val qual = mkAttributedQualifier(pre) + qual match { + case EmptyTree => mkAttributedIdent(sym) + case This(clazz) if qual.symbol.isEffectiveRoot => mkAttributedIdent(sym) + case _ => mkAttributedSelect(qual, sym) + } + } + + /** Builds a reference to given symbol. */ + def mkAttributedRef(sym: Symbol): Tree = + if (sym.owner.isClass) mkAttributedRef(sym.owner.thisType, sym) + else mkAttributedIdent(sym) + + /** Builds an untyped reference to given symbol. */ + def mkUnattributedRef(sym: Symbol): Tree = + if (sym.owner.isClass) Select(This(sym.owner), sym) + else Ident(sym) + + /** Replaces tree type with a stable type if possible */ + def stabilize(tree: Tree): Tree = { + for(tp <- stableTypeFor(tree)) tree.tpe = tp + tree + } + + /** Computes stable type for a tree if possible */ + def stableTypeFor(tree: Tree): Option[Type] = tree match { + case Ident(_) if tree.symbol.isStable => + Some(singleType(tree.symbol.owner.thisType, tree.symbol)) + case Select(qual, _) if ((tree.symbol ne null) && (qual.tpe ne null)) && // turned assert into guard for #4064 + tree.symbol.isStable && qual.tpe.isStable => + Some(singleType(qual.tpe, tree.symbol)) + case _ => + None + } + + /** Cast `tree' to type `pt' */ + def mkCast(tree: Tree, pt: Type): Tree = { + if (settings.debug.value) log("casting " + tree + ":" + tree.tpe + " to " + pt) + assert(!tree.tpe.isInstanceOf[MethodType], tree) + assert(!pt.typeSymbol.isPackageClass) + assert(!pt.typeSymbol.isPackageObjectClass) + assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) //@MAT only called during erasure, which already takes care of that + atPos(tree.pos)(mkAsInstanceOf(tree, pt, false)) + } + + /** Builds a reference with stable type to given symbol */ + def mkAttributedStableRef(pre: Type, sym: Symbol): Tree = + stabilize(mkAttributedRef(pre, sym)) + + def mkAttributedStableRef(sym: Symbol): Tree = + stabilize(mkAttributedRef(sym)) + + def mkAttributedThis(sym: Symbol): Tree = + This(sym.name.toTypeName) setSymbol sym setType sym.thisType + + def mkAttributedIdent(sym: Symbol): Tree = + Ident(sym.name) setSymbol sym setType sym.tpe + + def mkAttributedSelect(qual: Tree, sym: Symbol): Tree = { + // Tests involving the repl fail without the .isEmptyPackage condition. + if (qual.symbol != null && (qual.symbol.isEffectiveRoot || qual.symbol.isEmptyPackage)) + mkAttributedIdent(sym) + else { + val pkgQualifier = + if (sym != null && sym.owner.isPackageObjectClass && sym.owner.owner == qual.tpe.typeSymbol) { + val obj = sym.owner.sourceModule + Select(qual, nme.PACKAGEkw) setSymbol obj setType singleType(qual.tpe, obj) + } + else qual + + val tree = Select(pkgQualifier, sym) + if (pkgQualifier.tpe == null) tree + else tree setType (qual.tpe memberType sym) + } + } + + private def mkTypeApply(value: Tree, tpe: Type, what: Symbol) = + Apply( + TypeApply( + mkAttributedSelect(value, what), + List(TypeTree(tpe.normalize)) + ), + Nil + ) + /** Builds an instance test with given value and type. */ + def mkIsInstanceOf(value: Tree, tpe: Type, any: Boolean = true): Tree = + mkTypeApply(value, tpe, (if (any) Any_isInstanceOf else Object_isInstanceOf)) + + /** Builds a cast with given value and type. */ + def mkAsInstanceOf(value: Tree, tpe: Type, any: Boolean = true): Tree = + mkTypeApply(value, tpe, (if (any) Any_asInstanceOf else Object_asInstanceOf)) + + /** Cast `tree' to 'pt', unless tpe is a subtype of pt, or pt is Unit. */ + def maybeMkAsInstanceOf(tree: Tree, pt: Type, tpe: Type, beforeRefChecks: Boolean = false): Tree = + if ((pt == UnitClass.tpe) || (tpe <:< pt)) { + log("no need to cast from " + tpe + " to " + pt) + tree + } else + atPos(tree.pos) { + if (beforeRefChecks) + TypeApply(mkAttributedSelect(tree, Any_asInstanceOf), List(TypeTree(pt))) + else + mkAsInstanceOf(tree, pt) + } + + def mkClassOf(tp: Type): Tree = + Literal(Constant(tp)) setType ConstantType(Constant(tp))// ClassType(tp) + + /** Builds a list with given head and tail. */ + def mkNewCons(head: Tree, tail: Tree): Tree = + New(Apply(mkAttributedRef(ConsClass), List(head, tail))) + + /** Builds a list with given head and tail. */ + def mkNil: Tree = mkAttributedRef(NilModule) + + /** Builds a tree representing an undefined local, as in + * var x: T = _ + * which is appropriate to the given Type. + */ + def mkZero(tp: Type): Tree = { + val sym = tp.typeSymbol + val tree = + if (sym == UnitClass) Literal(()) + else if (sym == BooleanClass) Literal(false) + else if (isValueClass(sym)) Literal(0) + else if (NullClass.tpe <:< tp) Literal(null: Any) + else abort("Cannot determine zero for " + tp) + + tree setType tp + } + + /** Builds a tuple */ + def mkTuple(elems: List[Tree]): Tree = + if (elems.isEmpty) Literal(()) + else Apply( + Select(mkAttributedRef(TupleClass(elems.length).caseModule), nme.apply), + elems) + + // tree1 AND tree2 + def mkAnd(tree1: Tree, tree2: Tree): Tree = + Apply(Select(tree1, Boolean_and), List(tree2)) + + // tree1 OR tree2 + def mkOr(tree1: Tree, tree2: Tree): Tree = + Apply(Select(tree1, Boolean_or), List(tree2)) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/common/TreeInfo.scala b/src/compiler/scala/reflect/common/TreeInfo.scala new file mode 100644 index 0000000000..95189d1813 --- /dev/null +++ b/src/compiler/scala/reflect/common/TreeInfo.scala @@ -0,0 +1,385 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import Flags._ +import util.HashSet + +/** This class ... + * + * @author Martin Odersky + * @version 1.0 + */ +abstract class TreeInfo { + val trees: SymbolTable + import trees._ + import definitions.ThrowableClass + + def isOwnerDefinition(tree: Tree): Boolean = tree match { + case PackageDef(_, _) + | ClassDef(_, _, _, _) + | ModuleDef(_, _, _) + | DefDef(_, _, _, _, _, _) + | Import(_, _) => true + case _ => false + } + + def isDefinition(tree: Tree): Boolean = tree.isDef + + def isDeclaration(tree: Tree): Boolean = tree match { + case DefDef(_, _, _, _, _, EmptyTree) + | ValDef(_, _, _, EmptyTree) + | TypeDef(_, _, _, _) => true + case _ => false + } + + /** Is tree legal as a member definition of an interface? + */ + def isInterfaceMember(tree: Tree): Boolean = tree match { + case EmptyTree => true + case Import(_, _) => true + case TypeDef(_, _, _, _) => true + case DefDef(mods, _, _, _, _, __) => mods.isDeferred + case ValDef(mods, _, _, _) => mods.isDeferred + case DocDef(_, definition) => isInterfaceMember(definition) + case _ => false + } + + /** Is tree a pure (i.e. non-side-effecting) definition? + */ + def isPureDef(tree: Tree): Boolean = tree match { + case EmptyTree + | ClassDef(_, _, _, _) + | TypeDef(_, _, _, _) + | Import(_, _) + | DefDef(_, _, _, _, _, _) => + true + case ValDef(mods, _, _, rhs) => + !mods.isMutable && isPureExpr(rhs) + case DocDef(_, definition) => + isPureDef(definition) + case _ => + false + } + + /** Is tree a stable and pure expression? + */ + def isPureExpr(tree: Tree): Boolean = tree match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol.isStable + case Select(qual, _) => + tree.symbol.isStable && isPureExpr(qual) + case TypeApply(fn, _) => + isPureExpr(fn) + case Apply(fn, List()) => + /* Note: After uncurry, field accesses are represented as Apply(getter, Nil), + * so an Apply can also be pure. + * However, before typing, applications of nullary functional values are also + * Apply(function, Nil) trees. To prevent them from being treated as pure, + * we check that the callee is a method. */ + fn.symbol.isMethod && !fn.symbol.isLazy && isPureExpr(fn) + case Typed(expr, _) => + isPureExpr(expr) + case Block(stats, expr) => + (stats forall isPureDef) && isPureExpr(expr) + case _ => + false + } + + def mayBeVarGetter(sym: Symbol) = sym.info match { + case NullaryMethodType(_) => sym.owner.isClass && !sym.isStable + case mt @ MethodType(_, _) => mt.isImplicit && sym.owner.isClass && !sym.isStable + case _ => false + } + + def isVariableOrGetter(tree: Tree) = { + def sym = tree.symbol + def isVar = sym.isVariable + def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name)) != NoSymbol + + tree match { + case Ident(_) => isVar + case Select(_, _) => isVar || isGetter + case _ => + methPart(tree) match { + case Select(qual, nme.apply) => qual.tpe.member(nme.update) != NoSymbol + case _ => false + } + } + } + + /** Is tree a self constructor call? + */ + def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match { + case Ident(nme.CONSTRUCTOR) + | Select(This(_), nme.CONSTRUCTOR) => true + case _ => false + } + + def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { + case Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false + } + + def isSelfOrSuperConstrCall(tree: Tree) = + isSelfConstrCall(tree) || isSuperConstrCall(tree) + + /** Is tree a variable pattern */ + def isVarPattern(pat: Tree): Boolean = pat match { + case _: BackQuotedIdent => false + case x: Ident => isVariableName(x.name) + case _ => false + } + + /** The first constructor definitions in `stats' */ + def firstConstructor(stats: List[Tree]): Tree = stats find { + case x: DefDef => nme.isConstructorName(x.name) + case _ => false + } getOrElse EmptyTree + + /** The arguments to the first constructor in `stats'. */ + def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match { + case DefDef(_, _, _, args :: _, _, _) => args + case _ => Nil + } + + /** The value definitions marked PRESUPER in this statement sequence */ + def preSuperFields(stats: List[Tree]): List[ValDef] = + stats collect { case vd: ValDef if isEarlyValDef(vd) => vd } + + def isEarlyDef(tree: Tree) = tree match { + case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER + case ValDef(mods, _, _, _) => mods hasFlag PRESUPER + case _ => false + } + + def isEarlyValDef(tree: Tree) = tree match { + case ValDef(mods, _, _, _) => mods hasFlag PRESUPER + case _ => false + } + + def isEarlyTypeDef(tree: Tree) = tree match { + case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER + case _ => false + } + + /** Is tpt of the form T* ? */ + def isRepeatedParamType(tpt: Tree) = tpt match { + case TypeTree() => definitions.isRepeatedParamType(tpt.tpe) + case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS_NAME), _) => true + case AppliedTypeTree(Select(_, tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), _) => true + case _ => false + } + /** The parameter ValDefs from a def of the form T*. */ + def repeatedParams(tree: Tree): List[ValDef] = tree match { + case DefDef(_, _, _, vparamss, _, _) => vparamss.flatten filter (vd => isRepeatedParamType(vd.tpt)) + case _ => Nil + } + + /** Is tpt a by-name parameter type? */ + def isByNameParamType(tpt: Tree) = tpt match { + case TypeTree() => definitions.isByNameParamType(tpt.tpe) + case AppliedTypeTree(Select(_, tpnme.BYNAME_PARAM_CLASS_NAME), _) => true + case _ => false + } + + /** Is name a left-associative operator? */ + def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.endChar != ':') + + private val reserved = Set[Name](nme.false_, nme.true_, nme.null_) + + /** Is name a variable name? */ + def isVariableName(name: Name): Boolean = { + val first = name(0) + ((first.isLower && first.isLetter) || first == '_') && !reserved(name) + } + + /** Is tree a this node which belongs to `enclClass'? */ + def isSelf(tree: Tree, enclClass: Symbol): Boolean = tree match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** can this type be a type pattern */ + def mayBeTypePat(tree: Tree): Boolean = tree match { + case CompoundTypeTree(Template(tps, _, Nil)) => tps exists mayBeTypePat + case Annotated(_, tp) => mayBeTypePat(tp) + case AppliedTypeTree(constr, args) => mayBeTypePat(constr) || args.exists(_.isInstanceOf[Bind]) + case SelectFromTypeTree(tp, _) => mayBeTypePat(tp) + case _ => false + } + + /** Is this argument node of the form <expr> : _* ? + */ + def isWildcardStarArg(tree: Tree): Boolean = tree match { + case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true + case _ => false + } + def isWildcardStarArgList(trees: List[Tree]) = + trees.nonEmpty && isWildcardStarArg(trees.last) + + /** Is the argument a (possibly bound) _ arg? + */ + def isWildcardArg(tree: Tree): Boolean = unbind(tree) match { + case Ident(nme.WILDCARD) => true + case _ => false + } + + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ + def isDefaultCase(cdef: CaseDef) = cdef match { + case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) + case _ => false + } + + /** Does this CaseDef catch Throwable? */ + def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe) + + /** Does this CaseDef catch everything of a certain Type? */ + def catchesAllOf(cdef: CaseDef, threshold: Type) = + isDefaultCase(cdef) || (cdef.guard.isEmpty && (unbind(cdef.pat) match { + case Typed(Ident(nme.WILDCARD), tpt) => (tpt.tpe != null) && (threshold <:< tpt.tpe) + case _ => false + })) + + /** Is this pattern node a catch-all or type-test pattern? */ + def isCatchCase(cdef: CaseDef) = cdef match { + case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => + isSimpleThrowable(tpt.tpe) + case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => + isSimpleThrowable(tpt.tpe) + case _ => + isDefaultCase(cdef) + } + + private def isSimpleThrowable(tp: Type): Boolean = tp match { + case TypeRef(pre, sym, args) => + (pre == NoPrefix || pre.widen.typeSymbol.isStatic) && + (sym isNonBottomSubClass ThrowableClass) && /* bq */ !sym.isTrait + case _ => + false + } + + /* If we have run-time types, and these are used for pattern matching, + we should replace this by something like: + + tp match { + case TypeRef(pre, sym, args) => + args.isEmpty && (sym.owner.isPackageClass || isSimple(pre)) + case NoPrefix => + true + case _ => + false + } +*/ + + /** Is this pattern node a sequence-valued pattern? */ + def isSequenceValued(tree: Tree): Boolean = unbind(tree) match { + case Alternative(ts) => ts exists isSequenceValued + case ArrayValue(_, _) | Star(_) => true + case _ => false + } + + /** The underlying pattern ignoring any bindings */ + def unbind(x: Tree): Tree = x match { + case Bind(_, y) => unbind(y) + case y => y + } + + /** Is this tree a Star(_) after removing bindings? */ + def isStar(x: Tree) = unbind(x) match { + case Star(_) => true + case _ => false + } + + /** The method part of an application node + */ + def methPart(tree: Tree): Tree = tree match { + case Apply(fn, _) => methPart(fn) + case TypeApply(fn, _) => methPart(fn) + case AppliedTypeTree(fn, _) => methPart(fn) + case _ => tree + } + + def firstArgument(tree: Tree): Tree = tree match { + case Apply(fn, args) => + val f = firstArgument(fn) + if (f == EmptyTree && !args.isEmpty) args.head else f + case _ => + EmptyTree + } + + /** Is the tree Predef, scala.Predef, or _root_.scala.Predef? + */ + def isPredefExpr(t: Tree) = t match { + case Ident(nme.Predef) => true + case Select(Ident(nme.scala_), nme.Predef) => true + case Select(Select(Ident(nme.ROOTPKG), nme.scala_), nme.Predef) => true + case _ => false + } + + /** Is this file the body of a compilation unit which should not + * have Predef imported? + */ + def noPredefImportForUnit(body: Tree) = { + // Top-level definition whose leading imports include Predef. + def containsLeadingPredefImport(defs: List[Tree]): Boolean = defs match { + case PackageDef(_, defs1) :: _ => containsLeadingPredefImport(defs1) + case Import(expr, _) :: rest => isPredefExpr(expr) || containsLeadingPredefImport(rest) + case _ => false + } + def isImplDef(trees: List[Tree], name: Name): Boolean = trees match { + case Import(_, _) :: xs => isImplDef(xs, name) + case DocDef(_, tree1) :: Nil => isImplDef(List(tree1), name) + case Annotated(_, tree1) :: Nil => isImplDef(List(tree1), name) + case ModuleDef(_, `name`, _) :: Nil => true + case ClassDef(_, `name`, _, _) :: Nil => true + case _ => false + } + // Compilation unit is class or object 'name' in package 'scala' + def isUnitInScala(tree: Tree, name: Name) = tree match { + case PackageDef(Ident(nme.scala_), defs) => isImplDef(defs, name) + case _ => false + } + + ( isUnitInScala(body, nme.Predef) + || isUnitInScala(body, tpnme.ScalaObject) + || containsLeadingPredefImport(List(body))) + } + + def isAbsTypeDef(tree: Tree) = tree match { + case TypeDef(_, _, _, TypeBoundsTree(_, _)) => true + case TypeDef(_, _, _, rhs) => rhs.tpe.isInstanceOf[TypeBounds] + case _ => false + } + + def isAliasTypeDef(tree: Tree) = tree match { + case TypeDef(_, _, _, _) => !isAbsTypeDef(tree) + case _ => false + } + + /** Some handy extractors for spotting true and false expressions + * through the haze of braces. + */ + abstract class SeeThroughBlocks[T] { + protected def unapplyImpl(x: Tree): T + def unapply(x: Tree): T = x match { + case Block(Nil, expr) => unapply(expr) + case _ => unapplyImpl(x) + } + } + object IsTrue extends SeeThroughBlocks[Boolean] { + protected def unapplyImpl(x: Tree): Boolean = x equalsStructure Literal(Constant(true)) + } + object IsFalse extends SeeThroughBlocks[Boolean] { + protected def unapplyImpl(x: Tree): Boolean = x equalsStructure Literal(Constant(false)) + } +} diff --git a/src/compiler/scala/reflect/common/TreePrinters.scala b/src/compiler/scala/reflect/common/TreePrinters.scala new file mode 100644 index 0000000000..711232d774 --- /dev/null +++ b/src/compiler/scala/reflect/common/TreePrinters.scala @@ -0,0 +1,461 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import Flags._ + +trait TreePrinters { self: SymbolTable => + + //nsc import treeInfo.{ IsTrue, IsFalse } + + final val showOuterTests = false + + /** Adds backticks if the name is a scala keyword. */ + def quotedName(name: Name, decode: Boolean): String = { + val s = if (decode) name.decode else name.toString + val term = name.toTermName + if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s + else s + } + def quotedName(name: Name): String = quotedName(name, false) + + /** Turns a path into a String, introducing backquotes + * as necessary. + */ + def backquotedPath(t: Tree): String = t match { + case Select(qual, name) => "%s.%s".format(backquotedPath(qual), quotedName(name)) + case Ident(name) => quotedName(name) + case _ => t.toString + } + + class TreePrinter(out: PrintWriter) { + protected var indentMargin = 0 + protected val indentStep = 2 + protected var indentString = " " // 40 + + def flush() = out.flush() + + def indent() = indentMargin += indentStep + def undent() = indentMargin -= indentStep + + protected def doPrintPositions = settings.Xprintpos.value + def printPosition(tree: Tree) = if (doPrintPositions) print(showPos(tree.pos)) + + def println() { + out.println() + while (indentMargin > indentString.length()) + indentString += indentString + if (indentMargin > 0) + out.write(indentString, 0, indentMargin) + } + + def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { + ls match { + case List() => + case List(x) => printelem(x) + case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) + } + } + + def printColumn(ts: List[Tree], start: String, sep: String, end: String) { + print(start); indent; println() + printSeq(ts){print}{print(sep); println()}; undent; println(); print(end) + } + + def printRow(ts: List[Tree], start: String, sep: String, end: String) { + print(start); printSeq(ts){print}{print(sep)}; print(end) + } + + def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } + + def printTypeParams(ts: List[TypeDef]) { + if (!ts.isEmpty) { + print("["); printSeq(ts){ t => + printAnnotations(t) + printParam(t) + }{print(", ")}; print("]") + } + } + + def printValueParams(ts: List[ValDef]) { + print("(") + if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") + printSeq(ts){printParam}{print(", ")} + print(")") + } + + def printParam(tree: Tree) { + tree match { + case ValDef(mods, name, tp, rhs) => + printPosition(tree) + printAnnotations(tree) + print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) + case TypeDef(mods, name, tparams, rhs) => + printPosition(tree) + print(symName(tree, name)) + printTypeParams(tparams); print(rhs) + } + } + + def printBlock(tree: Tree) { + tree match { + case Block(_, _) => + print(tree) + case _ => + printColumn(List(tree), "{", ";", "}") + } + } + + private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { + case null | NoSymbol => orElse + case sym => f(sym) + } + private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) + + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + def nameFn(sym: Symbol) = { + val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" + prefix + tree.symbol.nameString + } + symFn(tree, nameFn, quotedName(name, decoded)) + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + + def printOpt(prefix: String, tree: Tree) { + if (!tree.isEmpty) { print(prefix); print(tree) } + } + + def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( + if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( + if (tree.symbol == NoSymbol) mods.privateWithin + else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name + else "" + ) + ) + + def printFlags(flags: Long, privateWithin: String) { + var mask: Long = if (settings.debug.value) -1L else PrintableFlags + val s = flagsToString(flags & mask, privateWithin) + if (s != "") print(s + " ") + } + + def printAnnotations(tree: Tree) { + val annots = + if (tree.symbol.hasAssignedAnnotations) tree.symbol.annotations + else tree.asInstanceOf[MemberDef].mods.annotations + + annots foreach (annot => print("@"+annot+" ")) + } + + def print(str: String) { out.print(str) } + def print(name: Name) { print(quotedName(name)) } + + private var currentOwner: Symbol = NoSymbol + private var selectorType: Type = NoType + + def printRaw(tree: Tree) { + tree match { + case EmptyTree => + print("<empty>") + + case ClassDef(mods, name, tparams, impl) => + printAnnotations(tree) + printModifiers(tree, mods) + val word = + if (mods.hasTraitFlag) "trait" + else if (ifSym(tree, _.isModuleClass)) "object" + else "class" + + print(word + " " + symName(tree, name)) + printTypeParams(tparams) + print(if (mods.isDeferred) " <: " else " extends "); print(impl) + + case PackageDef(packaged, stats) => + printAnnotations(tree) + print("package "); print(packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printAnnotations(tree) + printModifiers(tree, mods); print("object " + symName(tree, name)) + print(" extends "); print(impl) + + case ValDef(mods, name, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print(if (mods.isMutable) "var " else "val ") + print(symName(tree, name)) + printOpt(": ", tp) + if (!mods.isDeferred) { + print(" = ") + if (rhs.isEmpty) print("_") else print(rhs) + } + + case DefDef(mods, name, tparams, vparamss, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print("def " + symName(tree, name)) + printTypeParams(tparams); vparamss foreach printValueParams + printOpt(": ", tp); printOpt(" = ", rhs) + + case TypeDef(mods, name, tparams, rhs) => + if (mods hasFlag (PARAM | DEFERRED)) { + printAnnotations(tree) + printModifiers(tree, mods); print("type "); printParam(tree) + } else { + printAnnotations(tree) + printModifiers(tree, mods); print("type " + symName(tree, name)) + printTypeParams(tparams); printOpt(" = ", rhs) + } + + case LabelDef(name, params, rhs) => + print(symName(tree, name)); printRow(params, "(", ",", ")"); printBlock(rhs) + + case Import(expr, selectors) => + // Is this selector remapping a name (i.e, {name1 => name2}) + def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) + def selectorToString(s: ImportSelector): String = { + val from = quotedName(s.name) + if (isNotRemap(s)) from + else from + "=>" + quotedName(s.rename) + } + print("import "); print(backquotedPath(expr)) + print(".") + selectors match { + case List(s) => + // If there is just one selector and it is not remapping a name, no braces are needed + if (isNotRemap(s)) { + print(selectorToString(s)) + } else { + print("{"); print(selectorToString(s)); print("}") + } + // If there is more than one selector braces are always needed + case many => + print(many.map(selectorToString).mkString("{", ", ", "}")) + } + + case DocDef(comment, definition) => + print(comment.raw); println(); print(definition) + + case Template(parents, self, body) => + val currentOwner1 = currentOwner + if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner + printRow(parents, " with ") + if (!body.isEmpty) { + if (self.name != nme.WILDCARD) { + print(" { "); print(self.name); printOpt(": ", self.tpt); print(" => ") + } else if (!self.tpt.isEmpty) { + print(" { _ : "); print(self.tpt); print(" => ") + } else { + print(" {") + } + printColumn(body, "", ";", "}") + } + currentOwner = currentOwner1 + + case Block(stats, expr) => + printColumn(stats ::: List(expr), "{", ";", "}") + + case Match(selector, cases) => + val selectorType1 = selectorType + selectorType = selector.tpe + print(selector); printColumn(cases, " match {", "", "}") + selectorType = selectorType1 + + case CaseDef(pat, guard, body) => + print("case ") + def patConstr(pat: Tree): Tree = pat match { + case Apply(fn, args) => patConstr(fn) + case _ => pat + } + if (showOuterTests && + needsOuterTest( + patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) + print("???") + print(pat); printOpt(" if ", guard) + print(" => "); print(body) + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("("); print(elem); print(")*") + + case Bind(name, t) => + print("("); print(symName(tree, name)); print(" @ "); print(t); print(")") + + case UnApply(fun, args) => + print(fun); print(" <unapply> "); printRow(args, "(", ", ", ")") + + case ArrayValue(elemtpt, trees) => + print("Array["); print(elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => "); print(body); print(")") + if (settings.uniqid.value && tree.symbol != null) print("#"+tree.symbol.id) + + case Assign(lhs, rhs) => + print(lhs); print(" = "); print(rhs) + + case AssignOrNamedArg(lhs, rhs) => + print(lhs); print(" = "); print(rhs) + + case If(cond, thenp, elsep) => + print("if ("); print(cond); print(")"); indent; println() + print(thenp); undent + if (!elsep.isEmpty) { + println(); print("else"); indent; println(); print(elsep); undent + } + + case Return(expr) => + print("return "); print(expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block) + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw "); print(expr) + + case New(tpe) => + print("new "); print(tpe) + + case Typed(expr, tp) => + print("("); print(expr); print(": "); print(tp); print(")") + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]") + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")") + + case ApplyDynamic(qual, vargs) => + print("<apply-dynamic>("); print(qual); print("#"); print(tree.symbol.nameString) + printRow(vargs, ", (", ", ", "))") + + case Super(This(qual), mix) => + if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") + print("super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case Super(qual, mix) => + print(qual) + print(".super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case This(qual) => + if (!qual.isEmpty) print(symName(tree, qual) + ".") + print("this") + + case Select(qual @ New(tpe), name) if (!settings.debug.value) => + print(qual) + + case Select(qualifier, name) => + print(backquotedPath(qualifier)); print("."); print(symName(tree, name)) + + case Ident(name) => + print(symName(tree, name)) + + case Literal(x) => + print(x.escapedStringValue) + + case tt: TypeTree => + if ((tree.tpe eq null) || (settings.Xprintpos.value && tt.original != null)) { + if (tt.original != null) { print("<type: "); print(tt.original); print(">") } + else print("<type ?>") + } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { + print(tree.tpe.typeSymbol.toString()) + } else { + print(tree.tpe.toString()) + } + + case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => + def printAnnot() { + print("@"); print(tpt) + if (!args.isEmpty) + printRow(args, "(", ",", ")") + } + if (tree.isType) { print(tree); print(" "); printAnnot() } + else { print(tree); print(": "); printAnnot() } + + case SingletonTypeTree(ref) => + print(ref); print(".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier); print("#"); print(symName(tree, selector)) + + case CompoundTypeTree(templ) => + print(templ) + + case AppliedTypeTree(tp, args) => + print(tp); printRow(args, "[", ", ", "]") + + case TypeBoundsTree(lo, hi) => + printOpt(" >: ", lo); printOpt(" <: ", hi) + + case ExistentialTypeTree(tpt, whereClauses) => + print(tpt); + printColumn(whereClauses, " forSome { ", ";", "}") + + case SelectFromArray(qualifier, name, _) => + print(qualifier); print(".<arr>"); print(symName(tree, name)) + + case TypeTreeWithDeferredRefCheck() => + print("<tree with deferred refcheck>") + + case tree => + print("<unknown tree of class "+tree.getClass+">") + } + if (settings.printtypes.value && tree.isTerm && !tree.isEmpty) { + print("{"); print(if (tree.tpe eq null) "<null>" else tree.tpe.toString()); print("}") + } + } + + def print(tree: Tree) { + printPosition(tree) + printRaw( + //nsc if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { + //nsc tree match { + //nsc case ClassDef(_, _, _, impl @ Template(ps, emptyValDef, body)) + //nsc if (tree.symbol.thisSym != tree.symbol) => + //nsc ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body)) + //nsc case ClassDef(_, _, _, impl) => ClassDef(tree.symbol, impl) + //nsc case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) + //nsc case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) + //nsc case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) + //nsc case TypeDef(_, _, _, rhs) => TypeDef(tree.symbol, rhs) + //nsc case _ => tree + //nsc } + //nsc } else + tree) + } + } + + def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) + def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) + def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) + + /** A writer that writes to the current Console and + * is sensitive to replacement of the Console's + * output stream. + */ + object ConsoleWriter extends Writer { + override def write(str: String) { Console.print(str) } + + def write(cbuf: Array[Char], off: Int, len: Int) { + write(new String(cbuf, off, len)) + } + + def close = { /* do nothing */ } + def flush = { /* do nothing */ } + } +} diff --git a/src/compiler/scala/reflect/common/Trees.scala b/src/compiler/scala/reflect/common/Trees.scala new file mode 100644 index 0000000000..f7008d7a65 --- /dev/null +++ b/src/compiler/scala/reflect/common/Trees.scala @@ -0,0 +1,1823 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import scala.collection.mutable.ListBuffer +import Flags._ +import util.HashSet +import java.io.{ PrintWriter, StringWriter } +import Flags._ + +//import scala.tools.nsc.util.{ FreshNameCreator, HashSet, SourceFile } + +trait Trees /*extends reflect.generic.Trees*/ { self: SymbolTable => + + type DocComment <: { def raw: String } + + private[scala] var nodeCount = 0 + + object treeInfo extends { + val trees: Trees.this.type = self + } with TreeInfo + + /** @param privateWithin the qualifier for a private (a type name) + * or tpnme.EMPTY, if none is given. + * @param annotations the annotations for the definition. + * <strong>Note:</strong> the typechecker drops these annotations, + * use the AnnotationInfo's (Symbol.annotations) in later phases. + */ + case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) extends HasFlags { + /* Abstract types from HasFlags. */ + type FlagsType = Long + type AccessBoundaryType = Name + type AnnotationType = Tree + + def hasAccessBoundary = privateWithin != tpnme.EMPTY + def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask + def hasFlag(flag: Long) = (flag & flags) != 0L + def hasFlagsToString(mask: Long): String = flagsToString( + flags & mask, + if (hasAccessBoundary) privateWithin.toString else "" + ) + def & (flag: Long): Modifiers = { + val flags1 = flags & flag + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations, positions) + } + def &~ (flag: Long): Modifiers = { + val flags1 = flags & (~flag) + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations, positions) + } + def | (flag: Long): Modifiers = { + val flags1 = flags | flag + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations, positions) + } + def withAnnotations(annots: List[Tree]) = + if (annots.isEmpty) this + else copy(annotations = annotations ::: annots) + def withPosition(flag: Long, position: Position) = + copy(positions = positions + (flag -> position)) + + override def toString = "Modifiers(%s, %s, %s)".format(hasFlagsToString(-1L), annotations mkString ", ", positions) + } + + def Modifiers(flags: Long, privateWithin: Name): Modifiers = Modifiers(flags, privateWithin, List(), Map.empty) + def Modifiers(flags: Long): Modifiers = Modifiers(flags, tpnme.EMPTY) + + lazy val NoMods = Modifiers(0) + + // ------ tree base classes -------------------------------------------------- + + /** The base class for all trees */ + abstract class Tree extends Product { + val id = nodeCount + nodeCount += 1 + + private[this] var rawpos: Position = NoPosition + + def pos = rawpos + def pos_=(pos: Position) = rawpos = pos + def setPos(pos: Position): this.type = { rawpos = pos; this } + + private[this] var rawtpe: Type = _ + + def tpe = rawtpe + def tpe_=(t: Type) = rawtpe = t + + /** Set tpe to give `tp` and return this. + */ + def setType(tp: Type): this.type = { rawtpe = tp; this } + + /** Like `setType`, but if this is a previously empty TypeTree + * that fact is remembered so that resetType will snap back. + */ + def defineType(tp: Type): this.type = setType(tp) + + def symbol: Symbol = null + def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) } + def setSymbol(sym: Symbol): this.type = { symbol = sym; this } + + def hasSymbol = false + def isDef = false + def isEmpty = false + + def hasSymbolWhich(f: Symbol => Boolean) = hasSymbol && f(symbol) + + def isTerm: Boolean = this match { + case _: TermTree => true + case Bind(name, _) => name.isTermName + case Select(_, name) => name.isTermName + case Ident(name) => name.isTermName + case Annotated(_, arg) => arg.isTerm + case DocDef(_, defn) => defn.isTerm + case _ => false + } + + def isType: Boolean = this match { + case _: TypTree => true + case Bind(name, _) => name.isTypeName + case Select(_, name) => name.isTypeName + case Ident(name) => name.isTypeName + case Annotated(_, arg) => arg.isType + case DocDef(_, defn) => defn.isType + case _ => false + } + + def isErroneous = (this.tpe ne null) && this.tpe.isErroneous + def isTyped = (this.tpe ne null) && !this.tpe.isErroneous + + /** Apply `f' to each subtree */ + def foreach(f: Tree => Unit) { new ForeachTreeTraverser(f).traverse(this) } + + /** If 'pf' is defined for a given subtree, call super.traverse(pf(tree)), + * otherwise super.traverse(tree). + */ + def foreachPartial(pf: PartialFunction[Tree, Tree]) { new ForeachPartialTreeTraverser(pf).traverse(this) } + + /** Find all subtrees matching predicate `p' */ + def filter(f: Tree => Boolean): List[Tree] = { + val ft = new FilterTreeTraverser(f) + ft.traverse(this) + ft.hits.toList + } + + /** Returns optionally first tree (in a preorder traversal) which satisfies predicate `p', + * or None if none exists. + */ + def find(p: Tree => Boolean): Option[Tree] = { + val ft = new FindTreeTraverser(p) + ft.traverse(this) + ft.result + } + + def changeOwner(pairs: (Symbol, Symbol)*): Tree = { + pairs.foldLeft(this) { case (t, (oldOwner, newOwner)) => + new ChangeOwnerTraverser(oldOwner, newOwner) apply t + } + } + + /** Is there part of this tree which satisfies predicate `p'? */ + def exists(p: Tree => Boolean): Boolean = !find(p).isEmpty + + def equalsStructure(that : Tree) = equalsStructure0(that)(_ eq _) + def equalsStructure0(that: Tree)(f: (Tree,Tree) => Boolean): Boolean = + f(this, that) || ((this.productArity == that.productArity) && { + def equals0(this0: Any, that0: Any): Boolean = (this0, that0) match { + case (x: Tree, y: Tree) => f(x, y) || (x equalsStructure0 y)(f) + case (xs: List[_], ys: List[_]) => (xs corresponds ys)(equals0) + case _ => this0 == that0 + } + def compareOriginals() = (this, that) match { + case (x: TypeTree, y: TypeTree) if x.original != null && y.original != null => + (x.original equalsStructure0 y.original)(f) + case _ => + true + } + + (this.productIterator zip that.productIterator forall { case (x, y) => equals0(x, y) }) && compareOriginals() + }) + + def shallowDuplicate: Tree = new ShallowDuplicator(this) transform this + def shortClass: String = this.getClass.getName split "[.$]" last + + /** The direct child trees of this tree + * EmptyTrees are always omitted. Lists are collapsed. + */ + def children: List[Tree] = { + def subtrees(x: Any): List[Tree] = x match { + case EmptyTree => Nil + case t: Tree => List(t) + case xs: List[_] => xs flatMap subtrees + case _ => Nil + } + productIterator.toList flatMap subtrees + } + + /** Make a copy of this tree, keeping all attributes, + * except that all positions are focused (so nothing + * in this tree will be found when searching by position). + */ + private[scala] def duplicate: this.type = + duplicateTree(this).asInstanceOf[this.type] + + private[scala] def copyAttrs(tree: Tree): this.type = { + pos = tree.pos + tpe = tree.tpe + if (hasSymbol) symbol = tree.symbol + this + } + + override def toString(): String = { + val buffer = new StringWriter() + val printer = newTreePrinter(new PrintWriter(buffer)) + printer.print(this) + printer.flush() + buffer.toString + } + + override def hashCode(): Int = System.identityHashCode(this) + override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + } + + trait SymTree extends Tree { + override def hasSymbol = true + override var symbol: Symbol = NoSymbol + } + + trait RefTree extends SymTree { + def name: Name + } + + abstract class DefTree extends SymTree { + def name: Name + override def isDef = true + } + + trait TermTree extends Tree + + /** A tree for a type. Note that not all type trees implement + * this trait; in particular, Ident's are an exception. */ + trait TypTree extends Tree + +// ----- tree node alternatives -------------------------------------- + + /** The empty tree */ + case object EmptyTree extends TermTree { + super.tpe_=(NoType) + override def tpe_=(t: Type) = + if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") + override def isEmpty = true + } + + abstract class MemberDef extends DefTree { + def mods: Modifiers + def keyword: String = this match { + case TypeDef(_, _, _, _) => "type" + case ClassDef(mods, _, _, _) => if (mods hasFlag TRAIT) "trait" else "class" + case DefDef(_, _, _, _, _, _) => "def" + case ModuleDef(_, _, _) => "object" + case PackageDef(_, _) => "package" + case ValDef(mods, _, _, _) => if (mods.isMutable) "var" else "val" + case _ => "" + } + // final def hasFlag(mask: Long): Boolean = mods hasFlag mask + } + + /** Package clause + */ + case class PackageDef(pid: RefTree, stats: List[Tree]) + extends MemberDef { + def name = pid.name + def mods = NoMods + } + + abstract class ImplDef extends MemberDef { + def impl: Template + } + + /** Class definition */ + case class ClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template) + extends ImplDef + + /** Singleton object definition + */ + case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) + extends ImplDef + + abstract class ValOrDefDef extends MemberDef { + def name: TermName + def tpt: Tree + def rhs: Tree + } + + /** Value definition + */ + case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef + + /** Method definition + */ + case class DefDef(mods: Modifiers, name: TermName, tparams: List[TypeDef], + vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef + + /** Abstract type, type parameter, or type alias */ + case class TypeDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) + extends MemberDef + + /** <p> + * Labelled expression - the symbols in the array (must be Idents!) + * are those the label takes as argument + * </p> + * <p> + * The symbol that is given to the labeldef should have a MethodType + * (as if it were a nested function) + * </p> + * <p> + * Jumps are apply nodes attributed with label symbol, the arguments + * will get assigned to the idents. + * </p> + * <p> + * Note: on 2005-06-09 Martin, Iuli, Burak agreed to have forward + * jumps within a Block. + * </p> + */ + case class LabelDef(name: TermName, params: List[Ident], rhs: Tree) + extends DefTree with TermTree + + + /** Import selector + * + * Representation of an imported name its optional rename and their optional positions + * + * @param name the imported name + * @param namePos its position or -1 if undefined + * @param rename the name the import is renamed to (== name if no renaming) + * @param renamePos the position of the rename or -1 if undefined + */ + case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) + + /** Import clause + * + * @param expr + * @param selectors + */ + case class Import(expr: Tree, selectors: List[ImportSelector]) + extends SymTree + // The symbol of an Import is an import symbol @see Symbol.newImport + // It's used primarily as a marker to check that the import has been typechecked. + + /** Instantiation template of a class or trait + * + * @param parents + * @param body + */ + case class Template(parents: List[Tree], self: ValDef, body: List[Tree]) + extends SymTree { + // the symbol of a template is a local dummy. @see Symbol.newLocalDummy + // the owner of the local dummy is the enclosing trait or class. + // the local dummy is itself the owner of any local blocks + // For example: + // + // class C { + // def foo // owner is C + // { + // def bar // owner is local dummy + // } + // System.err.println("TEMPLATE: " + parents) + } + + /** Block of expressions (semicolon separated expressions) */ + case class Block(stats: List[Tree], expr: Tree) + extends TermTree + + /** Case clause in a pattern match, eliminated during explicitouter + * (except for occurrences in switch statements) + */ + case class CaseDef(pat: Tree, guard: Tree, body: Tree) + extends Tree + + /** Alternatives of patterns, eliminated by explicitouter, except for + * occurrences in encoded Switch stmt (=remaining Match(CaseDef(...)) + */ + case class Alternative(trees: List[Tree]) + extends TermTree + + /** Repetition of pattern, eliminated by explicitouter */ + case class Star(elem: Tree) + extends TermTree + + /** Bind of a variable to a rhs pattern, eliminated by explicitouter + * + * @param name + * @param body + */ + case class Bind(name: Name, body: Tree) + extends DefTree + + case class UnApply(fun: Tree, args: List[Tree]) + extends TermTree + + /** Array of expressions, needs to be translated in backend, + */ + case class ArrayValue(elemtpt: Tree, elems: List[Tree]) + extends TermTree + + /** Anonymous function, eliminated by analyzer */ + case class Function(vparams: List[ValDef], body: Tree) + extends TermTree with SymTree + // The symbol of a Function is a synthetic value of name nme.ANON_FUN_NAME + // It is the owner of the function's parameters. + + /** Assignment */ + case class Assign(lhs: Tree, rhs: Tree) + extends TermTree + + /** Conditional expression */ + case class If(cond: Tree, thenp: Tree, elsep: Tree) + extends TermTree + + /** <p> + * Pattern matching expression (before explicitouter) + * Switch statements (after explicitouter) + * </p> + * <p> + * After explicitouter, cases will satisfy the following constraints: + * </p> + * <ul> + * <li>all guards are EmptyTree,</li> + * <li>all patterns will be either <code>Literal(Constant(x:Int))</code> + * or <code>Alternative(lit|...|lit)</code></li> + * <li>except for an "otherwise" branch, which has pattern + * <code>Ident(nme.WILDCARD)</code></li> + * </ul> + */ + case class Match(selector: Tree, cases: List[CaseDef]) + extends TermTree + + /** Return expression */ + case class Return(expr: Tree) + extends TermTree with SymTree + // The symbol of a Return node is the enclosing method. + + case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) + extends TermTree + + /** Throw expression */ + case class Throw(expr: Tree) + extends TermTree + + /** Object instantiation + * One should always use factory method below to build a user level new. + * + * @param tpt a class type + */ + case class New(tpt: Tree) extends TermTree + + /** Type annotation, eliminated by explicit outer */ + case class Typed(expr: Tree, tpt: Tree) + extends TermTree + + // Martin to Sean: Should GenericApply/TypeApply/Apply not be SymTree's? After all, + // ApplyDynamic is a SymTree. + abstract class GenericApply extends TermTree { + val fun: Tree + val args: List[Tree] + } + + /** Type application */ + case class TypeApply(fun: Tree, args: List[Tree]) + extends GenericApply { + override def symbol: Symbol = fun.symbol + override def symbol_=(sym: Symbol) { fun.symbol = sym } + } + + /** Value application */ + case class Apply(fun: Tree, args: List[Tree]) + extends GenericApply { + override def symbol: Symbol = fun.symbol + override def symbol_=(sym: Symbol) { fun.symbol = sym } + } + + /** Dynamic value application. + * In a dynamic application q.f(as) + * - q is stored in qual + * - as is stored in args + * - f is stored as the node's symbol field. + */ + case class ApplyDynamic(qual: Tree, args: List[Tree]) + extends TermTree with SymTree + // The symbol of an ApplyDynamic is the function symbol of `qual', or NoSymbol, if there is none. + + /** Super reference, qual = corresponding this reference */ + case class Super(qual: Tree, mix: TypeName) extends TermTree { + // The symbol of a Super is the class _from_ which the super reference is made. + // For instance in C.super(...), it would be C. + override def symbol: Symbol = qual.symbol + override def symbol_=(sym: Symbol) { qual.symbol = sym } + } + + /** Self reference */ + case class This(qual: TypeName) + extends TermTree with SymTree + // The symbol of a This is the class to which the this refers. + // For instance in C.this, it would be C. + + /** Designator <qualifier> . <name> */ + case class Select(qualifier: Tree, name: Name) + extends RefTree + + /** Identifier <name> */ + case class Ident(name: Name) extends RefTree { } + + class BackQuotedIdent(name: Name) extends Ident(name) + + /** Literal */ + case class Literal(value: Constant) + extends TermTree { + assert(value ne null) + } + + def Literal(value: Any): Literal = + Literal(Constant(value)) + + /** A tree that has an annotation attached to it. Only used for annotated types and + * annotation ascriptions, annotations on definitions are stored in the Modifiers. + * Eliminated by typechecker (typedAnnotated), the annotations are then stored in + * an AnnotatedType. + */ + case class Annotated(annot: Tree, arg: Tree) extends Tree + + /** Singleton type, eliminated by RefCheck */ + case class SingletonTypeTree(ref: Tree) + extends TypTree + + /** Type selection <qualifier> # <name>, eliminated by RefCheck */ + case class SelectFromTypeTree(qualifier: Tree, name: TypeName) + extends TypTree with RefTree + + /** Intersection type <parent1> with ... with <parentN> { <decls> }, eliminated by RefCheck */ + case class CompoundTypeTree(templ: Template) + extends TypTree + + /** Applied type <tpt> [ <args> ], eliminated by RefCheck */ + case class AppliedTypeTree(tpt: Tree, args: List[Tree]) + extends TypTree { + override def symbol: Symbol = tpt.symbol + override def symbol_=(sym: Symbol) { tpt.symbol = sym } + } + + case class TypeBoundsTree(lo: Tree, hi: Tree) + extends TypTree + + case class ExistentialTypeTree(tpt: Tree, whereClauses: List[Tree]) + extends TypTree + + /** Array selection <qualifier> . <name> only used during erasure */ + case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) + extends TermTree with RefTree { } + + /** A synthetic tree holding an arbitrary type. Not to be confused with + * with TypTree, the trait for trees that are only used for type trees. + * TypeTree's are inserted in several places, but most notably in + * <code>RefCheck</code>, where the arbitrary type trees are all replaced by + * TypeTree's. */ + case class TypeTree() extends TypTree { + private var orig: Tree = null + private[Trees] var wasEmpty: Boolean = false + + override def symbol = if (tpe == null) null else tpe.typeSymbol + override def isEmpty = (tpe eq null) || tpe == NoType + + def original: Tree = orig + def setOriginal(tree: Tree): this.type = { + def followOriginal(t: Tree): Tree = t match { + case tt: TypeTree => followOriginal(tt.original) + case t => t + } + + orig = followOriginal(tree); setPos(tree.pos); + this + } + + override def defineType(tp: Type): this.type = { + wasEmpty = isEmpty + setType(tp) + } + } + + def TypeTree(tp: Type): TypeTree = TypeTree() setType tp + + /** Documented definition, eliminated by analyzer */ + case class DocDef(comment: DocComment, definition: Tree) + extends Tree { + override def symbol: Symbol = definition.symbol + override def symbol_=(sym: Symbol) { definition.symbol = sym } + // sean: seems to be important to the IDE + override def isDef = definition.isDef + } + + /** Either an assignment or a named argument. Only appears in argument lists, + * eliminated by typecheck (doTypedApply) + */ + case class AssignOrNamedArg(lhs: Tree, rhs: Tree) + extends TermTree + + case class Parens(args: List[Tree]) extends Tree // only used during parsing + + /** emitted by typer, eliminated by refchecks */ + case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree + // ---- values and creators --------------------------------------- + + /** @param sym the class symbol + * @return the implementation template + */ + def ClassDef(sym: Symbol, impl: Template): ClassDef = + atPos(sym.pos) { + ClassDef(Modifiers(sym.flags), + sym.name.toTypeName, + sym.typeParams map TypeDef, + impl) setSymbol sym + } + + /** Construct class definition with given class symbol, value parameters, + * supercall arguments and template body. + * + * @param sym the class symbol + * @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)' + * @param vparamss the value parameters -- if they have symbols they + * should be owned by `sym' + * @param argss the supercall arguments + * @param body the template statements without primary constructor + * and value parameter fields. + */ + def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): ClassDef = + ClassDef(sym, + Template(sym.info.parents map TypeTree, + if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym), + constrMods, vparamss, argss, body, superPos)) + + /** + * @param sym the class symbol + * @param impl the implementation template + */ + def ModuleDef(sym: Symbol, impl: Template): ModuleDef = + atPos(sym.pos) { + ModuleDef(Modifiers(sym.flags), sym.name, impl) setSymbol sym + } + + def ValDef(sym: Symbol, rhs: Tree): ValDef = + atPos(sym.pos) { + ValDef(Modifiers(sym.flags), sym.name, + TypeTree(sym.tpe) setPos focusPos(sym.pos), + rhs) setSymbol sym + } + + def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree) + + object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) { + override def isEmpty = true + super.setPos(NoPosition) + override def setPos(pos: Position) = { assert(false); this } + } + + def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = + atPos(sym.pos) { + assert(sym != NoSymbol) + DefDef(Modifiers(sym.flags), + sym.name, + sym.typeParams map TypeDef, + vparamss, + TypeTree(sym.tpe.finalResultType) setPos focusPos(sym.pos), + rhs) setSymbol sym + } + + def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = + DefDef(sym, Modifiers(sym.flags), vparamss, rhs) + + def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = + DefDef(sym, mods, sym.paramss map (_.map(ValDef)), rhs) + + def DefDef(sym: Symbol, rhs: Tree): DefDef = + DefDef(sym, Modifiers(sym.flags), rhs) + + def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = { + DefDef(sym, rhs(sym.info.paramss)) + } + + /** A TypeDef node which defines given `sym' with given tight hand side `rhs'. */ + def TypeDef(sym: Symbol, rhs: Tree): TypeDef = + atPos(sym.pos) { + TypeDef(Modifiers(sym.flags), sym.name.toTypeName, sym.typeParams map TypeDef, rhs) setSymbol sym + } + + /** A TypeDef node which defines abstract type or type parameter for given `sym' */ + def TypeDef(sym: Symbol): TypeDef = + TypeDef(sym, TypeBoundsTree(TypeTree(sym.info.bounds.lo), TypeTree(sym.info.bounds.hi))) + + def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = + atPos(sym.pos) { + LabelDef(sym.name, params map Ident, rhs) setSymbol sym + } + + /** Generates a template with constructor corresponding to + * + * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } + * extends superclass(args_1) ... (args_n) with mixins { self => body } + * + * This gets translated to + * + * extends superclass with mixins { self => + * presupers' // presupers without rhs + * vparamss // abstract fields corresponding to value parameters + * def <init>(vparamss) { + * presupers + * super.<init>(args) + * } + * body + * } + */ + def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): Template = { + /* Add constructor to template */ + + // create parameters for <init> as synthetic trees. + var vparamss1 = + vparamss map (vps => vps.map { vd => + atPos(focusPos(vd.pos)) { + ValDef( + Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR) withAnnotations vd.mods.annotations, + vd.name, vd.tpt.duplicate, vd.rhs.duplicate) + }}) + val (edefs, rest) = body span treeInfo.isEarlyDef + val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef + val (lvdefs, gvdefs) = evdefs map { + case vdef @ ValDef(mods, name, tpt, rhs) => + val fld = treeCopy.ValDef( + vdef.duplicate, mods, name, + atPos(focusPos(vdef.pos)) { TypeTree() setOriginal tpt setPos focusPos(tpt.pos) }, // atPos in case + EmptyTree) + val local = treeCopy.ValDef(vdef, Modifiers(PRESUPER), name, tpt, rhs) + (local, fld) + } unzip + + val constrs = { + if (constrMods hasFlag TRAIT) { + if (body forall treeInfo.isInterfaceMember) List() + else List( + atPos(wrappingPos(superPos, lvdefs)) ( + DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), List(List()), TypeTree(), Block(lvdefs, Literal(()))))) + } else { + // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section + if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) + vparamss1 = List() :: vparamss1; + val superRef: Tree = atPos(superPos) { + Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) + } + val superCall = (superRef /: argss) (Apply) + List( + atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( + DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(()))))) + } + } + // println("typed template, gvdefs = "+gvdefs+", parents = "+parents+", constrs = "+constrs) + constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs)) + // vparamss2 are used as field definitions for the class. remove defaults + val vparamss2 = vparamss map (vps => vps map { vd => + treeCopy.ValDef(vd, vd.mods &~ DEFAULTPARAM, vd.name, vd.tpt, EmptyTree) + }) + Template(parents, self, gvdefs ::: vparamss2.flatten ::: constrs ::: etdefs ::: rest) + } + + /** casedef shorthand */ + def CaseDef(pat: Tree, body: Tree): CaseDef = CaseDef(pat, EmptyTree, body) + + def Bind(sym: Symbol, body: Tree): Bind = + Bind(sym.name, body) setSymbol sym + + + /** Factory method for object creation `new tpt(args_1)...(args_n)` + * A `New(t, as)` is expanded to: `(new t).<init>(as)` + */ + def New(tpt: Tree, argss: List[List[Tree]]): Tree = { + assert(!argss.isEmpty) + val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR) + (superRef /: argss) (Apply) + } + + def Apply(sym: Symbol, args: Tree*): Tree = + Apply(Ident(sym), args.toList) + + def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix) + + def This(sym: Symbol): Tree = This(sym.name.toTypeName) setSymbol sym + + def Select(qualifier: Tree, sym: Symbol): Select = + Select(qualifier, sym.name) setSymbol sym + + def Ident(sym: Symbol): Ident = + Ident(sym.name) setSymbol sym + + /** Block factory that flattens directly nested blocks. + */ + def Block(stats: Tree*): Block = stats match { + case Seq(b @ Block(_, _)) => b + case Seq(stat) => Block(stats.toList, Literal(Constant(()))) + case Seq(_, rest @ _*) => Block(stats.init.toList, stats.last) + } + +// ----- subconstructors -------------------------------------------- + + class ApplyToImplicitArgs(fun: Tree, args: List[Tree]) extends Apply(fun, args) + + class ApplyImplicitView(fun: Tree, args: List[Tree]) extends Apply(fun, args) + +// ------ traversers, copiers, and transformers --------------------------------------------- + + val treeCopy = new LazyTreeCopier() + + class Traverser { + protected var currentOwner: Symbol = definitions.RootClass + def traverse(tree: Tree): Unit = tree match { + case EmptyTree => + ; + case PackageDef(pid, stats) => + traverse(pid) + atOwner(tree.symbol.moduleClass) { + traverseTrees(stats) + } + case ClassDef(mods, name, tparams, impl) => + atOwner(tree.symbol) { + traverseTrees(mods.annotations); traverseTrees(tparams); traverse(impl) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + traverseTrees(mods.annotations); traverse(impl) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + traverseTrees(mods.annotations); traverse(tpt); traverse(rhs) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + traverseTrees(mods.annotations); traverseTrees(tparams); traverseTreess(vparamss); traverse(tpt); traverse(rhs) + } + case TypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + traverseTrees(mods.annotations); traverseTrees(tparams); traverse(rhs) + } + case LabelDef(name, params, rhs) => + traverseTrees(params); traverse(rhs) + case Import(expr, selectors) => + traverse(expr) + case Annotated(annot, arg) => + traverse(annot); traverse(arg) + case Template(parents, self, body) => + traverseTrees(parents) + if (!self.isEmpty) traverse(self) + traverseStats(body, tree.symbol) + case Block(stats, expr) => + traverseTrees(stats); traverse(expr) + case CaseDef(pat, guard, body) => + traverse(pat); traverse(guard); traverse(body) + case Alternative(trees) => + traverseTrees(trees) + case Star(elem) => + traverse(elem) + case Bind(name, body) => + traverse(body) + case UnApply(fun, args) => + traverse(fun); traverseTrees(args) + case ArrayValue(elemtpt, trees) => + traverse(elemtpt); traverseTrees(trees) + case Function(vparams, body) => + atOwner(tree.symbol) { + traverseTrees(vparams); traverse(body) + } + case Assign(lhs, rhs) => + traverse(lhs); traverse(rhs) + case If(cond, thenp, elsep) => + traverse(cond); traverse(thenp); traverse(elsep) + case Match(selector, cases) => + traverse(selector); traverseTrees(cases) + case Return(expr) => + traverse(expr) + case Try(block, catches, finalizer) => + traverse(block); traverseTrees(catches); traverse(finalizer) + case Throw(expr) => + traverse(expr) + case New(tpt) => + traverse(tpt) + case Typed(expr, tpt) => + traverse(expr); traverse(tpt) + case TypeApply(fun, args) => + traverse(fun); traverseTrees(args) + case Apply(fun, args) => + traverse(fun); traverseTrees(args) + case ApplyDynamic(qual, args) => + traverse(qual); traverseTrees(args) + case Super(qual, _) => + traverse(qual) + case This(_) => + ; + case Select(qualifier, selector) => + traverse(qualifier) + case Ident(_) => + ; + case Literal(_) => + ; + case TypeTree() => + ; + case SingletonTypeTree(ref) => + traverse(ref) + case SelectFromTypeTree(qualifier, selector) => + traverse(qualifier) + case CompoundTypeTree(templ) => + traverse(templ) + case AppliedTypeTree(tpt, args) => + traverse(tpt); traverseTrees(args) + case TypeBoundsTree(lo, hi) => + traverse(lo); traverse(hi) + case ExistentialTypeTree(tpt, whereClauses) => + traverse(tpt); traverseTrees(whereClauses) + case SelectFromArray(qualifier, selector, erasure) => + traverse(qualifier) + case AssignOrNamedArg(lhs, rhs) => + traverse(lhs); traverse(rhs) + case DocDef(comment, definition) => + traverse(definition) + case Parens(ts) => + traverseTrees(ts) + case TypeTreeWithDeferredRefCheck() => // TODO: should we traverse the wrapped tree? + // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check) + } + + def traverseTrees(trees: List[Tree]) { + trees foreach traverse + } + def traverseTreess(treess: List[List[Tree]]) { + treess foreach traverseTrees + } + def traverseStats(stats: List[Tree], exprOwner: Symbol) { + stats foreach (stat => + if (exprOwner != currentOwner) atOwner(exprOwner)(traverse(stat)) + else traverse(stat) + ) + } + + def atOwner(owner: Symbol)(traverse: => Unit) { + val prevOwner = currentOwner + currentOwner = owner + traverse + currentOwner = prevOwner + } + + /** Leave apply available in the generic traverser to do something else. + */ + def apply[T <: Tree](tree: T): T = { traverse(tree); tree } + } + + abstract class TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], impl: Template): ClassDef + def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]): PackageDef + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template): ModuleDef + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree): ValDef + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef + def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree): TypeDef + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree): LabelDef + def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]): Import + def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef + def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]): Template + def Block(tree: Tree, stats: List[Tree], expr: Tree): Block + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef + def Alternative(tree: Tree, trees: List[Tree]): Alternative + def Star(tree: Tree, elem: Tree): Star + def Bind(tree: Tree, name: Name, body: Tree): Bind + def UnApply(tree: Tree, fun: Tree, args: List[Tree]): UnApply + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]): ArrayValue + def Function(tree: Tree, vparams: List[ValDef], body: Tree): Function + def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign + def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match + def Return(tree: Tree, expr: Tree): Return + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree): Try + def Throw(tree: Tree, expr: Tree): Throw + def New(tree: Tree, tpt: Tree): New + def Typed(tree: Tree, expr: Tree, tpt: Tree): Typed + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]): TypeApply + def Apply(tree: Tree, fun: Tree, args: List[Tree]): Apply + def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]): ApplyDynamic + def Super(tree: Tree, qual: Tree, mix: TypeName): Super + def This(tree: Tree, qual: Name): This + def Select(tree: Tree, qualifier: Tree, selector: Name): Select + def Ident(tree: Tree, name: Name): Ident + def Literal(tree: Tree, value: Constant): Literal + def TypeTree(tree: Tree): TypeTree + def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck + def Annotated(tree: Tree, annot: Tree, arg: Tree): Annotated + def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name): SelectFromTypeTree + def CompoundTypeTree(tree: Tree, templ: Template): CompoundTypeTree + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]): AppliedTypeTree + def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree): TypeBoundsTree + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[Tree]): ExistentialTypeTree + def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray + } + + class StrictTreeCopier extends TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], impl: Template) = + new ClassDef(mods, name.toTypeName, tparams, impl).copyAttrs(tree) + def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]) = + new PackageDef(pid, stats).copyAttrs(tree) + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = + new ModuleDef(mods, name, impl).copyAttrs(tree) + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = + new ValDef(mods, name, tpt, rhs).copyAttrs(tree) + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = + new DefDef(mods, name, tparams, vparamss, tpt, rhs).copyAttrs(tree) + def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree) = + new TypeDef(mods, name.toTypeName, tparams, rhs).copyAttrs(tree) + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = + new LabelDef(name, params, rhs).copyAttrs(tree) + def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = + new Import(expr, selectors).copyAttrs(tree) + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = + new DocDef(comment, definition).copyAttrs(tree) + def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]) = + new Template(parents, self, body).copyAttrs(tree) + def Block(tree: Tree, stats: List[Tree], expr: Tree) = + new Block(stats, expr).copyAttrs(tree) + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = + new CaseDef(pat, guard, body).copyAttrs(tree) + def Alternative(tree: Tree, trees: List[Tree]) = + new Alternative(trees).copyAttrs(tree) + def Star(tree: Tree, elem: Tree) = + new Star(elem).copyAttrs(tree) + def Bind(tree: Tree, name: Name, body: Tree) = + new Bind(name, body).copyAttrs(tree) + def UnApply(tree: Tree, fun: Tree, args: List[Tree]) = + new UnApply(fun, args).copyAttrs(tree) + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = + new ArrayValue(elemtpt, trees).copyAttrs(tree) + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = + new Function(vparams, body).copyAttrs(tree) + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = + new Assign(lhs, rhs).copyAttrs(tree) + def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = + new AssignOrNamedArg(lhs, rhs).copyAttrs(tree) + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = + new If(cond, thenp, elsep).copyAttrs(tree) + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = + new Match(selector, cases).copyAttrs(tree) + def Return(tree: Tree, expr: Tree) = + new Return(expr).copyAttrs(tree) + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = + new Try(block, catches, finalizer).copyAttrs(tree) + def Throw(tree: Tree, expr: Tree) = + new Throw(expr).copyAttrs(tree) + def New(tree: Tree, tpt: Tree) = + new New(tpt).copyAttrs(tree) + def Typed(tree: Tree, expr: Tree, tpt: Tree) = + new Typed(expr, tpt).copyAttrs(tree) + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = + new TypeApply(fun, args).copyAttrs(tree) + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = + (tree match { + case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) + case _: ApplyImplicitView => new ApplyImplicitView(fun, args) + case _ => new Apply(fun, args) + }).copyAttrs(tree) + def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = + new ApplyDynamic(qual, args).copyAttrs(tree) + def Super(tree: Tree, qual: Tree, mix: TypeName) = + new Super(qual, mix).copyAttrs(tree) + def This(tree: Tree, qual: Name) = + new This(qual.toTypeName).copyAttrs(tree) + def Select(tree: Tree, qualifier: Tree, selector: Name) = + new Select(qualifier, selector).copyAttrs(tree) + def Ident(tree: Tree, name: Name) = + new Ident(name).copyAttrs(tree) + def Literal(tree: Tree, value: Constant) = + new Literal(value).copyAttrs(tree) + def TypeTree(tree: Tree) = + new TypeTree().copyAttrs(tree) + def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match { + case dc@TypeTreeWithDeferredRefCheck() => new TypeTreeWithDeferredRefCheck()(dc.check).copyAttrs(tree) + } + def Annotated(tree: Tree, annot: Tree, arg: Tree) = + new Annotated(annot, arg).copyAttrs(tree) + def SingletonTypeTree(tree: Tree, ref: Tree) = + new SingletonTypeTree(ref).copyAttrs(tree) + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = + new SelectFromTypeTree(qualifier, selector.toTypeName).copyAttrs(tree) + def CompoundTypeTree(tree: Tree, templ: Template) = + new CompoundTypeTree(templ).copyAttrs(tree) + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = + new AppliedTypeTree(tpt, args).copyAttrs(tree) + def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree) = + new TypeBoundsTree(lo, hi).copyAttrs(tree) + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[Tree]) = + new ExistentialTypeTree(tpt, whereClauses).copyAttrs(tree) + def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = + new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree) + } + + class LazyTreeCopier(treeCopy: TreeCopier) extends TreeCopier { + def this() = this(new StrictTreeCopier) + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], impl: Template) = tree match { + case t @ ClassDef(mods0, name0, tparams0, impl0) + if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && (impl0 == impl) => t + case _ => treeCopy.ClassDef(tree, mods, name, tparams, impl) + } + def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]) = tree match { + case t @ PackageDef(pid0, stats0) + if (pid0 == pid) && (stats0 == stats) => t + case _ => treeCopy.PackageDef(tree, pid, stats) + } + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = tree match { + case t @ ModuleDef(mods0, name0, impl0) + if (mods0 == mods) && (name0 == name) && (impl0 == impl) => t + case _ => treeCopy.ModuleDef(tree, mods, name, impl) + } + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = tree match { + case t @ ValDef(mods0, name0, tpt0, rhs0) + if (mods0 == mods) && (name0 == name) && (tpt0 == tpt) && (rhs0 == rhs) => t + case _ => treeCopy.ValDef(tree, mods, name, tpt, rhs) + } + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = tree match { + case t @ DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) + if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && + (vparamss0 == vparamss) && (tpt0 == tpt) && (rhs == rhs0) => t + case _ => treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs) + } + def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree) = tree match { + case t @ TypeDef(mods0, name0, tparams0, rhs0) + if (mods0 == mods) && (name0 == name) && (tparams0 == tparams) && (rhs0 == rhs) => t + case _ => treeCopy.TypeDef(tree, mods, name, tparams, rhs) + } + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = tree match { + case t @ LabelDef(name0, params0, rhs0) + if (name0 == name) && (params0 == params) && (rhs0 == rhs) => t + case _ => treeCopy.LabelDef(tree, name, params, rhs) + } + def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = tree match { + case t @ Import(expr0, selectors0) + if (expr0 == expr) && (selectors0 == selectors) => t + case _ => treeCopy.Import(tree, expr, selectors) + } + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = tree match { + case t @ DocDef(comment0, definition0) + if (comment0 == comment) && (definition0 == definition) => t + case _ => treeCopy.DocDef(tree, comment, definition) + } + def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]) = tree match { + case t @ Template(parents0, self0, body0) + if (parents0 == parents) && (self0 == self) && (body0 == body) => t + case _ => treeCopy.Template(tree, parents, self, body) + } + def Block(tree: Tree, stats: List[Tree], expr: Tree) = tree match { + case t @ Block(stats0, expr0) + if ((stats0 == stats) && (expr0 == expr)) => t + case _ => treeCopy.Block(tree, stats, expr) + } + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = tree match { + case t @ CaseDef(pat0, guard0, body0) + if (pat0 == pat) && (guard0 == guard) && (body0 == body) => t + case _ => treeCopy.CaseDef(tree, pat, guard, body) + } + def Alternative(tree: Tree, trees: List[Tree]) = tree match { + case t @ Alternative(trees0) + if trees0 == trees => t + case _ => treeCopy.Alternative(tree, trees) + } + def Star(tree: Tree, elem: Tree) = tree match { + case t @ Star(elem0) + if elem0 == elem => t + case _ => treeCopy.Star(tree, elem) + } + def Bind(tree: Tree, name: Name, body: Tree) = tree match { + case t @ Bind(name0, body0) + if (name0 == name) && (body0 == body) => t + case _ => treeCopy.Bind(tree, name, body) + } + def UnApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ UnApply(fun0, args0) + if (fun0 == fun) && (args0 == args) => t + case _ => treeCopy.UnApply(tree, fun, args) + } + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = tree match { + case t @ ArrayValue(elemtpt0, trees0) + if (elemtpt0 == elemtpt) && (trees0 == trees) => t + case _ => treeCopy.ArrayValue(tree, elemtpt, trees) + } + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = tree match { + case t @ Function(vparams0, body0) + if (vparams0 == vparams) && (body0 == body) => t + case _ => treeCopy.Function(tree, vparams, body) + } + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = tree match { + case t @ Assign(lhs0, rhs0) + if (lhs0 == lhs) && (rhs0 == rhs) => t + case _ => treeCopy.Assign(tree, lhs, rhs) + } + def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match { + case t @ AssignOrNamedArg(lhs0, rhs0) + if (lhs0 == lhs) && (rhs0 == rhs) => t + case _ => treeCopy.AssignOrNamedArg(tree, lhs, rhs) + } + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = tree match { + case t @ If(cond0, thenp0, elsep0) + if (cond0 == cond) && (thenp0 == thenp) && (elsep0 == elsep) => t + case _ => treeCopy.If(tree, cond, thenp, elsep) + } + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = tree match { + case t @ Match(selector0, cases0) + if (selector0 == selector) && (cases0 == cases) => t + case _ => treeCopy.Match(tree, selector, cases) + } + def Return(tree: Tree, expr: Tree) = tree match { + case t @ Return(expr0) + if expr0 == expr => t + case _ => treeCopy.Return(tree, expr) + } + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = tree match { + case t @ Try(block0, catches0, finalizer0) + if (block0 == block) && (catches0 == catches) && (finalizer0 == finalizer) => t + case _ => treeCopy.Try(tree, block, catches, finalizer) + } + def Throw(tree: Tree, expr: Tree) = tree match { + case t @ Throw(expr0) + if expr0 == expr => t + case _ => treeCopy.Throw(tree, expr) + } + def New(tree: Tree, tpt: Tree) = tree match { + case t @ New(tpt0) + if tpt0 == tpt => t + case _ => treeCopy.New(tree, tpt) + } + def Typed(tree: Tree, expr: Tree, tpt: Tree) = tree match { + case t @ Typed(expr0, tpt0) + if (expr0 == expr) && (tpt0 == tpt) => t + case _ => treeCopy.Typed(tree, expr, tpt) + } + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ TypeApply(fun0, args0) + if (fun0 == fun) && (args0 == args) => t + case _ => treeCopy.TypeApply(tree, fun, args) + } + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ Apply(fun0, args0) + if (fun0 == fun) && (args0 == args) => t + case _ => treeCopy.Apply(tree, fun, args) + } + def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = tree match { + case t @ ApplyDynamic(qual0, args0) + if (qual0 == qual) && (args0 == args) => t + case _ => treeCopy.ApplyDynamic(tree, qual, args) + } + def Super(tree: Tree, qual: Tree, mix: TypeName) = tree match { + case t @ Super(qual0, mix0) + if (qual0 == qual) && (mix0 == mix) => t + case _ => treeCopy.Super(tree, qual, mix) + } + def This(tree: Tree, qual: Name) = tree match { + case t @ This(qual0) + if qual0 == qual => t + case _ => treeCopy.This(tree, qual) + } + def Select(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ Select(qualifier0, selector0) + if (qualifier0 == qualifier) && (selector0 == selector) => t + case _ => treeCopy.Select(tree, qualifier, selector) + } + def Ident(tree: Tree, name: Name) = tree match { + case t @ Ident(name0) + if name0 == name => t + case _ => treeCopy.Ident(tree, name) + } + def Literal(tree: Tree, value: Constant) = tree match { + case t @ Literal(value0) + if value0 == value => t + case _ => treeCopy.Literal(tree, value) + } + def TypeTree(tree: Tree) = tree match { + case t @ TypeTree() => t + case _ => treeCopy.TypeTree(tree) + } + def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match { + case t @ TypeTreeWithDeferredRefCheck() => t + case _ => treeCopy.TypeTreeWithDeferredRefCheck(tree) + } + def Annotated(tree: Tree, annot: Tree, arg: Tree) = tree match { + case t @ Annotated(annot0, arg0) + if (annot0==annot) => t + case _ => treeCopy.Annotated(tree, annot, arg) + } + def SingletonTypeTree(tree: Tree, ref: Tree) = tree match { + case t @ SingletonTypeTree(ref0) + if ref0 == ref => t + case _ => treeCopy.SingletonTypeTree(tree, ref) + } + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ SelectFromTypeTree(qualifier0, selector0) + if (qualifier0 == qualifier) && (selector0 == selector) => t + case _ => treeCopy.SelectFromTypeTree(tree, qualifier, selector) + } + def CompoundTypeTree(tree: Tree, templ: Template) = tree match { + case t @ CompoundTypeTree(templ0) + if templ0 == templ => t + case _ => treeCopy.CompoundTypeTree(tree, templ) + } + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = tree match { + case t @ AppliedTypeTree(tpt0, args0) + if (tpt0 == tpt) && (args0 == args) => t + case _ => treeCopy.AppliedTypeTree(tree, tpt, args) + } + def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree) = tree match { + case t @ TypeBoundsTree(lo0, hi0) + if (lo0 == lo) && (hi0 == hi) => t + case _ => treeCopy.TypeBoundsTree(tree, lo, hi) + } + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[Tree]) = tree match { + case t @ ExistentialTypeTree(tpt0, whereClauses0) + if (tpt0 == tpt) && (whereClauses0 == whereClauses) => t + case _ => treeCopy.ExistentialTypeTree(tree, tpt, whereClauses) + } + def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match { + case t @ SelectFromArray(qualifier0, selector0, _) + if (qualifier0 == qualifier) && (selector0 == selector) => t + case _ => treeCopy.SelectFromArray(tree, qualifier, selector, erasure) + } + } + + abstract class Transformer { + val treeCopy: TreeCopier = new LazyTreeCopier + protected var currentOwner: Symbol = definitions.RootClass + protected def currentMethod = currentOwner.enclMethod + protected def currentClass = currentOwner.enclClass + protected def currentPackage = currentOwner.toplevelClass.owner + def transform(tree: Tree): Tree = tree match { + case EmptyTree => + tree + case PackageDef(pid, stats) => + treeCopy.PackageDef( + tree, transform(pid).asInstanceOf[RefTree], + atOwner(tree.symbol.moduleClass) { + transformStats(stats, currentOwner) + } + ) + case ClassDef(mods, name, tparams, impl) => + atOwner(tree.symbol) { + treeCopy.ClassDef(tree, transformModifiers(mods), name, + transformTypeDefs(tparams), transformTemplate(impl)) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + treeCopy.ModuleDef(tree, transformModifiers(mods), + name, transformTemplate(impl)) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + treeCopy.ValDef(tree, transformModifiers(mods), + name, transform(tpt), transform(rhs)) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + treeCopy.DefDef(tree, transformModifiers(mods), name, + transformTypeDefs(tparams), transformValDefss(vparamss), + transform(tpt), transform(rhs)) + } + case TypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + treeCopy.TypeDef(tree, transformModifiers(mods), name, + transformTypeDefs(tparams), transform(rhs)) + } + case LabelDef(name, params, rhs) => + treeCopy.LabelDef(tree, name, transformIdents(params), transform(rhs)) //bq: Martin, once, atOwner(...) works, also change `LamdaLifter.proxy' + case Import(expr, selectors) => + treeCopy.Import(tree, transform(expr), selectors) + case DocDef(comment, definition) => + treeCopy.DocDef(tree, comment, transform(definition)) + case Template(parents, self, body) => + treeCopy.Template(tree, transformTrees(parents), transformValDef(self), transformStats(body, tree.symbol)) + case Block(stats, expr) => + treeCopy.Block(tree, transformStats(stats, currentOwner), transform(expr)) + case CaseDef(pat, guard, body) => + treeCopy.CaseDef(tree, transform(pat), transform(guard), transform(body)) + case Alternative(trees) => + treeCopy.Alternative(tree, transformTrees(trees)) + case Star(elem) => + treeCopy.Star(tree, transform(elem)) + case Bind(name, body) => + treeCopy.Bind(tree, name, transform(body)) + case UnApply(fun, args) => + treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala + case ArrayValue(elemtpt, trees) => + treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) + case Function(vparams, body) => + atOwner(tree.symbol) { + treeCopy.Function(tree, transformValDefs(vparams), transform(body)) + } + case Assign(lhs, rhs) => + treeCopy.Assign(tree, transform(lhs), transform(rhs)) + case AssignOrNamedArg(lhs, rhs) => + treeCopy.AssignOrNamedArg(tree, transform(lhs), transform(rhs)) + case If(cond, thenp, elsep) => + treeCopy.If(tree, transform(cond), transform(thenp), transform(elsep)) + case Match(selector, cases) => + treeCopy.Match(tree, transform(selector), transformCaseDefs(cases)) + case Return(expr) => + treeCopy.Return(tree, transform(expr)) + case Try(block, catches, finalizer) => + treeCopy.Try(tree, transform(block), transformCaseDefs(catches), transform(finalizer)) + case Throw(expr) => + treeCopy.Throw(tree, transform(expr)) + case New(tpt) => + treeCopy.New(tree, transform(tpt)) + case Typed(expr, tpt) => + treeCopy.Typed(tree, transform(expr), transform(tpt)) + case TypeApply(fun, args) => + treeCopy.TypeApply(tree, transform(fun), transformTrees(args)) + case Apply(fun, args) => + treeCopy.Apply(tree, transform(fun), transformTrees(args)) + case ApplyDynamic(qual, args) => + treeCopy.ApplyDynamic(tree, transform(qual), transformTrees(args)) + case Super(qual, mix) => + treeCopy.Super(tree, transform(qual), mix) + case This(qual) => + treeCopy.This(tree, qual) + case Select(qualifier, selector) => + treeCopy.Select(tree, transform(qualifier), selector) + case Ident(name) => + treeCopy.Ident(tree, name) + case Literal(value) => + treeCopy.Literal(tree, value) + case TypeTree() => + treeCopy.TypeTree(tree) + case TypeTreeWithDeferredRefCheck() => + treeCopy.TypeTreeWithDeferredRefCheck(tree) + case Annotated(annot, arg) => + treeCopy.Annotated(tree, transform(annot), transform(arg)) + case SingletonTypeTree(ref) => + treeCopy.SingletonTypeTree(tree, transform(ref)) + case SelectFromTypeTree(qualifier, selector) => + treeCopy.SelectFromTypeTree(tree, transform(qualifier), selector) + case CompoundTypeTree(templ) => + treeCopy.CompoundTypeTree(tree, transformTemplate(templ)) + case AppliedTypeTree(tpt, args) => + treeCopy.AppliedTypeTree(tree, transform(tpt), transformTrees(args)) + case TypeBoundsTree(lo, hi) => + treeCopy.TypeBoundsTree(tree, transform(lo), transform(hi)) + case ExistentialTypeTree(tpt, whereClauses) => + treeCopy.ExistentialTypeTree(tree, transform(tpt), transformTrees(whereClauses)) + case SelectFromArray(qualifier, selector, erasure) => + treeCopy.SelectFromArray(tree, transform(qualifier), selector, erasure) + } + + def transformTrees(trees: List[Tree]): List[Tree] = + trees mapConserve (transform(_)) + def transformTemplate(tree: Template): Template = + transform(tree: Tree).asInstanceOf[Template] + def transformTypeDefs(trees: List[TypeDef]): List[TypeDef] = + trees mapConserve (tree => transform(tree).asInstanceOf[TypeDef]) + def transformValDef(tree: ValDef): ValDef = + if (tree.isEmpty) tree else transform(tree).asInstanceOf[ValDef] + def transformValDefs(trees: List[ValDef]): List[ValDef] = + trees mapConserve (transformValDef(_)) + def transformValDefss(treess: List[List[ValDef]]): List[List[ValDef]] = + treess mapConserve (transformValDefs(_)) + def transformCaseDefs(trees: List[CaseDef]): List[CaseDef] = + trees mapConserve (tree => transform(tree).asInstanceOf[CaseDef]) + def transformIdents(trees: List[Ident]): List[Ident] = + trees mapConserve (tree => transform(tree).asInstanceOf[Ident]) + def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + stats mapConserve (stat => + if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(transform(stat)) + else transform(stat)) filter (EmptyTree !=) + def transformModifiers(mods: Modifiers): Modifiers = + Modifiers(mods.flags, mods.privateWithin, transformTrees(mods.annotations), mods.positions) + + def atOwner[A](owner: Symbol)(trans: => A): A = { + val prevOwner = currentOwner + currentOwner = owner + val result = trans + currentOwner = prevOwner + result + } + } + + private[scala] def duplicateTree(tree: Tree): Tree = duplicator transform tree + + private lazy val duplicator = new Transformer { + override val treeCopy = new StrictTreeCopier + override def transform(t: Tree) = { + val t1 = super.transform(t) + if ((t1 ne t) && isRangePos(t1.pos)) t1 setPos focusPos(t.pos) + t1 + } + } + + private class ShallowDuplicator(orig: Tree) extends Transformer { + override val treeCopy = new StrictTreeCopier + override def transform(tree: Tree) = + if (tree eq orig) super.transform(tree) + else tree + } + + class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(_) => + def subst(from: List[Symbol], to: List[Tree]): Tree = + if (from.isEmpty) tree + else if (tree.symbol == from.head) to.head + else subst(from.tail, to.tail); + subst(from, to) + case _ => + super.transform(tree) + } + } + + class TreeTypeSubstituter(val from: List[Symbol], val to: List[Type]) extends Traverser { + val typeSubst = new SubstTypeMap(from, to) + def fromContains = typeSubst.fromContains + + override def traverse(tree: Tree) { + if (tree.tpe ne null) tree.tpe = typeSubst(tree.tpe) + if (tree.isDef) { + val sym = tree.symbol + val info1 = typeSubst(sym.info) + if (info1 ne sym.info) sym.setInfo(info1) + } + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + override def toString() = "TreeTypeSubstituter("+from+","+to+")" + } + + lazy val EmptyTreeTypeSubstituter = new TreeTypeSubstituter(List(), List()) + + class TreeSymSubstTraverser(val from: List[Symbol], val to: List[Symbol]) extends Traverser { + val subst = new SubstSymMap(from, to) + override def traverse(tree: Tree) { + if (tree.tpe ne null) tree.tpe = subst(tree.tpe) + if (tree.isDef) { + val sym = tree.symbol + val info1 = subst(sym.info) + if (info1 ne sym.info) sym.setInfo(info1) + } + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + override def toString() = "TreeSymSubstTraverser("+from+","+to+")" + } + + /** Substitute symbols in 'from' with symbols in 'to'. Returns a new + * tree using the new symbols and whose Ident and Select nodes are + * name-consistent with the new symbols. + */ + class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends Transformer { + val symSubst = new SubstSymMap(from, to) + override def transform(tree: Tree): Tree = { + def subst(from: List[Symbol], to: List[Symbol]) { + if (!from.isEmpty) + if (tree.symbol == from.head) tree setSymbol to.head + else subst(from.tail, to.tail) + } + + if (tree.tpe ne null) tree.tpe = symSubst(tree.tpe) + if (tree.hasSymbol) { + subst(from, to) + tree match { + case Ident(name0) if tree.symbol != NoSymbol => + treeCopy.Ident(tree, tree.symbol.name) + case Select(qual, name0) => + treeCopy.Select(tree, transform(qual), tree.symbol.name) + case _ => + super.transform(tree) + } + } else + super.transform(tree) + } + def apply[T <: Tree](tree: T): T = transform(tree).asInstanceOf[T] + override def toString() = "TreeSymSubstituter("+from+","+to+")" + } + + class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { + def changeOwner(tree: Tree) = { + if ((tree.isDef || tree.isInstanceOf[Function]) && + tree.symbol != NoSymbol && tree.symbol.owner == oldowner) + tree.symbol.owner = newowner + } + override def traverse(tree: Tree) { + changeOwner(tree) + super.traverse(tree) + } + } + + object posAssigner extends Traverser { + var pos: Position = _ + override def traverse(t: Tree) { + if (t != EmptyTree && t.pos == NoPosition) { + t.setPos(pos) + super.traverse(t) + } + } + } + + def atPos[T <: Tree](pos: Position)(tree: T): T = { + posAssigner.pos = pos + posAssigner.traverse(tree) + tree + } + + class ForeachPartialTreeTraverser(pf: PartialFunction[Tree, Tree]) extends Traverser { + override def traverse(tree: Tree) { + val t = if (pf isDefinedAt tree) pf(tree) else tree + super.traverse(t) + } + } + + class ForeachTreeTraverser(f: Tree => Unit) extends Traverser { + override def traverse(t: Tree) { + f(t) + super.traverse(t) + } + } + + class FilterTreeTraverser(p: Tree => Boolean) extends Traverser { + val hits = new ListBuffer[Tree] + override def traverse(t: Tree) { + if (p(t)) hits += t + super.traverse(t) + } + } + + class FindTreeTraverser(p: Tree => Boolean) extends Traverser { + var result: Option[Tree] = None + override def traverse(t: Tree) { + if (result.isEmpty) { + if (p(t)) result = Some(t) + super.traverse(t) + } + } + } + + object resetPos extends Traverser { + override def traverse(t: Tree) { + if (t != EmptyTree) t.setPos(NoPosition) + super.traverse(t) + } + } + + + /** resets symbol and tpe fields in a tree, @see ResetAttrsTraverse + */ + def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x } + def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x } + + /** A traverser which resets symbol and tpe fields of all nodes in a given tree + * except for (1) TypeTree nodes, whose <code>.tpe</code> field is kept, and + * (2) This(pkg) nodes, where pkg refers to a package symbol -- their attributes are kept, and + * (3) if a <code>.symbol</code> field refers to a symbol which is defined + * outside the tree, it is also kept. + * + * (2) is necessary because some This(pkg) are generated where pkg is not + * an enclosing package.n In that case, resetting the symbol would cause the + * next type checking run to fail. See #3152. + * + * (bq:) This traverser has mutable state and should be discarded after use + */ + private class ResetAttrsTraverser extends Traverser { + protected def isLocal(sym: Symbol): Boolean = true + protected def resetDef(tree: Tree) { + tree.symbol = NoSymbol + } + override def traverse(tree: Tree): Unit = { + tree match { + case _: DefTree | Function(_, _) | Template(_, _, _) => + resetDef(tree) + tree.tpe = null + case tpt: TypeTree => + if (tpt.wasEmpty) tree.tpe = null + case This(_) if tree.symbol != null && tree.symbol.isPackageClass => + ; + case EmptyTree => + ; + case _ => + if (tree.hasSymbol && isLocal(tree.symbol)) tree.symbol = NoSymbol + tree.tpe = null + } + super.traverse(tree) + } + } + + private class ResetLocalAttrsTraverser extends ResetAttrsTraverser { + private val erasedSyms = HashSet[Symbol](8) + override protected def isLocal(sym: Symbol) = erasedSyms(sym) + override protected def resetDef(tree: Tree) { + erasedSyms addEntry tree.symbol + super.resetDef(tree) + } + override def traverse(tree: Tree): Unit = tree match { + case Template(parents, self, body) => + for (stat <- body) + if (stat.isDef) erasedSyms.addEntry(stat.symbol) + super.traverse(tree) + case _ => + super.traverse(tree) + } + } + + /* A standard pattern match + case EmptyTree => + case PackageDef(pid, stats) => + // package pid { stats } + case ClassDef(mods, name, tparams, impl) => + // mods class name [tparams] impl where impl = extends parents { defs } + case ModuleDef(mods, name, impl) => (eliminated by refcheck) + // mods object name impl where impl = extends parents { defs } + case ValDef(mods, name, tpt, rhs) => + // mods val name: tpt = rhs + // note missing type information is expressed by tpt = TypeTree() + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + // mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs + // note missing type information is expressed by tpt = TypeTree() + case TypeDef(mods, name, tparams, rhs) => (eliminated by erasure) + // mods type name[tparams] = rhs + // mods type name[tparams] >: lo <: hi, where lo, hi are in a TypeBoundsTree, + and DEFERRED is set in mods + case LabelDef(name, params, rhs) => + // used for tailcalls and like + // while/do are desugared to label defs as follows: + // while (cond) body ==> LabelDef($L, List(), if (cond) { body; L$() } else ()) + // do body while (cond) ==> LabelDef($L, List(), body; if (cond) L$() else ()) + case Import(expr, selectors) => (eliminated by typecheck) + // import expr.{selectors} + // Selectors are a list of pairs of names (from, to). + // The last (and maybe only name) may be a nme.WILDCARD + // for instance + // import qual.{x, y => z, _} would be represented as + // Import(qual, List(("x", "x"), ("y", "z"), (WILDCARD, null))) + case Template(parents, self, body) => + // extends parents { self => body } + // if self is missing it is represented as emptyValDef + case Block(stats, expr) => + // { stats; expr } + case CaseDef(pat, guard, body) => (eliminated by transmatch/explicitouter) + // case pat if guard => body + case Alternative(trees) => (eliminated by transmatch/explicitouter) + // pat1 | ... | patn + case Star(elem) => (eliminated by transmatch/explicitouter) + // pat* + case Bind(name, body) => (eliminated by transmatch/explicitouter) + // name @ pat + case UnApply(fun: Tree, args) (introduced by typer, eliminated by transmatch/explicitouter) + // used for unapply's + case ArrayValue(elemtpt, trees) => (introduced by uncurry) + // used to pass arguments to vararg arguments + // for instance, printf("%s%d", foo, 42) is translated to after uncurry to: + // Apply( + // Ident("printf"), + // Literal("%s%d"), + // ArrayValue(<Any>, List(Ident("foo"), Literal(42)))) + case Function(vparams, body) => (eliminated by lambdaLift) + // vparams => body where vparams:List[ValDef] + case Assign(lhs, rhs) => + // lhs = rhs + case If(cond, thenp, elsep) => + // if (cond) thenp else elsep + case Match(selector, cases) => + // selector match { cases } + case Return(expr) => + // return expr + case Try(block, catches, finalizer) => + // try block catch { catches } finally finalizer where catches: List[CaseDef] + case Throw(expr) => + // throw expr + case New(tpt) => + // new tpt always in the context: (new tpt).<init>[targs](args) + case Typed(expr, tpt) => (eliminated by erasure) + // expr: tpt + case TypeApply(fun, args) => + // fun[args] + case Apply(fun, args) => + // fun(args) + // for instance fun[targs](args) is expressed as Apply(TypeApply(fun, targs), args) + case ApplyDynamic(qual, args) (introduced by erasure, eliminated by cleanup) + // fun(args) + case Super(qual, mix) => + // qual.super[mix] if qual and/or mix is empty, ther are tpnme.EMPTY + case This(qual) => + // qual.this + case Select(qualifier, selector) => + // qualifier.selector + case Ident(name) => + // name + // note: type checker converts idents that refer to enclosing fields or methods + // to selects; name ==> this.name + case Literal(value) => + // value + case TypeTree() => (introduced by refcheck) + // a type that's not written out, but given in the tpe attribute + case Annotated(annot, arg) => (eliminated by typer) + // arg @annot for types, arg: @annot for exprs + case SingletonTypeTree(ref) => (eliminated by uncurry) + // ref.type + case SelectFromTypeTree(qualifier, selector) => (eliminated by uncurry) + // qualifier # selector, a path-dependent type p.T is expressed as p.type # T + case CompoundTypeTree(templ: Template) => (eliminated by uncurry) + // parent1 with ... with parentN { refinement } + case AppliedTypeTree(tpt, args) => (eliminated by uncurry) + // tpt[args] + case TypeBoundsTree(lo, hi) => (eliminated by uncurry) + // >: lo <: hi + case ExistentialTypeTree(tpt, whereClauses) => (eliminated by uncurry) + // tpt forSome { whereClauses } + +*/ +} + diff --git a/src/compiler/scala/reflect/common/TypeDebugging.scala b/src/compiler/scala/reflect/common/TypeDebugging.scala new file mode 100644 index 0000000000..6028473b39 --- /dev/null +++ b/src/compiler/scala/reflect/common/TypeDebugging.scala @@ -0,0 +1,72 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect +package common + +trait TypeDebugging { + self: SymbolTable => + + import definitions._ + + // @M toString that is safe during debugging (does not normalize, ...) + object TypeDebugStrings { + object str { + def parentheses(xs: List[_]): String = xs.mkString("(", ", ", ")") + def brackets(xs: List[_]): String = if (xs.isEmpty) "" else xs.mkString("[", ", ", "]") + def tparams(tparams: List[Type]): String = brackets(tparams map debug) + def parents(ps: List[Type]): String = (ps map debug).mkString(" with ") + def refine(defs: Scope): String = defs.toList.mkString("{", " ;\n ", "}") + } + + def dump(tp: Type): Unit = { + println("** " + tp + " / " + tp.getClass + " **") + import tp._ + + println("typeSymbol = " + typeSymbol) + println("termSymbol = " + termSymbol) + println("widen = " + widen) + println("deconst = " + deconst) + println("typeOfThis = " + typeOfThis) + println("bounds = " + bounds) + println("parents = " + parents) + println("prefixChain = " + prefixChain) + println("typeConstructor = " + typeConstructor) + println(" .. typeConstructor.typeParams = " + typeConstructor.typeParams) + println(" .. _.variance = " + (typeConstructor.typeParams map (_.variance))) + println("typeArgs = " + typeArgs) + println("resultType = " + resultType) + println("finalResultType = " + finalResultType) + println("paramss = " + paramss) + println("paramTypes = " + paramTypes) + println("typeParams = " + typeParams) + println("boundSyms = " + boundSyms) + println("baseTypeSeq = " + baseTypeSeq) + println("baseClasses = " + baseClasses) + println("toLongString = " + toLongString) + } + + private def debug(tp: Type): String = tp match { + case TypeRef(pre, sym, args) => debug(pre) + "." + sym.nameString + str.tparams(args) + case ThisType(sym) => sym.nameString + ".this" + case SingleType(pre, sym) => debug(pre) +"."+ sym.nameString +".type" + case RefinedType(parents, defs) => str.parents(parents) + str.refine(defs) + case ClassInfoType(parents, defs, clazz) => "class "+ clazz.nameString + str.parents(parents) + str.refine(defs) + case PolyType(tparams, result) => str.brackets(tparams) + " " + debug(result) + case TypeBounds(lo, hi) => ">: "+ debug(lo) +" <: "+ debug(hi) + case tv @ TypeVar(_, _) => tv.toString + case ExistentialType(tparams, qtpe) => "forSome "+ str.brackets(tparams) + " " + debug(qtpe) + case _ => tp.toString + } + def debugString(tp: Type) = debug(tp) + } + private def TDS = TypeDebugStrings + + def paramString(tp: Type) = TDS.str parentheses (tp.params map (_.defString)) + def typeParamsString(tp: Type) = TDS.str brackets (tp.typeParams map (_.defString)) + def typeArgsString(tp: Type) = TDS.str brackets (tp.typeArgs map (_.safeToString)) + def debugString(tp: Type) = TDS debugString tp +} + diff --git a/src/compiler/scala/reflect/common/Types.scala b/src/compiler/scala/reflect/common/Types.scala new file mode 100644 index 0000000000..78407cd868 --- /dev/null +++ b/src/compiler/scala/reflect/common/Types.scala @@ -0,0 +1,5699 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common + +import scala.collection.{ mutable, immutable } +import scala.ref.WeakReference +import mutable.ListBuffer +//import ast.TreeGen +//import util.{ Position, NoPosition } +import Flags._ +import scala.util.control.ControlThrowable +import scala.annotation.tailrec +import util.Statistics._ + +/* A standard type pattern match: + case ErrorType => + // internal: error + case WildcardType => + // internal: unknown + case NoType => + case NoPrefix => + case ThisType(sym) => + // sym.this.type + case SuperType(thistpe, supertpe) => + // super references + case SingleType(pre, sym) => + // pre.sym.type + case ConstantType(value) => + // Int(2) + case TypeRef(pre, sym, args) => + // pre.sym[targs] + // Outer.this.C would be represented as TypeRef(ThisType(Outer), C, List()) + case RefinedType(parents, defs) => + // parent1 with ... with parentn { defs } + case ExistentialType(tparams, result) => + // result forSome { tparams } + case AnnotatedType(annots, tp, selfsym) => + // tp @annots + + // the following are non-value types; you cannot write them down in Scala source. + + case TypeBounds(lo, hi) => + // >: lo <: hi + case ClassInfoType(parents, defs, clazz) => + // same as RefinedType except as body of class + case MethodType(paramtypes, result) => + // (paramtypes)result + // For instance def m(): T is represented as MethodType(List(), T) + case NullaryMethodType(result) => // eliminated by uncurry + // an eval-by-name type + // For instance def m: T is represented as NullaryMethodType(T) + case PolyType(tparams, result) => + // [tparams]result where result is a (Nullary)MethodType or ClassInfoType + + // The remaining types are not used after phase `typer'. + case OverloadedType(pre, tparams, alts) => + // all alternatives of an overloaded ident + case AntiPolyType(pre, targs) => + // rarely used, disappears when combined with a PolyType + case TypeVar(inst, constr) => + // a type variable + // Replace occurrences of type parameters with type vars, where + // inst is the instantiation and constr is a list of bounds. + case DeBruijnIndex(level, index) + // for dependent method types: a type referring to a method parameter. + // Not presently used, it seems. +*/ + +trait Types /*extends reflect.generic.Types*/ { self: SymbolTable => + import definitions._ + + //statistics + def uniqueTypeCount = if (uniques == null) 0 else uniques.size + + private var explainSwitch = false + private final val emptySymbolSet = immutable.Set.empty[Symbol] + + private final val alternativeNarrow = false + + private final val LogPendingSubTypesThreshold = 50 + private final val LogPendingBaseTypesThreshold = 50 + private final val LogVolatileThreshold = 50 + + /** A don't care value for the depth parameter in lubs/glbs and related operations */ + private final val AnyDepth = -3 + + /** Decrement depth unless it is a don't care */ + private final def decr(depth: Int) = if (depth == AnyDepth) AnyDepth else depth - 1 + + private final val printLubs = false + + /** The current skolemization level, needed for the algorithms + * in isSameType, isSubType that do constraint solving under a prefix + */ + var skolemizationLevel = 0 + + /** A log of type variable with their original constraints. Used in order + * to undo constraints in the case of isSubType/isSameType failure. + */ + object undoLog { + private type UndoLog = List[(TypeVar, TypeConstraint)] + private[scala] var log: UndoLog = List() + + /** Undo all changes to constraints to type variables upto `limit' + */ + private def undoTo(limit: UndoLog) { + while ((log ne limit) && log.nonEmpty) { + val (tv, constr) = log.head + tv.constr = constr + log = log.tail + } + } + + private[Types] def record(tv: TypeVar) = { + log ::= (tv, tv.constr.cloneInternal) + } + private[scala] def clear() { + if (settings.debug.value) + self.log("Clearing " + log.size + " entries from the undoLog.") + + log = Nil + } + + // `block` should not affect constraints on typevars + def undo[T](block: => T): T = { + val before = log + + try block + finally undoTo(before) + } + + // if `block` evaluates to false, it should not affect constraints on typevars + def undoUnless(block: => Boolean): Boolean = { + val before = log + var result = false + + try result = block + finally if (!result) undoTo(before) + + result + } + } + + /** A map from lists to compound types that have the given list as parents. + * This is used to avoid duplication in the computation of base type sequences and baseClasses. + * It makes use of the fact that these two operations depend only on the parents, + * not on the refinement. + */ + val intersectionWitness = new mutable.WeakHashMap[List[Type], WeakReference[Type]] + + //private object gen extends { + // val global : Types.this.type = Types.this + //} with TreeGen + + //import gen._ + + /** A proxy for a type (identified by field `underlying') that forwards most + * operations to it (for exceptions, see WrappingProxy, which forwards even more operations). + * every operation that is overridden for some kind of types should be forwarded. + */ + trait SimpleTypeProxy extends Type { + def underlying: Type + + // the following operations + those in RewrappingTypeProxy are all operations + // in class Type that are overridden in some subclass + // Important to keep this up-to-date when new operations are added! + override def isTrivial = underlying.isTrivial + override def isHigherKinded: Boolean = underlying.isHigherKinded + override def typeConstructor: Type = underlying.typeConstructor + override def isNotNull = underlying.isNotNull + override def isError = underlying.isError + override def isErroneous = underlying.isErroneous + override def isStable: Boolean = underlying.isStable + override def isVolatile = underlying.isVolatile + override def finalResultType = underlying.finalResultType + override def paramSectionCount = underlying.paramSectionCount + override def paramss = underlying.paramss + override def params = underlying.params + override def paramTypes = underlying.paramTypes + override def termSymbol = underlying.termSymbol + override def termSymbolDirect = underlying.termSymbolDirect + override def typeParams = underlying.typeParams + override def boundSyms = underlying.boundSyms + override def typeSymbol = underlying.typeSymbol + override def typeSymbolDirect = underlying.typeSymbolDirect + override def widen = underlying.widen + override def typeOfThis = underlying.typeOfThis + override def bounds = underlying.bounds + override def parents = underlying.parents + override def prefix = underlying.prefix + override def decls = underlying.decls + override def baseType(clazz: Symbol) = underlying.baseType(clazz) + override def baseTypeSeq = underlying.baseTypeSeq + override def baseTypeSeqDepth = underlying.baseTypeSeqDepth + override def baseClasses = underlying.baseClasses + } + + /** A proxy for a type (identified by field `underlying') that forwards most + * operations to it. Every operation that is overridden for some kind of types is + * forwarded here. Some operations are rewrapped again. + */ + trait RewrappingTypeProxy extends SimpleTypeProxy { + protected def maybeRewrap(newtp: Type) = if (newtp eq underlying) this else rewrap(newtp) + protected def rewrap(newtp: Type): Type + + // the following are all operations in class Type that are overridden in some subclass + // Important to keep this up-to-date when new operations are added! + override def widen = maybeRewrap(underlying.widen) + override def narrow = underlying.narrow + override def deconst = maybeRewrap(underlying.deconst) + override def resultType = maybeRewrap(underlying.resultType) + override def resultType(actuals: List[Type]) = maybeRewrap(underlying.resultType(actuals)) + override def finalResultType = maybeRewrap(underlying.finalResultType) + override def paramSectionCount = 0 + override def paramss: List[List[Symbol]] = List() + override def params: List[Symbol] = List() + override def paramTypes: List[Type] = List() + override def typeArgs = underlying.typeArgs + override def notNull = maybeRewrap(underlying.notNull) + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]) = underlying.instantiateTypeParams(formals, actuals) + override def skolemizeExistential(owner: Symbol, origin: AnyRef) = underlying.skolemizeExistential(owner, origin) + override def normalize = maybeRewrap(underlying.normalize) + override def dealias = maybeRewrap(underlying.dealias) + override def cloneInfo(owner: Symbol) = maybeRewrap(underlying.cloneInfo(owner)) + override def atOwner(owner: Symbol) = maybeRewrap(underlying.atOwner(owner)) + override def prefixString = underlying.prefixString + override def isComplete = underlying.isComplete + override def complete(sym: Symbol) = underlying.complete(sym) + override def load(sym: Symbol) { underlying.load(sym) } + override def withAnnotations(annots: List[AnnotationInfo]) = maybeRewrap(underlying.withAnnotations(annots)) + override def withoutAnnotations = maybeRewrap(underlying.withoutAnnotations) + } + + /** The base class for all types */ + abstract class Type { + + /** Types for which asSeenFrom always is the identity, no matter what + * prefix or owner. + */ + def isTrivial: Boolean = false + + /** Is this type higher-kinded, i.e., is it a type constructor @M */ + def isHigherKinded: Boolean = false + + /** Does this type denote a stable reference (i.e. singleton type)? */ + def isStable: Boolean = false + + /** Is this type dangerous (i.e. it might contain conflicting + * type information when empty, so that it can be constructed + * so that type unsoundness results.) A dangerous type has an underlying + * type of the form T_1 with T_n { decls }, where one of the + * T_i (i > 1) is an abstract type. + */ + def isVolatile: Boolean = false + + /** Is this type guaranteed not to have `null' as a value? */ + def isNotNull: Boolean = false + + /** Is this type a structural refinement type (it 'refines' members that have not been inherited) */ + def isStructuralRefinement: Boolean = false + + /** Does this type depend immediately on an enclosing method parameter? + * i.e., is it a singleton type whose termSymbol refers to an argument of the symbol's owner (which is a method) + */ + def isImmediatelyDependent: Boolean = false + + /** Does this depend on an enclosing method parameter? */ + def isDependent: Boolean = IsDependentCollector.collect(this) + + /** True for WildcardType or BoundedWildcardType */ + def isWildcard = false + + /** Is this type produced as a repair for an error? */ + def isError: Boolean = typeSymbol.isError || termSymbol.isError + + /** Is this type produced as a repair for an error? */ + def isErroneous: Boolean = ErroneousCollector.collect(this) + + /** Does this type denote a reference type which can be null? */ + // def isNullable: Boolean = false + + /** Can this type only be subtyped by bottom types? + * This is assessed to be the case if the class is final, + * and all type parameters (if any) are invariant. + */ + def isFinalType = + typeSymbol.isFinal && (typeSymbol.typeParams forall (_.variance == 0)) + + /** Is this type completed (i.e. not a lazy type)? + */ + def isComplete: Boolean = true + + /** If this is a lazy type, assign a new type to `sym'. */ + def complete(sym: Symbol) {} + + /** The term symbol associated with the type + * Note that the symbol of the normalized type is returned (@see normalize) + */ + def termSymbol: Symbol = NoSymbol + + /** The type symbol associated with the type + * Note that the symbol of the normalized type is returned (@see normalize) + */ + def typeSymbol: Symbol = NoSymbol + + /** The term symbol *directly* associated with the type + */ + def termSymbolDirect: Symbol = termSymbol + + /** The type symbol *directly* associated with the type + */ + def typeSymbolDirect: Symbol = typeSymbol + + /** The base type underlying a type proxy, + * identity on all other types */ + def underlying: Type = this + + /** Widen from singleton type to its underlying non-singleton + * base type by applying one or more `underlying' dereferences, + * identity for all other types. + * + * class Outer { class C ; val x: C } + * val o: Outer + * <o.x.type>.widen = o.C + */ + def widen: Type = this + + /** Map a constant type or not-null-type to its underlying base type, + * identity for all other types. + */ + def deconst: Type = this + + /** The type of `this' of a class type or reference type + */ + def typeOfThis: Type = typeSymbol.typeOfThis + + /** Map to a singleton type which is a subtype of this type. + * The fallback implemented here gives + * T.narrow = T' forSome { type T' <: T with Singleton } + * Overridden where we know more about where types come from. + */ + def narrow: Type = + if (phase.erasedTypes) this + else commonOwner(this) freshExistential ".type" setInfo singletonBounds(this) tpe + + /** For a TypeBounds type, itself; + * for a reference denoting an abstract type, its bounds, + * for all other types, a TypeBounds type all of whose bounds are this type. + */ + def bounds: TypeBounds = TypeBounds(this, this) + + /** For a class or intersection type, its parents. + * For a TypeBounds type, the parents of its hi bound. + * inherited by typerefs, singleton types, and refinement types, + * The empty list for all other types */ + def parents: List[Type] = List() + + /** For a typeref or single-type, the prefix of the normalized type (@see normalize). + * NoType for all other types. */ + def prefix: Type = NoType + + /** A chain of all typeref or singletype prefixes of this type, longest first. + * (Only used from safeToString.) + */ + def prefixChain: List[Type] = this match { + case TypeRef(pre, _, _) => pre :: pre.prefixChain + case SingleType(pre, _) => pre :: pre.prefixChain + case _ => List() + } + + /** This type, without its type arguments @M */ + def typeConstructor: Type = this + + /** For a typeref, its arguments. The empty list for all other types */ + def typeArgs: List[Type] = List() + + /** For a (nullary) method or poly type, its direct result type, + * the type itself for all other types. */ + def resultType: Type = this + + def resultType(actuals: List[Type]) = this + + /** Only used for dependent method types. */ + def resultApprox: Type = if (settings.YdepMethTpes.value) ApproximateDependentMap(resultType) else resultType + + /** If this is a TypeRef `clazz`[`T`], return the argument `T` + * otherwise return this type + */ + def remove(clazz: Symbol): Type = this + + /** For a curried/nullary method or poly type its non-method result type, + * the type itself for all other types */ + def finalResultType: Type = this + + /** For a method type, the number of its value parameter sections, + * 0 for all other types */ + def paramSectionCount: Int = 0 + + /** For a method or poly type, a list of its value parameter sections, + * the empty list for all other types */ + def paramss: List[List[Symbol]] = List() + + /** For a method or poly type, its first value parameter section, + * the empty list for all other types */ + def params: List[Symbol] = List() + + /** For a method or poly type, the types of its first value parameter section, + * the empty list for all other types */ + def paramTypes: List[Type] = List() + + /** For a (potentially wrapped) poly type, its type parameters, + * the empty list for all other types */ + def typeParams: List[Symbol] = List() + + /** For a (potentially wrapped) poly or existential type, its bound symbols, + * the empty list for all other types */ + def boundSyms: immutable.Set[Symbol] = emptySymbolSet + + /** Mixin a NotNull trait unless type already has one + * ...if the option is given, since it is causing typing bugs. + */ + def notNull: Type = + if (!settings.Ynotnull.value || isNotNull || phase.erasedTypes) this + else NotNullType(this) + + /** Replace formal type parameter symbols with actual type arguments. + * + * Amounts to substitution except for higher-kinded types. (See overridden method in TypeRef) -- @M + */ + def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = + if (sameLength(formals, actuals)) this.subst(formals, actuals) else ErrorType + + /** If this type is an existential, turn all existentially bound variables to type skolems. + * @param owner The owner of the created type skolems + * @param origin The tree whose type was an existential for which the skolem was created. + */ + def skolemizeExistential(owner: Symbol, origin: AnyRef): Type = this + + /** A simple version of skolemizeExistential for situations where + * owner or unpack location do not matter (typically used in subtype tests) + */ + def skolemizeExistential: Type = skolemizeExistential(NoSymbol, null) + + /** Reduce to beta eta-long normal form. + * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. + * Functions on types are also implemented as PolyTypes. + * + * Example: (in the below, <List> is the type constructor of List) + * TypeRef(pre, <List>, List()) is replaced by + * PolyType(X, TypeRef(pre, <List>, List(X))) + */ + def normalize = this // @MAT + + /** Expands type aliases. */ + def dealias = this + + + /** For a classtype or refined type, its defined or declared members; + * inherited by subtypes and typerefs. + * The empty scope for all other types. + */ + def decls: Scope = EmptyScope + + /** The defined or declared members with name `name' in this type; + * an OverloadedSymbol if several exist, NoSymbol if none exist. + * Alternatives of overloaded symbol appear in the order they are declared. + */ + def decl(name: Name): Symbol = findDecl(name, 0) + + /** The non-private defined or declared members with name `name' in this type; + * an OverloadedSymbol if several exist, NoSymbol if none exist. + * Alternatives of overloaded symbol appear in the order they are declared. + */ + def nonPrivateDecl(name: Name): Symbol = findDecl(name, PRIVATE) + + /** A list of all members of this type (defined or inherited) + * Members appear in linearization order of their owners. + * Members with the same owner appear in reverse order of their declarations. + */ + def members: List[Symbol] = findMember(nme.ANYNAME, 0, 0, false).alternatives + + /** A list of all non-private members of this type (defined or inherited) */ + def nonPrivateMembers: List[Symbol] = + findMember(nme.ANYNAME, PRIVATE | BRIDGES, 0, false).alternatives + + /** A list of all non-private members of this type (defined or inherited), + * admitting members with given flags `admit` + */ + def nonPrivateMembersAdmitting(admit: Long): List[Symbol] = + findMember(nme.ANYNAME, (PRIVATE | BRIDGES) & ~admit, 0, false).alternatives + + /** A list of all implicit symbols of this type (defined or inherited) */ + def implicitMembers: List[Symbol] = + findMember(nme.ANYNAME, BRIDGES, IMPLICIT, false).alternatives + + /** A list of all deferred symbols of this type (defined or inherited) */ + def deferredMembers: List[Symbol] = + findMember(nme.ANYNAME, BRIDGES, DEFERRED, false).alternatives + + /** The member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def member(name: Name): Symbol = findMember(name, BRIDGES, 0, false) + + /** The non-private member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist. + * Bridges are excluded from the result + */ + def nonPrivateMember(name: Name): Symbol = + findMember(name, PRIVATE | BRIDGES, 0, false) + + /** The non-private member with given name, admitting members with given flags `admit` + * an OverloadedSymbol if several exist, NoSymbol if none exist + */ + def nonPrivateMemberAdmitting(name: Name, admit: Long): Symbol = + findMember(name, (PRIVATE | BRIDGES) & ~admit, 0, false) + + /** The non-local member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def nonLocalMember(name: Name): Symbol = + findMember(name, LOCAL | BRIDGES, 0, false) + + /** The least type instance of given class which is a supertype + * of this type. Example: + * class D[T] + * class C extends p.D[Int] + * ThisType(C).baseType(D) = p.D[Int] + */ + def baseType(clazz: Symbol): Type = NoType + + /** This type as seen from prefix `pre' and class `clazz'. This means: + * Replace all thistypes of `clazz' or one of its subclasses + * by `pre' and instantiate all parameters by arguments of `pre'. + * Proceed analogously for thistypes referring to outer classes. + * + * Example: + * class D[T] { def m: T } + * class C extends p.D[Int] + * T.asSeenFrom(ThisType(C), D) (where D is owner of m) + * = Int + */ + def asSeenFrom(pre: Type, clazz: Symbol): Type = + if (!isTrivial && (!phase.erasedTypes || pre.typeSymbol == ArrayClass)) { + incCounter(asSeenFromCount) + val start = startTimer(asSeenFromNanos) + val m = new AsSeenFromMap(pre.normalize, clazz) + val tp = m apply this + val result = existentialAbstraction(m.capturedParams, tp) + stopTimer(asSeenFromNanos, start) + result + } else this + + /** The info of `sym', seen as a member of this type. + * + * Example: + * class D[T] { def m: T } + * class C extends p.D[Int] + * ThisType(C).memberType(m) = Int + */ + def memberInfo(sym: Symbol): Type = { + sym.info.asSeenFrom(this, sym.owner) + } + + /** The type of `sym', seen as a member of this type. */ + def memberType(sym: Symbol): Type = sym match { + case meth: MethodSymbol => + meth.typeAsMemberOf(this) + case _ => + computeMemberType(sym) + } + + def computeMemberType(sym: Symbol): Type = sym.tpeHK match { //@M don't prematurely instantiate higher-kinded types, they will be instantiated by transform, typedTypeApply, etc. when really necessary + case OverloadedType(_, alts) => + OverloadedType(this, alts) + case tp => + tp.asSeenFrom(this, sym.owner) + } + + /** Substitute types `to' for occurrences of references to + * symbols `from' in this type. + */ + def subst(from: List[Symbol], to: List[Type]): Type = + new SubstTypeMap(from, to) apply this + + /** Substitute symbols `to' for occurrences of symbols + * `from' in this type. + * !!! NOTE !!!: If you need to do a substThis and a substSym, the substThis has to come + * first, as otherwise symbols will immediately get rebound in typeRef to the old + * symbol. + */ + def substSym(from: List[Symbol], to: List[Symbol]): Type = + if (from eq to) this + else new SubstSymMap(from, to) apply this + + /** Substitute all occurrences of `ThisType(from)' in this type + * by `to'. + * !!! NOTE !!!: If you need to do a substThis and a substSym, the substThis has to come + * first, as otherwise symbols will immediately get rebound in typeRef to the old + * symbol. + */ + def substThis(from: Symbol, to: Type): Type = + new SubstThisMap(from, to) apply this + + def substSuper(from: Type, to: Type): Type = + new SubstSuperMap(from, to) apply this + + /** Returns all parts of this type which satisfy predicate `p' */ + def filter(p: Type => Boolean): List[Type] = new FilterTypeCollector(p).collect(this).toList + + /** Returns optionally first type (in a preorder traversal) which satisfies predicate `p', + * or None if none exists. + */ + def find(p: Type => Boolean): Option[Type] = new FindTypeCollector(p).collect(this) + + /** Apply `f' to each part of this type */ + def foreach(f: Type => Unit) { new ForEachTypeTraverser(f).traverse(this) } + + /** Apply `f' to each part of this type; children get mapped before their parents */ + def map(f: Type => Type): Type = new TypeMap { + def apply(x: Type) = f(mapOver(x)) + } apply this + + /** Is there part of this type which satisfies predicate `p'? */ + def exists(p: Type => Boolean): Boolean = !find(p).isEmpty + + /** Does this type contain a reference to this symbol? */ + def contains(sym: Symbol): Boolean = new ContainsCollector(sym).collect(this) + + /** Does this type contain a reference to this type */ + def containsTp(tp: Type): Boolean = new ContainsTypeCollector(tp).collect(this) + + /** Is this type a subtype of that type? */ + def <:<(that: Type): Boolean = { + if (util.Statistics.enabled) stat_<:<(that) + else { + (this eq that) || + (if (explainSwitch) explain("<:", isSubType, this, that) + else isSubType(this, that, AnyDepth)) + } + } + + + + /** Is this type a subtype of that type in a pattern context? + * Any type arguments on the right hand side are replaced with + * fresh existentials, except for Arrays. + * + * See bug1434.scala for an example of code which would fail + * if only a <:< test were applied. + */ + def matchesPattern(that: Type): Boolean = { + (this <:< that) || ((this, that) match { + case (TypeRef(_, ArrayClass, List(arg1)), TypeRef(_, ArrayClass, List(arg2))) if arg2.typeSymbol.typeParams.nonEmpty => + arg1 matchesPattern arg2 + case (_, TypeRef(_, _, args)) => + val newtp = existentialAbstraction(args map (_.typeSymbol), that) + !(that =:= newtp) && (this <:< newtp) + case _ => + false + }) + } + + def stat_<:<(that: Type): Boolean = { + incCounter(subtypeCount) + val start = startTimer(subtypeNanos) + val result = + (this eq that) || + (if (explainSwitch) explain("<:", isSubType, this, that) + else isSubType(this, that, AnyDepth)) + stopTimer(subtypeNanos, start) + result + } + + /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long. + */ + def weak_<:<(that: Type): Boolean = { + incCounter(subtypeCount) + val start = startTimer(subtypeNanos) + val result = + ((this eq that) || + (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that) + else isWeakSubType(this, that))) + stopTimer(subtypeNanos, start) + result + } + + /** Is this type equivalent to that type? */ + def =:=(that: Type): Boolean = ( + (this eq that) || + (if (explainSwitch) explain("=", isSameType, this, that) + else isSameType(this, that)) + ); + + /** Does this type implement symbol `sym' with same or stronger type? + */ + def specializes(sym: Symbol): Boolean = + if (explainSwitch) explain("specializes", specializesSym, this, sym) + else specializesSym(this, sym) + + /** Is this type close enough to that type so that members + * with the two type would override each other? + * This means: + * - Either both types are polytypes with the same number of + * type parameters and their result types match after renaming + * corresponding type parameters + * - Or both types are (nullary) method types with equivalent type parameter types + * and matching result types + * - Or both types are equivalent + * - Or phase.erasedTypes is false and both types are neither method nor + * poly types. + */ + def matches(that: Type): Boolean = matchesType(this, that, !phase.erasedTypes) + + /** Same as matches, except that non-method types are always assumed to match. + */ + def looselyMatches(that: Type): Boolean = matchesType(this, that, true) + + /** The shortest sorted upwards closed array of types that contains + * this type as first element. + * + * A list or array of types ts is upwards closed if + * + * for all t in ts: + * for all typerefs p.s[args] such that t <: p.s[args] + * there exists a typeref p'.s[args'] in ts such that + * t <: p'.s['args] <: p.s[args], + * + * and + * + * for all singleton types p.s such that t <: p.s + * there exists a singleton type p'.s in ts such that + * t <: p'.s <: p.s + * + * Sorting is with respect to Symbol.isLess() on type symbols. + */ + def baseTypeSeq: BaseTypeSeq = baseTypeSingletonSeq(this) + + /** The maximum depth (@see maxDepth) + * of each type in the BaseTypeSeq of this type. + */ + def baseTypeSeqDepth: Int = 1 + + /** The list of all baseclasses of this type (including its own typeSymbol) + * in reverse linearization order, starting with the class itself and ending + * in class Any. + */ + def baseClasses: List[Symbol] = List() + + /** + * @param sym the class symbol + * @return the index of given class symbol in the BaseTypeSeq of this type, + * or -1 if no base type with given class symbol exists. + */ + def baseTypeIndex(sym: Symbol): Int = { + val bts = baseTypeSeq + var lo = 0 + var hi = bts.length - 1 + while (lo <= hi) { + val mid = (lo + hi) / 2 + val btssym = bts.typeSymbol(mid) + if (sym == btssym) return mid + else if (sym isLess btssym) hi = mid - 1 + else if (btssym isLess sym) lo = mid + 1 + else abort() + } + -1 + } + + /** If this is a poly- or methodtype, a copy with cloned type / value parameters + * owned by `owner'. Identity for all other types. + */ + def cloneInfo(owner: Symbol) = this + + /** Make sure this type is correct as the info of given owner; clone it if not. + */ + def atOwner(owner: Symbol) = this + + protected def objectPrefix = "object " + protected def packagePrefix = "package " + + def trimPrefix(str: String) = str stripPrefix objectPrefix stripPrefix packagePrefix + + /** The string representation of this type used as a prefix */ + def prefixString = trimPrefix(toString) + "#" + + /** Convert toString avoiding infinite recursions by cutting off + * after `maxTostringRecursions` recursion levels. Uses `safeToString` + * to produce a string on each level. + */ + override def toString: String = + if (tostringRecursions >= maxTostringRecursions) + "..." + else + try { + tostringRecursions += 1 + safeToString + } finally { + tostringRecursions -= 1 + } + + /** Method to be implemented in subclasses. + * Converts this type to a string in calling toString for its parts. + */ + def safeToString: String = super.toString + + /** The string representation of this type, with singletypes explained */ + def toLongString = { + val str = toString + if (str endsWith ".type") str + " (with underlying type " + widen + ")" + else str + } + + /** A test whether a type contains any unification type variables */ + def isGround: Boolean = this match { + case TypeVar(_, constr) => + constr.instValid && constr.inst.isGround + case TypeRef(pre, sym, args) => + sym.isPackageClass || pre.isGround && (args forall (_.isGround)) + case SingleType(pre, sym) => + sym.isPackageClass || pre.isGround + case ThisType(_) | NoPrefix | WildcardType | NoType | ErrorType | ConstantType(_) => + true + case _ => + typeVarToOriginMap(this) eq this + } + + /** If this is a symbol loader type, load and assign a new type to + * `sym'. + */ + def load(sym: Symbol) {} + + private def findDecl(name: Name, excludedFlags: Int): Symbol = { + var alts: List[Symbol] = List() + var sym: Symbol = NoSymbol + var e: ScopeEntry = decls.lookupEntry(name) + while (e ne null) { + if (!e.sym.hasFlag(excludedFlags)) { + if (sym == NoSymbol) sym = e.sym + else { + if (alts.isEmpty) alts = List(sym) + alts = e.sym :: alts + } + } + e = decls.lookupNextEntry(e) + } + if (alts.isEmpty) sym + else (baseClasses.head.newOverloaded(this, alts)) + } + + /** + * Find member(s) in this type. If several members matching criteria are found, they are + * returned in an OverloadedSymbol + * + * @param name The member's name, where nme.ANYNAME means `unspecified' + * @param excludedFlags Returned members do not have these flags + * @param requiredFlags Returned members do have these flags + * @param stableOnly If set, return only members that are types or stable values + */ + //TODO: use narrow only for modules? (correct? efficiency gain?) + def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean): Symbol = { + val suspension = TypeVar.Suspension + // if this type contains type variables, put them to sleep for a while -- don't just wipe them out by + // replacing them by the corresponding type parameter, as that messes up (e.g.) type variables in type refinements + // without this, the matchesType call would lead to type variables on both sides + // of a subtyping/equality judgement, which can lead to recursive types being constructed. + // See (t0851) for a situation where this happens. + if (!this.isGround) { + // PP: The foreach below was formerly expressed as: + // for(tv @ TypeVar(_, _) <- this) { suspension suspend tv } + // + // The tree checker failed this saying a TypeVar is required, but a (Type @unchecked) was found. + // This is a consequence of using a pattern match and variable binding + ticket #1503, which + // was addressed by weakening the type of bindings in pattern matches if they occur on the right. + // So I'm not quite sure why this works at all, as the checker is right that it is mistyped. + // For now I modified it as below, which achieves the same without error. + // + // make each type var in this type use its original type for comparisons instead of collecting constraints + this foreach { + case tv: TypeVar => suspension suspend tv + case _ => () + } + } + + incCounter(findMemberCount) + val start = startTimer(findMemberNanos) + + //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG + var members: Scope = null + var member: Symbol = NoSymbol + var excluded = excludedFlags | DEFERRED + var continue = true + var self: Type = null + var membertpe: Type = null + while (continue) { + continue = false + val bcs0 = baseClasses + var bcs = bcs0 + while (!bcs.isEmpty) { + val decls = bcs.head.info.decls + var entry = + if (name == nme.ANYNAME) decls.elems else decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + if (sym hasAllFlags requiredFlags) { + val excl = sym.getFlag(excluded) + if (excl == 0L && + (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + (bcs eq bcs0) || + !sym.isPrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (name.isTypeName || stableOnly && sym.isStable) { + stopTimer(findMemberNanos, start) + suspension.resumeAll + return sym + } else if (member == NoSymbol) { + member = sym + } else if (members eq null) { + if (member.name != sym.name || + !(member == sym || + member.owner != sym.owner && + !sym.isPrivate && { + if (self eq null) self = this.narrow + if (membertpe eq null) membertpe = self.memberType(member) + (membertpe matches self.memberType(sym)) + })) { + members = new Scope(List(member, sym)) + } + } else { + var prevEntry = members.lookupEntry(sym.name) + while ((prevEntry ne null) && + !(prevEntry.sym == sym || + prevEntry.sym.owner != sym.owner && + !sym.hasFlag(PRIVATE) && { + if (self eq null) self = this.narrow + self.memberType(prevEntry.sym) matches self.memberType(sym) + })) { + prevEntry = members lookupNextEntry prevEntry + } + if (prevEntry eq null) { + members enter sym + } + } + } else if (excl == DEFERRED.toLong) { + continue = true + } + } + entry = if (name == nme.ANYNAME) entry.next else decls lookupNextEntry entry + } // while (entry ne null) + // excluded = excluded | LOCAL + bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail + } // while (!bcs.isEmpty) + excluded = excludedFlags + } // while (continue) + stopTimer(findMemberNanos, start) + suspension.resumeAll + if (members eq null) { + if (member == NoSymbol) incCounter(noMemberCount) + member + } else { + incCounter(multMemberCount) + baseClasses.head.newOverloaded(this, members.toList) + } + } + + /** The existential skolems and existentially quantified variables which are free in this type */ + def existentialSkolems: List[Symbol] = { + var boundSyms: List[Symbol] = List() + var skolems: List[Symbol] = List() + for (t <- this) { + t match { + case ExistentialType(quantified, qtpe) => + boundSyms = boundSyms ::: quantified + case TypeRef(_, sym, _) => + if ((sym hasFlag EXISTENTIAL) && !(boundSyms contains sym) && !(skolems contains sym)) + skolems = sym :: skolems + case _ => + } + } + skolems + } + + /** Return the annotations on this type. */ + def annotations: List[AnnotationInfo] = Nil + + /** Test for the presence of an annotation */ + def hasAnnotation(clazz: Symbol) = annotations exists { _.atp.typeSymbol == clazz } + + /** Add an annotation to this type */ + def withAnnotation(annot: AnnotationInfo) = withAnnotations(List(annot)) + + /** Add a number of annotations to this type */ + def withAnnotations(annots: List[AnnotationInfo]): Type = + annots match { + case Nil => this + case _ => AnnotatedType(annots, this, NoSymbol) + } + + /** Remove any annotations from this type */ + def withoutAnnotations = this + + /** Remove any annotations from this type and from any + * types embedded in this type. */ + def stripAnnotations = StripAnnotationsMap(this) + + /** Set the self symbol of an annotated type, or do nothing + * otherwise. */ + def withSelfsym(sym: Symbol) = this + + /** The selfsym of an annotated type, or NoSymbol of anything else */ + def selfsym: Symbol = NoSymbol + + /** The kind of this type; used for debugging */ + def kind: String = "unknown type of class "+getClass() + } + +// Subclasses ------------------------------------------------------------ + + trait UniqueType { + override lazy val hashCode: Int = super.hashCode() + } + + /** A base class for types that defer some operations + * to their immediate supertype. + */ + abstract class SubType extends Type { + def supertype: Type + override def parents: List[Type] = supertype.parents + override def decls: Scope = supertype.decls + override def baseType(clazz: Symbol): Type = supertype.baseType(clazz) + override def baseTypeSeq: BaseTypeSeq = supertype.baseTypeSeq + override def baseTypeSeqDepth: Int = supertype.baseTypeSeqDepth + override def baseClasses: List[Symbol] = supertype.baseClasses + override def isNotNull = supertype.isNotNull + } + + case class NotNullType(override val underlying: Type) extends SubType with RewrappingTypeProxy { + def supertype = underlying + protected def rewrap(newtp: Type): Type = NotNullType(newtp) + override def isNotNull: Boolean = true + override def notNull = this + override def deconst: Type = underlying //todo: needed? + override def safeToString: String = underlying.toString + " with NotNull" + override def kind = "NotNullType" + } + + /** A base class for types that represent a single value + * (single-types and this-types). + */ + abstract class SingletonType extends SubType with SimpleTypeProxy { + def supertype = underlying + override def isTrivial = false + override def isStable = true + override def isVolatile = underlying.isVolatile + override def widen: Type = underlying.widen + override def baseTypeSeq: BaseTypeSeq = { + incCounter(singletonBaseTypeSeqCount) + underlying.baseTypeSeq prepend this + } + override def isHigherKinded = false // singleton type classifies objects, thus must be kind * + override def safeToString: String = prefixString + "type" +/* + override def typeOfThis: Type = typeSymbol.typeOfThis + override def bounds: TypeBounds = TypeBounds(this, this) + override def prefix: Type = NoType + override def typeArgs: List[Type] = List() + override def typeParams: List[Symbol] = List() +*/ + } + + /** An object representing an erroneous type */ + case object ErrorType extends Type { + // todo see whether we can do without + override def isError: Boolean = true + override def decls: Scope = new ErrorScope(NoSymbol) + override def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean): Symbol = { + var sym = decls lookup name + if (sym == NoSymbol) { + sym = NoSymbol.newErrorSymbol(name) + decls enter sym + } + sym + } + override def baseType(clazz: Symbol): Type = this + override def safeToString: String = "<error>" + override def narrow: Type = this + // override def isNullable: Boolean = true + override def kind = "ErrorType" + } + + /** An object representing an unknown type, used during type inference. + * If you see WildcardType outside of inference it is almost certainly a bug. + */ + case object WildcardType extends Type { + override def isWildcard = true + override def safeToString: String = "?" + // override def isNullable: Boolean = true + override def kind = "WildcardType" + } + + case class BoundedWildcardType(override val bounds: TypeBounds) extends Type { + override def isWildcard = true + override def safeToString: String = "?" + bounds + override def kind = "BoundedWildcardType" + } + + /** An object representing a non-existing type */ + case object NoType extends Type { + override def isTrivial: Boolean = true + override def safeToString: String = "<notype>" + // override def isNullable: Boolean = true + override def kind = "NoType" + } + + /** An object representing a non-existing prefix */ + case object NoPrefix extends Type { + override def isTrivial: Boolean = true + override def isStable: Boolean = true + override def prefixString = "" + override def safeToString: String = "<noprefix>" + // override def isNullable: Boolean = true + override def kind = "NoPrefixType" + } + + /** A class for this-types of the form <sym>.this.type + */ + abstract case class ThisType(sym: Symbol) extends SingletonType { + //assert(sym.isClass && !sym.isModuleClass || sym.isRoot, sym) + override def isTrivial: Boolean = sym.isPackageClass + override def isNotNull = true + override def typeSymbol = sym + override def underlying: Type = sym.typeOfThis + override def isVolatile = false + override def isHigherKinded = sym.isRefinementClass && underlying.isHigherKinded + override def prefixString = + if (settings.debug.value) sym.nameString + ".this." + else if (sym.isAnonOrRefinementClass) "this." + else if (sym.printWithoutPrefix) "" + else if (sym.isModuleClass) sym.fullName + "." + else sym.nameString + ".this." + override def safeToString: String = + if (sym.isRoot) "<root>" + else if (sym.isEmptyPackageClass) "<empty>" + else super.safeToString + override def narrow: Type = this + override def kind = "ThisType" + } + + object ThisType { + def apply(sym: Symbol): Type = + if (!phase.erasedTypes) unique(new ThisType(sym) with UniqueType) + else if (sym.isImplClass) sym.typeOfThis + else sym.tpe + } + + /** A class for singleton types of the form <prefix>.<sym.name>.type. + * Cannot be created directly; one should always use + * `singleType' for creation. + */ + case class SingleType(pre: Type, sym: Symbol) extends SingletonType { + override val isTrivial: Boolean = pre.isTrivial + // override def isNullable = underlying.isNullable + override def isNotNull = underlying.isNotNull + private var underlyingCache: Type = NoType + private var underlyingPeriod = NoPeriod + override def underlying: Type = { + val period = underlyingPeriod + if (period != currentPeriod) { + underlyingPeriod = currentPeriod + if (!isValid(period)) { + underlyingCache = pre.memberType(sym).resultType; + assert(underlyingCache ne this, this) + } + } + underlyingCache + } + + // more precise conceptually, but causes cyclic errors: (paramss exists (_ contains sym)) + override def isImmediatelyDependent = (sym ne NoSymbol) && (sym.owner.isMethod && sym.isValueParameter) + + override def isVolatile : Boolean = underlying.isVolatile && !sym.isStable +/* + override def narrow: Type = { + if (phase.erasedTypes) this + else { + val thissym = refinedType(List(this), sym.owner, EmptyScope).typeSymbol + if (sym.owner != NoSymbol) { + //Console.println("narrowing module " + sym + thissym.owner); + thissym.typeOfThis = this + } + thissym.thisType + } + } +*/ + override def narrow: Type = this + + override def termSymbol = sym + override def prefix: Type = pre + override def prefixString: String = + if ((sym.isEmptyPackage || sym.isInterpreterWrapper || sym.isPredefModule || sym.isScalaPackage) && !settings.debug.value) "" + else pre.prefixString + sym.nameString + "." + override def kind = "SingleType" + } + + abstract case class SuperType(thistpe: Type, supertpe: Type) extends SingletonType { + override val isTrivial: Boolean = thistpe.isTrivial && supertpe.isTrivial + override def isNotNull = true; + override def typeSymbol = thistpe.typeSymbol + override def underlying = supertpe + override def prefix: Type = supertpe.prefix + override def prefixString = thistpe.prefixString.replaceAll("""this\.$""", "super.") + override def narrow: Type = thistpe.narrow + override def kind = "SuperType" + } + + object SuperType { + def apply(thistp: Type, supertp: Type): Type = + if (phase.erasedTypes) supertp + else unique(new SuperType(thistp, supertp) with UniqueType) + } + + /** A class for the bounds of abstract types and type parameters + */ + abstract case class TypeBounds(lo: Type, hi: Type) extends SubType { + def supertype = hi + override val isTrivial: Boolean = lo.isTrivial && hi.isTrivial + override def bounds: TypeBounds = this + def containsType(that: Type) = that match { + case TypeBounds(_, _) => that <:< this + case _ => lo <:< that && that <:< hi + } + // override def isNullable: Boolean = NullClass.tpe <:< lo; + override def safeToString = ">: " + lo + " <: " + hi + override def kind = "TypeBoundsType" + } + + object TypeBounds { + def empty: TypeBounds = apply(NothingClass.tpe, AnyClass.tpe) + def upper(hi: Type): TypeBounds = apply(NothingClass.tpe, hi) + def lower(lo: Type): TypeBounds = apply(lo, AnyClass.tpe) + + def apply(lo: Type, hi: Type): TypeBounds = + unique(new TypeBounds(lo, hi) with UniqueType) + } + + /** A common base class for intersection types and class types + */ + abstract class CompoundType extends Type { + + var baseTypeSeqCache: BaseTypeSeq = _ + private var baseTypeSeqPeriod = NoPeriod + private var baseClassesCache: List[Symbol] = _ + private var baseClassesPeriod = NoPeriod + + override def baseTypeSeq: BaseTypeSeq = { + val period = baseTypeSeqPeriod; + if (period != currentPeriod) { // no caching in IDE + baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + if (parents.exists(_.exists(_.isInstanceOf[TypeVar]))) { + // rename type vars to fresh type params, take base type sequence of + // resulting type, and rename back all the entries in that sequence + var tvs = Set[TypeVar]() + for (p <- parents) + for (t <- p) t match { + case tv: TypeVar => tvs += tv + case _ => + } + val varToParamMap: Map[Type, Symbol] = tvs map (tv => tv -> tv.origin.typeSymbol.cloneSymbol) toMap + val paramToVarMap = varToParamMap map (_.swap) + val varToParam = new TypeMap { + def apply(tp: Type) = varToParamMap get tp match { + case Some(sym) => sym.tpe + case _ => mapOver(tp) + } + } + val paramToVar = new TypeMap { + def apply(tp: Type) = tp match { + case TypeRef(_, tsym, _) if paramToVarMap.isDefinedAt(tsym) => paramToVarMap(tsym) + case _ => mapOver(tp) + } + } + val bts = copyRefinedType(this.asInstanceOf[RefinedType], parents map varToParam, varToParam mapOver decls).baseTypeSeq + baseTypeSeqCache = bts lateMap paramToVar + } else { + incCounter(compoundBaseTypeSeqCount) + baseTypeSeqCache = undetBaseTypeSeq + baseTypeSeqCache = if (typeSymbol.isRefinementClass) + memo(compoundBaseTypeSeq(this))(_.baseTypeSeq updateHead typeSymbol.tpe) + else + compoundBaseTypeSeq(this) + // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors + // when compiling with + // scalac scala.collection.IterableViewLike.scala scala.collection.IterableLike.scala + // I have not yet figured out precisely why this is the case. + // My current assumption is that taking memos forces baseTypeSeqs to be computed + // at stale types (i.e. the underlying typeSymbol has already another type). + // I do not yet see precisely why this would cause a problem, but it looks + // fishy in any case. + } + } + //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + } + if (baseTypeSeqCache eq undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + typeSymbol) + baseTypeSeqCache + } + + override def baseTypeSeqDepth: Int = baseTypeSeq.maxDepth + + override def baseClasses: List[Symbol] = { + def computeBaseClasses: List[Symbol] = + if (parents.isEmpty) List(typeSymbol) + else { + //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = parents.head + var mixins = parents.tail + val sbcs = superclazz.baseClasses + var bcs = sbcs + def isNew(clazz: Symbol): Boolean = ( + superclazz.baseTypeIndex(clazz) < 0 && + { var p = bcs; + while ((p ne sbcs) && (p.head != clazz)) p = p.tail; + p eq sbcs + } + ); + while (!mixins.isEmpty) { + def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = + if (mbcs.isEmpty) bcs + else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) + else addMixinBaseClasses(mbcs.tail); + bcs = addMixinBaseClasses(mixins.head.baseClasses) + mixins = mixins.tail + } + typeSymbol :: bcs + } + val period = baseClassesPeriod + if (period != currentPeriod) { + baseClassesPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + baseClassesCache = null + baseClassesCache = memo(computeBaseClasses)(typeSymbol :: _.baseClasses.tail) + } + } + if (baseClassesCache eq null) + throw new TypeError("illegal cyclic reference involving " + typeSymbol) + baseClassesCache + } + + /** The slightly less idiomatic use of Options is due to + * performance considerations. A version using for comprehensions + * might be too slow (this is deemed a hotspot of the type checker). + * + * See with Martin before changing this method. + */ + def memo[A](op1: => A)(op2: Type => A): A = { + def updateCache(): A = { + intersectionWitness(parents) = new WeakReference(this) + op1 + } + + intersectionWitness get parents match { + case Some(ref) => + ref.get match { + case Some(w) => if (w eq this) op1 else op2(w) + case None => updateCache() + } + case None => updateCache() + } + + } + + override def baseType(sym: Symbol): Type = { + val index = baseTypeIndex(sym) + if (index >= 0) baseTypeSeq(index) else NoType + } + + override def narrow: Type = typeSymbol.thisType + override def isNotNull: Boolean = parents exists (_.isNotNull) + + override def isStructuralRefinement: Boolean = + typeSymbol.isAnonOrRefinementClass && + (decls.toList exists { entry => !entry.isConstructor && entry.allOverriddenSymbols.isEmpty }) + + // override def isNullable: Boolean = + // parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType); + + override def safeToString: String = + parents.mkString(" with ") + + (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + decls.mkString("{", "; ", "}") else "") + } + + /** A class representing intersection types with refinements of the form + * `<parents_0> with ... with <parents_n> { decls }' + * Cannot be created directly; + * one should always use `refinedType' for creation. + */ + case class RefinedType(override val parents: List[Type], + override val decls: Scope) extends CompoundType { + + override def isHigherKinded = ( + parents.nonEmpty && + (parents forall (_.isHigherKinded)) && + !phase.erasedTypes // @MO to AM: please check this class! + ) + + override def typeParams = + if (isHigherKinded) parents.head.typeParams + else super.typeParams + + //@M may result in an invalid type (references to higher-order args become dangling ) + override def typeConstructor = + copyRefinedType(this, parents map (_.typeConstructor), decls) + + private def dummyArgs = typeParams map (_.typeConstructor) + + /* MO to AM: This is probably not correct + * If they are several higher-kinded parents with different bounds we need + * to take the intersection of their bounds + */ + override def normalize = { + if (isHigherKinded) { + typeFun( + typeParams, + RefinedType( + parents map { + case TypeRef(pre, sym, List()) => TypeRef(pre, sym, dummyArgs) + case p => p + }, + decls, + typeSymbol)) + } + else super.normalize + } + + /** A refined type P1 with ... with Pn { decls } is volatile if + * one of the parent types Pi is an abstract type, and + * either i > 1, or decls or a following parent Pj, j > 1, contributes + * an abstract member. + * A type contributes an abstract member if it has an abstract member which + * is also a member of the whole refined type. A scope `decls' contributes + * an abstract member if it has an abstract definition which is also + * a member of the whole type. + */ + override def isVolatile = { + def isVisible(m: Symbol) = + this.nonPrivateMember(m.name).alternatives contains m + def contributesAbstractMembers(p: Type) = + p.deferredMembers exists isVisible + + ((parents exists (_.isVolatile)) + || + (parents dropWhile (! _.typeSymbol.isAbstractType) match { + case ps @ (_ :: ps1) => + (ps ne parents) || + (ps1 exists contributesAbstractMembers) || + (decls.iterator exists (m => m.isDeferred && isVisible(m))) + case _ => + false + })) + } + + override def kind = "RefinedType" + } + + object RefinedType { + def apply(parents: List[Type], decls: Scope, clazz: Symbol) = + new RefinedType(parents, decls) { override def typeSymbol = clazz } + } + + /** A class representing a class info + */ + case class ClassInfoType( + override val parents: List[Type], + override val decls: Scope, + override val typeSymbol: Symbol) extends CompoundType + { + + /** refs indices */ + private final val NonExpansive = 0 + private final val Expansive = 1 + + /** initialization states */ + private final val UnInitialized = 0 + private final val Initializing = 1 + private final val Initialized = 2 + + private type RefMap = Map[Symbol, immutable.Set[Symbol]] + + /** All type parameters reachable from given type parameter + * by a path which contains at least one expansive reference. + * @See Kennedy, Pierce: On Decidability of Nominal Subtyping with Variance + */ + def expansiveRefs(tparam: Symbol) = { + if (state == UnInitialized) { + computeRefs() + while (state != Initialized) propagate() + } + getRefs(Expansive, tparam) + } + + /* The rest of this class is auxiliary code for `expansiveRefs' + */ + + /** The type parameters which are referenced type parameters of this class. + * Two entries: refs(0): Non-expansive references + * refs(1): Expansive references + */ + private var refs: Array[RefMap] = _ + + /** The initialization state of the class: UnInialized --> Initializing --> Initialized + */ + private var state = UnInitialized + + /** Get references for given type parameter + * @param which in {NonExpansive, Expansive} + * @param from The type parameter from which references originate. + */ + private def getRefs(which: Int, from: Symbol): Set[Symbol] = refs(which) get from match { + case Some(set) => set + case none => Set() + } + + /** Augment existing refs map with reference <pre>from -> to</pre> + * @param which <- {NonExpansive, Expansive} + */ + private def addRef(which: Int, from: Symbol, to: Symbol) { + refs(which) = refs(which) + (from -> (getRefs(which, from) + to)) + } + + /** Augment existing refs map with references <pre>from -> sym</pre>, for + * all elements <pre>sym</pre> of set `to'. + * @param which <- {NonExpansive, Expansive} + */ + private def addRefs(which: Int, from: Symbol, to: Set[Symbol]) { + refs(which) = refs(which) + (from -> (getRefs(which, from) ++ to)) + } + + /** The ClassInfoType which belongs to the class containing given type parameter + */ + private def classInfo(tparam: Symbol): ClassInfoType = + tparam.owner.info.resultType match { + case ci: ClassInfoType => ci + case _ => classInfo(ObjectClass) // something's wrong; fall back to safe value + // (this can happen only for erroneous programs). + } + + /** Compute initial (one-step) references and set state to `Initializing'. + */ + private def computeRefs() { + refs = Array(Map(), Map()) + for (tparam <- typeSymbol.typeParams) { + val enterRefs = new TypeMap { + def apply(tp: Type): Type = { + tp match { + case TypeRef(_, sym, args) => + for ((tparam1, arg) <- sym.info.typeParams zip args) + if (arg contains tparam) { + addRef(NonExpansive, tparam, tparam1) + if (arg.typeSymbol != tparam) addRef(Expansive, tparam, tparam1) + } + case _ => + } + mapOver(tp) + } + } + for (p <- parents) enterRefs(p) + } + state = Initializing + } + + /** Propagate to form transitive closure. + * Set state to Initialized if no change resulted from propagation. + * @return true iff there as a change in last iteration + */ + private def propagate(): Boolean = { + if (state == UnInitialized) computeRefs() + //Console.println("Propagate "+symbol+", initial expansive = "+refs(Expansive)+", nonexpansive = "+refs(NonExpansive))//DEBUG + val lastRefs = Array(refs(0), refs(1)) + state = Initialized + var change = false + for ((from, targets) <- refs(NonExpansive).iterator) + for (target <- targets) { + var thatInfo = classInfo(target) + if (thatInfo.state != Initialized) + change = change | thatInfo.propagate() + addRefs(NonExpansive, from, thatInfo.getRefs(NonExpansive, target)) + addRefs(Expansive, from, thatInfo.getRefs(Expansive, target)) + } + for ((from, targets) <- refs(Expansive).iterator) + for (target <- targets) { + var thatInfo = classInfo(target) + if (thatInfo.state != Initialized) + change = change | thatInfo.propagate() + addRefs(Expansive, from, thatInfo.getRefs(NonExpansive, target)) + } + change = change || refs(0) != lastRefs(0) || refs(1) != lastRefs(1) + if (change) state = Initializing + //else Console.println("Propagate "+symbol+", final expansive = "+refs(Expansive)+", nonexpansive = "+refs(NonExpansive))//DEBUG + change + } + + // override def isNullable: Boolean = + // symbol == AnyClass || + // symbol != NothingClass && (symbol isSubClass ObjectClass) && !(symbol isSubClass NonNullClass); + + // override def isNonNull: Boolean = symbol == NonNullClass || super.isNonNull; + override def kind = "ClassInfoType" + } + + class PackageClassInfoType(decls: Scope, clazz: Symbol) + extends ClassInfoType(List(), decls, clazz) + + /** A class representing a constant type. + * + * @param value ... + */ + abstract case class ConstantType(value: Constant) extends SingletonType { + override def underlying: Type = value.tpe + assert(underlying.typeSymbol != UnitClass) + override def isTrivial: Boolean = true + override def isNotNull = value.value != null + override def deconst: Type = underlying + override def safeToString: String = + underlying.toString + "(" + value.escapedStringValue + ")" + // override def isNullable: Boolean = value.value eq null + // override def isNonNull: Boolean = value.value ne null + override def kind = "ConstantType" + } + + object ConstantType { + def apply(value: Constant): ConstantType = { + class UniqueConstantType extends ConstantType(value) with UniqueType { + /** Save the type of 'value'. For Java enums, it depends on finding the linked class, + * which might not be found after 'flatten'. */ + private lazy val _tpe: Type = value.tpe + override def underlying: Type = _tpe + } + unique(new UniqueConstantType) + } + } + + private var volatileRecursions: Int = 0 + private val pendingVolatiles = new mutable.HashSet[Symbol] + + /** A class for named types of the form + * `<prefix>.<sym.name>[args]' + * Cannot be created directly; one should always use `typeRef' + * for creation. (@M: Otherwise hashing breaks) + * + * @M: a higher-kinded type is represented as a TypeRef with sym.info.typeParams.nonEmpty, but args.isEmpty + * @param pre ... + * @param sym ... + * @param args ... + */ + abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends Type { +// assert(!sym.isAbstractType || pre.isStable || pre.isError) +// assert(!pre.isInstanceOf[ClassInfoType], this) +// assert(!(sym hasFlag (PARAM | EXISTENTIAL)) || pre == NoPrefix, this) +// assert(args.isEmpty || !sym.info.typeParams.isEmpty, this) +// assert(args.isEmpty || ((sym ne AnyClass) && (sym ne NothingClass)) + + private val parentsCache = new ListOfTypesCache { + @inline final def calculate() = thisInfo.parents map transform + } + private var baseTypeSeqCache: BaseTypeSeq = _ + private var baseTypeSeqPeriod = NoPeriod + + override def isStable: Boolean = { + sym == NothingClass || + sym == SingletonClass || + sym.isAliasType && normalize.isStable || + sym.isAbstractType && (bounds.hi.typeSymbol isSubClass SingletonClass) + } + + override def isVolatile: Boolean = { + sym.isAliasType && normalize.isVolatile || + sym.isAbstractType && { + // need to be careful not to fall into an infinite recursion here + // because volatile checking is done before all cycles are detected. + // the case to avoid is an abstract type directly or + // indirectly upper-bounded by itself. See #2918 + try { + volatileRecursions += 1 + if (volatileRecursions < LogVolatileThreshold) + bounds.hi.isVolatile + else if (pendingVolatiles(sym)) + true // we can return true here, because a cycle will be detected + // here afterwards and an error will result anyway. + else + try { + pendingVolatiles += sym + bounds.hi.isVolatile + } finally { + pendingVolatiles -= sym + } + } finally { + volatileRecursions -= 1 + } + } + } + + override lazy val isTrivial: Boolean = + !sym.isTypeParameter && pre.isTrivial && args.forall(_.isTrivial) + + override def isNotNull = + sym.isModuleClass || sym == NothingClass || isValueClass(sym) || super.isNotNull + + // @M: propagate actual type params (args) to `tp', by replacing formal type parameters with actual ones + // if tp is higher kinded, the "actual" type arguments are types that simply reference the corresponding type parameters (unbound type variables) + def transform(tp: Type): Type = { + val res = tp.asSeenFrom(pre, sym.owner) + if (sym.typeParams.isEmpty || (args exists (_.isError)) || isRaw(sym, args)/*#2266/2305*/) res + else res.instantiateTypeParams(sym.typeParams, typeArgsOrDummies) + } + + //@M! use appliedType on the polytype that represents the bounds (or if aliastype, the rhs) + def transformInfo(tp: Type): Type = appliedType(tp.asSeenFrom(pre, sym.owner), typeArgsOrDummies) + + def thisInfo = + if (sym.isAliasType) normalize + else if (sym.isNonClassType) transformInfo(sym.info) + else sym.info + + def relativeInfo = if (sym.isNonClassType) transformInfo(pre.memberInfo(sym)) else pre.memberInfo(sym) + + override def typeSymbol = if (sym.isAliasType) normalize.typeSymbol else sym + override def termSymbol = if (sym.isAliasType) normalize.termSymbol else super.termSymbol + override def typeSymbolDirect = sym + override def termSymbolDirect = super.termSymbol + +/* @MAT +whenever you see `tp.typeSymbol.isXXXX' and then act on tp based on that predicate, you're on thin ice, +as `typeSymbol' (and `prefix') automatically normalize, but the other inspectors don't. +In other words, even if `tp.normalize.sym.isXXX' is true, `tp.sym.isXXX' may be false (if sym were a public method to access the non-normalized typeSymbol)... + +In retrospect, I think `tp.typeSymbol.isXXX' or (worse) `tp.typeSymbol==XXX' should be replaced by `val tp = tp0.asXXX'. +A type's typeSymbol should never be inspected directly. +*/ + + override def bounds: TypeBounds = + if (sym.isAbstractType) thisInfo.bounds // transform(thisInfo.bounds).asInstanceOf[TypeBounds] // ??? seems to be doing asSeenFrom twice + else super.bounds + + override def parents: List[Type] = parentsCache.get() + override def typeOfThis = transform(sym.typeOfThis) + +/* + override def narrow = + if (sym.isModuleClass) transform(sym.thisType) + else if (sym.isAliasType) normalize.narrow + else super.narrow +*/ + override def narrow = + if (sym.isModuleClass) singleType(pre, sym.sourceModule) + else if (sym.isAliasType) normalize.narrow + else super.narrow + + override def prefix: Type = + if (sym.isAliasType) normalize.prefix + else pre + + override def typeArgs: List[Type] = args + private def typeArgsOrDummies = if (!isHigherKinded) args else dummyArgs + + // @MAT was typeSymbol.unsafeTypeParams, but typeSymbol normalizes now + private def typeParamsDirect = + if (isDefinitionsInitialized) sym.typeParams + else sym.unsafeTypeParams + + // placeholders derived from type params + private def dummyArgs = typeParamsDirect map (_.typeConstructor) //@M must be .typeConstructor + + // (!result.isEmpty) IFF isHigherKinded + override def typeParams: List[Symbol] = if (isHigherKinded) typeParamsDirect else List() + + override def typeConstructor = TypeRef(pre, sym, Nil) + // note: does not go through typeRef. There's no need to because neither `pre' nor `sym' changes. + // And there's a performance advantage to call TypeRef directly. + + + // a reference (in a Scala program) to a type that has type parameters, but where the reference does not include type arguments + // note that it doesn't matter whether the symbol refers to a java or scala symbol, + // it does matter whether it occurs in java or scala code + // typerefs w/o type params that occur in java signatures/code are considered raw types, and are represented as existential types + override def isHigherKinded = args.isEmpty && typeParamsDirect.nonEmpty + + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = + if (isHigherKinded) { + val substTps = formals.intersect(typeParams) + + if (sameLength(substTps, typeParams)) + typeRef(pre, sym, actuals) + else if (sameLength(formals, actuals)) // partial application (needed in infer when bunching type arguments from classes and methods together) + typeRef(pre, sym, dummyArgs).subst(formals, actuals) + else ErrorType + } + else + super.instantiateTypeParams(formals, actuals) + + + private var normalized: Type = null + + @inline private def betaReduce: Type = { + assert(sameLength(sym.info.typeParams, typeArgs), this) + // isHKSubType0 introduces synthetic type params so that betaReduce can first apply sym.info to typeArgs before calling asSeenFrom + // asSeenFrom then skips synthetic type params, which are used to reduce HO subtyping to first-order subtyping, but which can't be instantiated from the given prefix and class + // appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner) // this crashes pos/depmet_implicit_tpbetareduce.scala + transform(sym.info.resultType) + } + + // @M: initialize (by sym.info call) needed (see test/files/pos/ticket0137.scala) + @inline private def etaExpand: Type = { + val tpars = sym.info.typeParams // must go through sym.info for typeParams to initialise symbol + typeFunAnon(tpars, typeRef(pre, sym, tpars map (_.tpeHK))) // todo: also beta-reduce? + } + + override def dealias: Type = + if (sym.isAliasType && sameLength(sym.info.typeParams, args)) { + betaReduce.dealias + } else this + + def normalize0: Type = + if (pre eq WildcardType) WildcardType // arises when argument-dependent types are approximated (see def depoly in implicits) + else if (isHigherKinded) etaExpand // eta-expand, subtyping relies on eta-expansion of higher-kinded types + else if (sym.isAliasType && sameLength(sym.info.typeParams, args)) + betaReduce.normalize // beta-reduce, but don't do partial application -- cycles have been checked in typeRef + else if (sym.isRefinementClass) + sym.info.normalize // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers + else { + if(sym.isAliasType) ErrorType //println("!!error: "+(pre, sym, sym.info, sym.info.typeParams, args)) + else super.normalize + } + + // TODO: test case that is compiled in a specific order and in different runs + override def normalize: Type = { + if (phase.erasedTypes) normalize0 + else { + if (normalized == null) + normalized = normalize0 + + normalized + } + } + + override def decls: Scope = { + sym.info match { + case TypeRef(_, sym1, _) => + assert(sym1 != sym, this) // @MAT was != typeSymbol + case _ => + } + thisInfo.decls + } + + override def baseType(clazz: Symbol): Type = + if (sym == clazz) this + else if (sym.isClass) transform(sym.info.baseType(clazz)) + else + try { + basetypeRecursions += 1 + if (basetypeRecursions < LogPendingBaseTypesThreshold) + relativeInfo.baseType(clazz) + else if (pendingBaseTypes contains this) + if (clazz == AnyClass) clazz.tpe else NoType + else + try { + pendingBaseTypes += this + relativeInfo.baseType(clazz) + } finally { + pendingBaseTypes -= this + } + } finally { + basetypeRecursions -= 1 + } + + override def baseTypeSeq: BaseTypeSeq = { + val period = baseTypeSeqPeriod + if (period != currentPeriod) { + baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + incCounter(typerefBaseTypeSeqCount) + baseTypeSeqCache = undetBaseTypeSeq + baseTypeSeqCache = + if (sym.isAbstractType) transform(bounds.hi).baseTypeSeq prepend this + else sym.info.baseTypeSeq map transform + } + } + if (baseTypeSeqCache == undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + sym) + baseTypeSeqCache + } + + override def baseTypeSeqDepth: Int = baseTypeSeq.maxDepth + + override def baseClasses: List[Symbol] = thisInfo.baseClasses + + // override def isNullable: Boolean = sym.info.isNullable + + override def safeToString: String = { + if (!settings.debug.value) { + this match { + case TypeRef(_, RepeatedParamClass, arg :: _) => return arg + "*" + case TypeRef(_, ByNameParamClass, arg :: _) => return "=> " + arg + case _ => + if (isFunctionType(this)) + return normalize.typeArgs.init.mkString("(", ", ", ")") + " => " + normalize.typeArgs.last + else if (isTupleTypeOrSubtype(this)) + return normalize.typeArgs.mkString("(", ", ", if (hasLength(normalize.typeArgs, 1)) ",)" else ")") + else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic)) { + val normed = normalize; + if (normed ne this) return normed.toString + } + } + } + val monopart = + if (!settings.debug.value && + (shorthands contains sym.fullName) && + (sym.ownerChain forall (_.isClass))) // ensure that symbol is not a local copy with a name coincidence + sym.name.toString + else + pre.prefixString + sym.nameString + + var str = monopart + (if (args.isEmpty) "" else args.mkString("[", ",", "]")) + if (sym.isPackageClass) + packagePrefix + str + else if (sym.isModuleClass) + objectPrefix + str + else if (sym.isAnonymousClass && sym.isInitialized && !settings.debug.value && !phase.erasedTypes) + thisInfo.parents.mkString(" with ") + { + if (sym.isStructuralRefinement) + ((decls.toList filter { entry => + !entry.isConstructor && entry.allOverriddenSymbols.isEmpty && !entry.isPrivate + }) map { entry => entry.defString }).mkString("{", "; ", "}") + else + "" + } + else if (sym.isRefinementClass && sym.isInitialized) + thisInfo.toString + else str + } + + override def prefixString = "" + ( + if (settings.debug.value) + super.prefixString + else if (sym.printWithoutPrefix) + "" + else if (sym.isPackageClass) + sym.fullName + "." + else if (isStable && nme.isSingletonName(sym.name)) + nme.dropSingletonName(sym.name) + "." + else + super.prefixString + ) + override def kind = "TypeRef" + } + + object TypeRef { + def apply(pre: Type, sym: Symbol, args: List[Type]): Type = { + unique(new TypeRef(pre, sym, args) with UniqueType) + } + } + + /** A class representing a method type with parameters. + * Note that a parameterless method is represented by a NullaryMethodType: + * + * def m(): Int MethodType(Nil, Int) + * def m: Int NullaryMethodType(Int) + */ + case class MethodType(override val params: List[Symbol], + override val resultType: Type) extends Type { + override def isTrivial: Boolean = isTrivial0 + private lazy val isTrivial0 = + resultType.isTrivial && params.forall{p => p.tpe.isTrivial && ( + !settings.YdepMethTpes.value || !(params.exists(_.tpe.contains(p)) || resultType.contains(p))) + } + + def isImplicit = params.nonEmpty && params.head.isImplicit + def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized? + + //assert(paramTypes forall (pt => !pt.typeSymbol.isImplClass))//DEBUG + override def paramSectionCount: Int = resultType.paramSectionCount + 1 + + override def paramss: List[List[Symbol]] = params :: resultType.paramss + + override def paramTypes = params map (_.tpe) + + override def boundSyms = immutable.Set[Symbol](params ++ resultType.boundSyms: _*) + + // AM to TR: #dropNonContraintAnnotations + // this is needed for plugins to work correctly, only TypeConstraint annotations are supposed to be carried over + // TODO: this should probably be handled in a more structured way in adapt -- remove this map in resultType and watch the continuations tests fail + object dropNonContraintAnnotations extends TypeMap { + override val dropNonConstraintAnnotations = true + def apply(x: Type) = mapOver(x) + } + + override def resultType(actuals: List[Type]) = + if (isTrivial) dropNonContraintAnnotations(resultType) + else { + if (sameLength(actuals, params)) { + val idm = new InstantiateDependentMap(params, actuals) + val res = idm(resultType) + // println("resultTypeDep "+(params, actuals, resultType, idm.existentialsNeeded, "\n= "+ res)) + existentialAbstraction(idm.existentialsNeeded, res) + } else { + // Thread.dumpStack() + // println("resultType "+(params, actuals, resultType)) + if (phase.erasedTypes) resultType + else existentialAbstraction(params, resultType) + } + } + + // implicit args can only be depended on in result type: TODO this may be generalised so that the only constraint is dependencies are acyclic + def approximate: MethodType = MethodType(params, resultApprox) + + override def finalResultType: Type = resultType.finalResultType + + override def safeToString = paramString(this) + resultType + + override def cloneInfo(owner: Symbol) = { + val vparams = cloneSymbols(params, owner) + copyMethodType(this, vparams, resultType.substSym(params, vparams).cloneInfo(owner)) + } + + override def atOwner(owner: Symbol) = + if ((params exists (_.owner != owner)) || (resultType.atOwner(owner) ne resultType)) + cloneInfo(owner) + else + this + + override def kind = "MethodType" + } + + class JavaMethodType(ps: List[Symbol], rt: Type) extends MethodType(ps, rt) { + override def isJava = true + } + + case class NullaryMethodType(override val resultType: Type) extends Type { + // AM to TR: #dropNonContraintAnnotations + // change isTrivial to the commented version and watch continuations-run/t3225.scala fail + // isTrivial implies asSeenFrom is bypassed, since it's supposed to be the identity map + // it's not really the identity due to dropNonContraintAnnotations + override def isTrivial: Boolean = false //resultType.isTrivial -- `false` to make continuations plugin work (so that asSeenFromMap drops non-constrain annotations even when type doesn't change otherwise) + override def prefix: Type = resultType.prefix + override def narrow: Type = resultType.narrow + override def finalResultType: Type = resultType.finalResultType + override def termSymbol: Symbol = resultType.termSymbol + override def typeSymbol: Symbol = resultType.typeSymbol + override def parents: List[Type] = resultType.parents + override def decls: Scope = resultType.decls + override def baseTypeSeq: BaseTypeSeq = resultType.baseTypeSeq + override def baseTypeSeqDepth: Int = resultType.baseTypeSeqDepth + override def baseClasses: List[Symbol] = resultType.baseClasses + override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) + override def boundSyms = resultType.boundSyms + override def isVolatile = resultType.isVolatile + override def safeToString: String = "=> "+ resultType + override def kind = "NullaryMethodType" + } + + /** A type function or the type of a polymorphic value (and thus of kind *). + * + * Before the introduction of NullaryMethodType, a polymorphic nullary method (e.g, def isInstanceOf[T]: Boolean) + * used to be typed as PolyType(tps, restpe), and a monomorphic one as PolyType(Nil, restpe) + * This is now: PolyType(tps, NullaryMethodType(restpe)) and NullaryMethodType(restpe) + * by symmetry to MethodTypes: PolyType(tps, MethodType(params, restpe)) and MethodType(params, restpe) + * + * Thus, a PolyType(tps, TypeRef(...)) unambiguously indicates a type function (which results from eta-expanding a type constructor alias). + * Similarly, PolyType(tps, ClassInfoType(...)) is a type constructor. + * + * A polytype is of kind * iff its resultType is a (nullary) method type. + */ + case class PolyType(override val typeParams: List[Symbol], override val resultType: Type) + extends Type { + //assert(!(typeParams contains NoSymbol), this) + assert(typeParams nonEmpty, this) // used to be a marker for nullary method type, illegal now (see @NullaryMethodType) + + override def paramSectionCount: Int = resultType.paramSectionCount + override def paramss: List[List[Symbol]] = resultType.paramss + override def params: List[Symbol] = resultType.params + override def paramTypes: List[Type] = resultType.paramTypes + override def parents: List[Type] = resultType.parents + override def decls: Scope = resultType.decls + override def termSymbol: Symbol = resultType.termSymbol + override def typeSymbol: Symbol = resultType.typeSymbol + override def boundSyms = immutable.Set[Symbol](typeParams ++ resultType.boundSyms: _*) + override def prefix: Type = resultType.prefix + override def baseTypeSeq: BaseTypeSeq = resultType.baseTypeSeq + override def baseTypeSeqDepth: Int = resultType.baseTypeSeqDepth + override def baseClasses: List[Symbol] = resultType.baseClasses + override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) + override def narrow: Type = resultType.narrow + override def isVolatile = resultType.isVolatile + override def finalResultType: Type = resultType.finalResultType + + /** @M: typeDefSig wraps a TypeBounds in a PolyType + * to represent a higher-kinded type parameter + * wrap lo&hi in polytypes to bind variables + */ + override def bounds: TypeBounds = + TypeBounds(typeFun(typeParams, resultType.bounds.lo), + typeFun(typeParams, resultType.bounds.hi)) + + override def isHigherKinded = !typeParams.isEmpty + + override def safeToString = typeParamsString(this) + resultType + + override def cloneInfo(owner: Symbol) = { + val tparams = cloneSymbols(typeParams, owner) + PolyType(tparams, resultType.substSym(typeParams, tparams).cloneInfo(owner)) + } + + override def atOwner(owner: Symbol) = + if ((typeParams exists (_.owner != owner)) || (resultType.atOwner(owner) ne resultType)) + cloneInfo(owner) + else + this + + override def kind = "PolyType" + } + + case class ExistentialType(quantified: List[Symbol], + override val underlying: Type) extends RewrappingTypeProxy + { + override protected def rewrap(newtp: Type) = existentialAbstraction(quantified, newtp) + + override def isTrivial = false + override def isStable: Boolean = false + override def bounds = TypeBounds(maybeRewrap(underlying.bounds.lo), maybeRewrap(underlying.bounds.hi)) + override def parents = underlying.parents map maybeRewrap + override def boundSyms = quantified.toSet + override def prefix = maybeRewrap(underlying.prefix) + override def typeArgs = underlying.typeArgs map maybeRewrap + override def params = underlying.params mapConserve { param => + val tpe1 = rewrap(param.tpe) + if (tpe1 eq param.tpe) param else param.cloneSymbol.setInfo(tpe1) + } + override def paramTypes = underlying.paramTypes map maybeRewrap + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]) = { +// maybeRewrap(underlying.instantiateTypeParams(formals, actuals)) + + val quantified1 = new SubstTypeMap(formals, actuals) mapOver quantified + val underlying1 = underlying.instantiateTypeParams(formals, actuals) + if ((quantified1 eq quantified) && (underlying1 eq underlying)) this + else existentialAbstraction(quantified1, underlying1.substSym(quantified, quantified1)) + + } + override def baseType(clazz: Symbol) = maybeRewrap(underlying.baseType(clazz)) + override def baseTypeSeq = underlying.baseTypeSeq map maybeRewrap + override def isHigherKinded = false + + override def skolemizeExistential(owner: Symbol, origin: AnyRef) = { + def mkSkolem(tparam: Symbol): Symbol = { + val skolem = new TypeSkolem( + if (owner == NoSymbol) tparam.owner else owner, + tparam.pos, tparam.name.toTypeName, origin) + skolem.setInfo(tparam.info.cloneInfo(skolem)) + .setFlag(tparam.flags | EXISTENTIAL) + .resetFlag(PARAM) + } + val skolems = quantified map mkSkolem + for (skolem <- skolems) + skolem setInfo skolem.info.substSym(quantified, skolems) + underlying.substSym(quantified, skolems) + } + + private def wildcardArgsString(available: Set[Symbol], args: List[Type]): List[String] = args match { + case TypeRef(_, sym, _) :: args1 if (available contains sym) => + ("_"+sym.infoString(sym.info)) :: wildcardArgsString(available - sym, args1) + case arg :: args1 if !(quantified exists (arg contains _)) => + arg.toString :: wildcardArgsString(available, args1) + case _ => + List() + } + + override def safeToString: String = { + if (!(quantified exists (_.isSingletonExistential)) && !settings.debug.value) + // try to represent with wildcards first + underlying match { + case TypeRef(pre, sym, args) if args.nonEmpty => + val wargs = wildcardArgsString(quantified.toSet, args) + if (sameLength(wargs, args)) + return TypeRef(pre, sym, List()) + wargs.mkString("[", ", ", "]") + case _ => + } + var ustr = underlying.toString + underlying match { + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => ustr = "("+ustr+")" + case _ => + } + val str = + ustr+(quantified map (_.existentialToString) mkString(" forSome { ", "; ", " }")) + if (settings.explaintypes.value) "("+str+")" else str + } + + override def cloneInfo(owner: Symbol) = { + val tparams = cloneSymbols(quantified, owner) + ExistentialType(tparams, underlying.substSym(quantified, tparams)) + } + + override def atOwner(owner: Symbol) = + if (quantified exists (_.owner != owner)) cloneInfo(owner) else this + + override def kind = "ExistentialType" + + def withTypeVars(op: Type => Boolean): Boolean = withTypeVars(op, AnyDepth) + + def withTypeVars(op: Type => Boolean, depth: Int): Boolean = { + val quantifiedFresh = cloneSymbols(quantified) + val tvars = quantifiedFresh map (tparam => TypeVar(tparam)) + val underlying1 = underlying.instantiateTypeParams(quantified, tvars) // fuse subst quantified -> quantifiedFresh -> tvars + op(underlying1) && { + solve(tvars, quantifiedFresh, quantifiedFresh map (x => 0), false, depth) && + isWithinBounds(NoPrefix, NoSymbol, quantifiedFresh, tvars map (_.constr.inst)) + } + } + } + + /** A class containing the alternatives and type prefix of an overloaded symbol. + * Not used after phase `typer'. + */ + case class OverloadedType(pre: Type, alternatives: List[Symbol]) extends Type { + override def prefix: Type = pre + override def safeToString = + (alternatives map pre.memberType).mkString("", " <and> ", "") + override def kind = "OverloadedType" + } + + /** A class remembering a type instantiation for some a set of overloaded + * polymorphic symbols. + * Not used after phase `typer'. + */ + case class AntiPolyType(pre: Type, targs: List[Type]) extends Type { + override def safeToString = + pre.toString + targs.mkString("(with type arguments ", ",", ")"); + override def memberType(sym: Symbol) = appliedType(pre.memberType(sym), targs) +// override def memberType(sym: Symbol) = pre.memberType(sym) match { +// case PolyType(tparams, restp) => +// restp.subst(tparams, targs) +// /* I don't think this is needed, as existential types close only over value types +// case ExistentialType(tparams, qtpe) => +// existentialAbstraction(tparams, qtpe.memberType(sym)) +// */ +// case ErrorType => +// ErrorType +// } + override def kind = "AntiPolyType" + } + + //private var tidCount = 0 //DEBUG + + //@M + // a TypeVar used to be a case class with only an origin and a constr + // then, constr became mutable (to support UndoLog, I guess), but pattern-matching returned the original constr0 (a bug) + // now, pattern-matching returns the most recent constr + object TypeVar { + // encapsulate suspension so we can automatically link the suspension of cloned typevars to their original if this turns out to be necessary + def Suspension = new Suspension + class Suspension { + private val suspended = mutable.HashSet[TypeVar]() + def suspend(tv: TypeVar): Unit = { + tv.suspended = true + suspended += tv + } + def resumeAll(): Unit = { + for(tv <- suspended) { + tv.suspended = false + } + suspended.clear + } + } + + def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) + def apply(origin: Type, constr: TypeConstraint) = new TypeVar(origin, constr, List(), List()) + def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint, List(), tparam.typeParams) // TODO why not initialise TypeConstraint with bounds of tparam? + def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]) = new TypeVar(origin, constr, args, params) + } + + /** A class representing a type variable + * Not used after phase `typer'. + * A higher-kinded type variable has type arguments (a list of Type's) and type parameters (list of Symbols) + * A TypeVar whose list of args is non-empty can only be instantiated by a higher-kinded type that can be applied to these args + * a typevar is much like a typeref, except it has special logic for type equality/subtyping + */ + class TypeVar(val origin: Type, val constr0: TypeConstraint, override val typeArgs: List[Type], override val params: List[Symbol]) extends Type { + // params are needed to keep track of variance (see mapOverArgs in SubstMap) + assert(typeArgs.isEmpty || sameLength(typeArgs, params)) + // var tid = { tidCount += 1; tidCount } //DEBUG + + /** The constraint associated with the variable */ + var constr = constr0 + def instValid = constr.instValid + + /** The variable's skolemization level */ + val level = skolemizationLevel + + /** + * two occurrences of a higher-kinded typevar, e.g. ?CC[Int] and ?CC[String], correspond to + * *two instances* of TypeVar that share the *same* TypeConstraint + * constr for ?CC only tracks type constructors anyway, so when ?CC[Int] <:< List[Int] and ?CC[String] <:< Iterable[String] + * ?CC's hibounds contains List and Iterable + */ + def applyArgs(newArgs: List[Type]): TypeVar = + if (newArgs.isEmpty) this // SubstMap relies on this (though this check is redundant when called from appliedType...) + else TypeVar(origin, constr, newArgs, params) // @M TODO: interaction with undoLog?? + // newArgs.length may differ from args.length (could've been empty before) + // example: when making new typevars, you start out with C[A], then you replace C by ?C, which should yield ?C[A], then A by ?A, ?C[?A] + // we need to track a TypeVar's arguments, and map over them (see TypeMap::mapOver) + // TypeVars get applied to different arguments over time (in asSeenFrom) + // -- see pos/tcpoly_infer_implicit_tuplewrapper.scala + // thus: make new TypeVar's for every application of a TV to args, + // inference may generate several TypeVar's for a single type parameter that must be inferred, + // only one of them is in the set of tvars that need to be solved, but + // they share the same TypeConstraint instance + + // <region name="constraint mutators + undoLog"> + // invariant: before mutating constr, save old state in undoLog (undoLog is used to reset constraints to avoid piling up unrelated ones) + def setInst(tp: Type) { +// assert(!(tp containsTp this), this) + undoLog record this + constr.inst = tp + } + + def addLoBound(tp: Type, isNumericBound: Boolean = false) { + assert(tp != this) // implies there is a cycle somewhere (?) + //println("addLoBound: "+(safeToString, debugString(tp))) //DEBUG + undoLog record this + constr.addLoBound(tp, isNumericBound) + } + + def addHiBound(tp: Type, isNumericBound: Boolean = false) { + // assert(tp != this) + //println("addHiBound: "+(safeToString, debugString(tp))) //DEBUG + undoLog record this + constr.addHiBound(tp, isNumericBound) + } + // </region> + + // ignore subtyping&equality checks while true -- see findMember + private[TypeVar] var suspended = false + + /** Called when a TypeVar is involved in a subtyping check. Result is whether + * this TypeVar could plausibly be a [super/sub]type of argument `tp` and if so, + * tracks tp as a [lower/upper] bound of this TypeVar. + * + * if (isLowerBound) this typevar could be a subtype, track tp as a lower bound + * if (!isLowerBound) this typevar could be a supertype, track tp as an upper bound + * + * If isNumericBound is true, the subtype check is performed with weak_<:< instead of <:<. + */ + def registerBound(tp: Type, isLowerBound: Boolean, isNumericBound: Boolean = false): Boolean = { + // println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG + if (isLowerBound) assert(tp != this) + + def checkSubtypeLower(tp1: Type, tp2: Type) = + if (isNumericBound) tp1 weak_<:< tp2 + else tp1 <:< tp2 + + // swaps the arguments if it's an upper bound + def checkSubtype(tp1: Type, tp2: Type) = + if (isLowerBound) checkSubtypeLower(tp1, tp2) + else checkSubtypeLower(tp2, tp1) + + def addBound(tp: Type) = { + if (isLowerBound) addLoBound(tp, isNumericBound) + else addHiBound(tp, isNumericBound) + // println("addedBound: "+(this, tp)) // @MDEBUG + true + } + + /** Simple case: type arguments can be ignored, because either this typevar has + * no type parameters, or we are comparing to Any/Nothing. + * + * The latter condition is needed because HK unification is limited to constraints of the shape + * TC1[T1,..., TN] <: TC2[T'1,...,T'N] + * which would preclude the following important constraints: + * Nothing <: ?TC[?T] + * ?TC[?T] <: Any + */ + def unifySimple = (params.isEmpty || tp.typeSymbol == NothingClass || tp.typeSymbol == AnyClass) && + addBound(tp) + + /** Full case: involving a check of the form + * TC1[T1,..., TN] <: TC2[T'1,...,T'N] + * Checks subtyping of higher-order type vars, and uses variances as defined in the + * type parameter we're trying to infer (the result will be sanity-checked later) + */ + def unifyFull(tp: Type) = sameLength(typeArgs, tp.typeArgs) && { // this is a higher-kinded type var with same arity as tp + // side effect: adds the type constructor itself as a bound + addBound(tp.typeConstructor) + if (isLowerBound) isSubArgs(tp.typeArgs, typeArgs, params) + else isSubArgs(typeArgs, tp.typeArgs, params) + } + + /** TODO: need positive/negative test cases demonstrating this is correct. + */ + def unifyParents = + if (isLowerBound) tp.parents exists unifyFull + else tp.parents forall unifyFull + + // TODO: fancier unification, maybe rewrite constraint as follows? + // val sym = constr.hiBounds map {_.typeSymbol} find { _.typeParams.length == typeArgs.length} + // this <: tp.baseType(sym) + if (suspended) checkSubtype(tp, origin) + else if (constr.instValid) checkSubtype(tp, constr.inst) // type var is already set + else isRelatable(tp) && { + unifySimple || unifyFull(tp) || unifyFull(tp.dealias) || unifyFull(tp.widen) || unifyParents + } + } + + def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = { //println("regTypeEq: "+(safeToString, debugString(tp), typeVarLHS)) //@MDEBUG + def checkIsSameType(tp: Type) = + if(typeVarLHS) constr.inst =:= tp + else tp =:= constr.inst + + if (suspended) tp =:= origin + else if (constr.instValid) checkIsSameType(tp) + else isRelatable(tp) && { + val newInst = wildcardToTypeVarMap(tp) + if (constr.isWithinBounds(newInst)) { + setInst(tp) + true + } else false + } + } + + /** + * ?A.T =:= tp is rewritten as the constraint ?A <: {type T = tp} + * + * TODO: make these constraints count (incorporate them into implicit search in applyImplicitArgs) + * (T corresponds to @param sym) + */ + def registerTypeSelection(sym: Symbol, tp: Type): Boolean = { + val bound = refinedType(List(WildcardType), NoSymbol) + val bsym = bound.typeSymbol.newAliasType(NoPosition, sym.name.toTypeName) + bsym setInfo tp + bound.decls enter bsym + registerBound(bound, false) + } + + /** Can this variable be related in a constraint to type `tp'? + * This is not the case if `tp' contains type skolems whose + * skolemization level is higher than the level of this variable. + */ + def isRelatable(tp: Type): Boolean = + !tp.exists { t => + t.typeSymbol match { + case ts: TypeSkolem => ts.level > level + case _ => false + } + } + + override val isHigherKinded = typeArgs.isEmpty && params.nonEmpty + + override def normalize: Type = + if (constr.instValid) constr.inst + // get here when checking higher-order subtyping of the typevar by itself + // TODO: check whether this ever happens? + else if (isHigherKinded) typeFun(params, applyArgs(params map (_.typeConstructor))) + else super.normalize + + override def typeSymbol = origin.typeSymbol + override def isStable = origin.isStable + override def isVolatile = origin.isVolatile + + private def levelString = if (settings.explaintypes.value) level else "" + override def safeToString = constr.inst match { + case null => "<null " + origin + ">" + case NoType => "?" + levelString + origin + typeArgsString(this) + case x => "" + x + } + override def kind = "TypeVar" + + def cloneInternal = { + // cloning a suspended type variable when it's suspended will cause the clone + // to never be resumed with the current implementation + assert(!suspended) + TypeVar(origin, constr cloneInternal, typeArgs, params) // @M TODO: clone args/params? + } + } + + /** A type carrying some annotations. Created by the typechecker + * when eliminating ``Annotated'' trees (see typedAnnotated). + * + * @param annotations the list of annotations on the type + * @param underlying the type without the annotation + * @param selfsym a "self" symbol with type <code>underlying</code>; + * only available if -Yself-in-annots is turned on. Can be NoSymbol + * if it is not used. + */ + case class AnnotatedType(override val annotations: List[AnnotationInfo], + override val underlying: Type, + override val selfsym: Symbol) + extends RewrappingTypeProxy { + + assert(!annotations.isEmpty) + + override protected def rewrap(tp: Type) = AnnotatedType(annotations, tp, selfsym) + + override def isTrivial: Boolean = isTrivial0 + private lazy val isTrivial0 = underlying.isTrivial && (annotations forall (_.isTrivial)) + + override def safeToString: String = { + val attString = + if (annotations.isEmpty) + "" + else + annotations.mkString(" @", " @", "") + + underlying + attString + } + + /** Add a number of annotations to this type */ + override def withAnnotations(annots: List[AnnotationInfo]): Type = + copy(annots:::this.annotations) + + /** Remove any annotations from this type */ + override def withoutAnnotations = underlying.withoutAnnotations + + /** Set the self symbol */ + override def withSelfsym(sym: Symbol) = + AnnotatedType(annotations, underlying, sym) + + /** Drop the annotations on the bounds, unless but the low and high + * bounds are exactly tp. + */ + override def bounds: TypeBounds = underlying.bounds match { + case TypeBounds(_: this.type, _: this.type) => TypeBounds(this, this) + case oftp => oftp + } + + // ** Replace formal type parameter symbols with actual type arguments. * / + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]) = { + val annotations1 = annotations.map(info => AnnotationInfo(info.atp.instantiateTypeParams( + formals, actuals), info.args, info.assocs).setPos(info.pos)) + val underlying1 = underlying.instantiateTypeParams(formals, actuals) + if ((annotations1 eq annotations) && (underlying1 eq underlying)) this + else AnnotatedType(annotations1, underlying1, selfsym) + } + + /** Return the base type sequence of tp, dropping the annotations, unless the base type sequence of tp + * is precisely tp itself. */ + override def baseTypeSeq: BaseTypeSeq = { + val oftp = underlying.baseTypeSeq + if ((oftp.length == 1) && (oftp(0) eq underlying)) + baseTypeSingletonSeq(this) + else + oftp + } + + override def kind = "AnnotatedType" + } + + /** A class representing types with a name. When an application uses + * named arguments, the named argument types for calling isApplicable + * are represented as NamedType. + */ + case class NamedType(name: Name, tp: Type) extends Type { + override def safeToString: String = name.toString +": "+ tp + } + + /** A class representing an as-yet unevaluated type. + */ + abstract class LazyType extends Type { + override def isComplete: Boolean = false + override def complete(sym: Symbol) + override def safeToString = "<?>" + override def kind = "LazyType" + } + +// Creators --------------------------------------------------------------- + + /** Rebind symbol `sym' to an overriding member in type + * `pre'. + */ + private def rebind(pre: Type, sym: Symbol): Symbol = { + val owner = sym.owner + if (owner.isClass && owner != pre.typeSymbol && !sym.isEffectivelyFinal && !sym.isClass) { + //Console.println("rebind "+pre+" "+sym)//DEBUG + val rebind = pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || sym.isStable) + if (rebind == NoSymbol) sym + else { + // Console.println("rebound "+pre+" "+sym+" to "+rebind)//DEBUG + rebind + } + } else sym + } + + /** Convert a `super' prefix to a this-type if `sym' + * is abstract or final. + */ + private def removeSuper(tp: Type, sym: Symbol): Type = tp match { + case SuperType(thistp, _) => + if (sym.isEffectivelyFinal || sym.isDeferred) thistp + else tp + case _ => + tp + } + + /** The canonical creator for single-types */ + def singleType(pre: Type, sym: Symbol): Type = { + if (phase.erasedTypes) + sym.tpe.resultType + else if (sym.isRootPackage) + ThisType(RootClass) + else { + var sym1 = rebind(pre, sym) + val pre1 = removeSuper(pre, sym1) + if (pre1 ne pre) sym1 = rebind(pre1, sym1) + // why not do the hash-consing in the SingleType.apply() + // factory, like the other UniqueTypes? + unique(new SingleType(pre1, sym1) with UniqueType) + } + } + + /** the canonical creator for a refined type with a given scope */ + def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos : Position): Type = { + if (phase.erasedTypes) + if (parents.isEmpty) ObjectClass.tpe else parents.head + else { + val clazz = owner.newRefinementClass(NoPosition) + val result = RefinedType(parents, decls, clazz) + clazz.setInfo(result) + result + } + } + + /** The canonical creator for a refined type with an initially empty scope. + * + * @param parents ... + * @param owner ... + * @return ... + */ + def refinedType(parents: List[Type], owner: Symbol): Type = + refinedType(parents, owner, new Scope, owner.pos) + + def copyRefinedType(original: RefinedType, parents: List[Type], decls: Scope) = + if ((parents eq original.parents) && (decls eq original.decls)) original + else { + val owner = if (original.typeSymbol == NoSymbol) NoSymbol else original.typeSymbol.owner + val result = refinedType(parents, owner) + val syms1 = decls.toList + for (sym <- syms1) + result.decls.enter(sym.cloneSymbol(result.typeSymbol)) + val syms2 = result.decls.toList + val resultThis = result.typeSymbol.thisType + for (sym <- syms2) + sym.setInfo(sym.info.substThis(original.typeSymbol, resultThis).substSym(syms1, syms2)) + result + } + + /** The canonical creator for typerefs + * todo: see how we can clean this up a bit + */ + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type = { + // type alias selections are rebound in TypeMap ("coevolved", actually -- see #3731) + // e.g., when type parameters that are referenced by the alias are instantiated in + // the prefix. See pos/depmet_rebind_typealias. + def rebindTR(pre: Type, sym: Symbol) = + if (sym.isAbstractType) rebind(pre, sym) else sym + + val sym1 = rebindTR(pre, sym) + + // we require that object is initialized, thus info.typeParams instead of typeParams. + if (sym1.isAliasType && sameLength(sym1.info.typeParams, args)) { + if (sym1.lockOK) TypeRef(pre, sym1, args) // don't expand type alias (cycles checked by lockOK) + else throw new TypeError("illegal cyclic reference involving " + sym1) + } + else { + val pre1 = removeSuper(pre, sym1) + if (pre1 ne pre) + typeRef(pre1, rebindTR(pre1, sym1), args) + else pre match { + case _: CompoundType if sym1.isClass => + // sharpen prefix so that it is maximal and still contains the class. + pre.parents.reverse dropWhile (_.member(sym1.name) != sym1) match { + case Nil => TypeRef(pre, sym1, args) + case parent :: _ => typeRef(parent, sym1, args) + } + case _ => + TypeRef(pre, sym1, args) + } + } + } + + /** The canonical creator for implicit method types */ + def JavaMethodType(params: List[Symbol], resultType: Type): JavaMethodType = + new JavaMethodType(params, resultType) // don't unique this! + + /** Create a new MethodType of the same class as tp, i.e. keep JavaMethodType */ + def copyMethodType(tp: Type, params: List[Symbol], restpe: Type): Type = tp match { + case _: JavaMethodType => JavaMethodType(params, restpe) + case _ => MethodType(params, restpe) + } + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself, and repeated parent classes are merged. + */ + def intersectionType(tps: List[Type], owner: Symbol): Type = tps match { + case List(tp) => + tp + case _ => + refinedType(tps, owner) +/* + def merge(tps: List[Type]): List[Type] = tps match { + case tp :: tps1 => + val tps1a = tps1 filter (_.typeSymbol.==(tp.typeSymbol)) + val tps1b = tps1 filter (_.typeSymbol.!=(tp.typeSymbol)) + mergePrefixAndArgs(tps1a, -1) match { + case Some(tp1) => tp1 :: merge(tps1b) + case None => throw new MalformedType( + "malformed type: "+refinedType(tps, owner)+" has repeated parent class "+ + tp.typeSymbol+" with incompatible prefixes or type arguments") + } + case _ => tps + } + refinedType(merge(tps), owner) +*/ + } + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself. */ + def intersectionType(tps: List[Type]): Type = tps match { + case List(tp) => tp + case _ => refinedType(tps, commonOwner(tps)) + } + + /** A creator for type applications */ + def appliedType(tycon: Type, args: List[Type]): Type = + if (args.isEmpty) tycon //@M! `if (args.isEmpty) tycon' is crucial (otherwise we create new types in phases after typer and then they don't get adapted (??)) + else tycon match { + case TypeRef(pre, sym @ (NothingClass|AnyClass), _) => typeRef(pre, sym, Nil) //@M drop type args to Any/Nothing + case TypeRef(pre, sym, _) => typeRef(pre, sym, args) + case PolyType(tparams, restpe) => restpe.instantiateTypeParams(tparams, args) + case ExistentialType(tparams, restpe) => ExistentialType(tparams, appliedType(restpe, args)) + case st: SingletonType => appliedType(st.widen, args) // @M TODO: what to do? see bug1 + case RefinedType(parents, decls) => RefinedType(parents map (appliedType(_, args)), decls) // MO to AM: please check + case TypeBounds(lo, hi) => TypeBounds(appliedType(lo, args), appliedType(hi, args)) + case tv@TypeVar(_, _) => tv.applyArgs(args) + case AnnotatedType(annots, underlying, self) => AnnotatedType(annots, appliedType(underlying, args), self) + case ErrorType => tycon + case WildcardType => tycon // needed for neg/t0226 + case _ => abort(debugString(tycon)) + } + + /** A creator for type parameterizations that strips empty type parameter lists. + * Use this factory method to indicate the type has kind * (it's a polymorphic value) + * until we start tracking explicit kinds equivalent to typeFun (except that the latter requires tparams nonEmpty) + */ + def polyType(tparams: List[Symbol], tpe: Type): Type = + if (tparams nonEmpty) typeFun(tparams, tpe) + else tpe // it's okay to be forgiving here + + /** A creator for anonymous type functions, where the symbol for the type function still needs to be created + * + * TODO: + * type params of anonymous type functions, which currently can only arise from normalising type aliases, are owned by the type alias of which they are the eta-expansion + * higher-order subtyping expects eta-expansion of type constructors that arise from a class; here, the type params are owned by that class, but is that the right thing to do? + */ + def typeFunAnon(tps: List[Symbol], body: Type): Type = typeFun(tps, body) + + /** A creator for a type functions, assuming the type parameters tps already have the right owner + */ + def typeFun(tps: List[Symbol], body: Type): Type = PolyType(tps, body) + + /** A creator for existential types. This generates: + * + * tpe1 where { tparams } + * + * where `tpe1' is the result of extrapolating `tpe' wrt to `tparams'. Extrapolating means + * that type variables in `tparams' occurring in covariant positions are replaced by upper bounds, + * (minus any SingletonClass markers), + * type variables in `tparams' occurring in contravariant positions are replaced by upper bounds, + * provided the resulting type is legal wrt to stability, and does not contain any + * type variable in `tparams'. + * The abstraction drops all type parameters that are not directly or indirectly + * referenced by type `tpe1'. + * If there are no remaining type parameters, simply returns result type `tpe'. + */ + def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type = + if (tparams.isEmpty) tpe0 + else { + var occurCount = emptySymCount ++ (tparams map (_ -> 0)) + val tpe = deAlias(tpe0) + def countOccs(tp: Type) = + for (t <- tp) { + t match { + case TypeRef(_, sym, _) => + occurCount get sym match { + case Some(count) => occurCount += (sym -> (count + 1)) + case none => + } + case _ => + } + } + countOccs(tpe) + for (tparam <- tparams) countOccs(tparam.info) + + val extrapolate = new TypeMap { + variance = 1 + def apply(tp: Type): Type = { + val tp1 = mapOver(tp) + tp1 match { + case TypeRef(pre, sym, args) if (variance != 0) && (occurCount isDefinedAt sym) => + val repl = if (variance == 1) dropSingletonType(tp1.bounds.hi) else tp1.bounds.lo + //println("eliminate "+sym+"/"+repl+"/"+occurCount(sym)+"/"+(tparams exists (repl.contains)))//DEBUG + if (repl.typeSymbol != NothingClass && repl.typeSymbol != NullClass && + occurCount(sym) == 1 && !(tparams exists (repl.contains))) + repl + else tp1 + case _ => + tp1 + } + } + override def mapOver(tp: Type): Type = tp match { + case SingleType(pre, sym) => + if (sym.isPackageClass) tp // short path + else { + val pre1 = this(pre) + if ((pre1 eq pre) || !pre1.isStable) tp + else singleType(pre1, sym) + } + case _ => super.mapOver(tp) + } + + override def mapOver(tree: Tree) = + tree match { + case tree:Ident if tree.tpe.isStable => + // Do not discard the types of existential ident's. + // The symbol of the Ident itself cannot be listed + // in the existential's parameters, so the + // resulting existential type would be ill-formed. + Some(tree) + + case _ => + super.mapOver(tree) + } + } + val tpe1 = extrapolate(tpe) + var tparams0 = tparams + var tparams1 = tparams0 filter tpe1.contains + + while (tparams1 != tparams0) { + tparams0 = tparams1 + tparams1 = tparams filter { p => + tparams1 exists { p1 => p1 == p || (p1.info contains p) } + } + } + if (tparams1.isEmpty) tpe1 + else tpe1 match { + case ExistentialType(tparams2, tpe2) => ExistentialType(tparams1 ::: tparams2, tpe2) + case _ => ExistentialType(tparams1, tpe1) + } + } + + /** Remove any occurrences of type aliases from this type */ + object deAlias extends TypeMap { + def apply(tp: Type): Type = mapOver { + tp match { + case TypeRef(pre, sym, args) if sym.isAliasType => tp.normalize + case _ => tp + } + } + } + + /** Remove any occurrence of type <singleton> from this type and its parents */ + object dropSingletonType extends TypeMap { + def apply(tp: Type): Type = { + tp match { + case TypeRef(_, SingletonClass, _) => + AnyClass.tpe + case tp1 @ RefinedType(parents, decls) => + var parents1 = parents filter (_.typeSymbol != SingletonClass) + if (parents1.isEmpty) parents1 = List(AnyClass.tpe) + if (parents1.tail.isEmpty && decls.isEmpty) mapOver(parents1.head) + else mapOver(copyRefinedType(tp1, parents1, decls)) + case tp1 => + mapOver(tp1) + } + } + } + +// Hash consing -------------------------------------------------------------- + + private val initialUniquesCapacity = 4096 + private var uniques: util.HashSet[Type] = _ + private var uniqueRunId = NoRunId + + private def unique[T <: Type](tp: T): T = { + incCounter(rawTypeCount) + if (uniqueRunId != currentRunId) { + uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) + uniqueRunId = currentRunId + } + (uniques findEntryOrUpdate tp).asInstanceOf[T] + } + +// Helper Classes --------------------------------------------------------- + + /** @PP: Unable to see why these apparently constant types should need vals + * in every TypeConstraint, I lifted them out. + */ + private lazy val numericLoBound = IntClass.tpe + private lazy val numericHiBound = intersectionType(List(ByteClass.tpe, CharClass.tpe), ScalaPackageClass) + + /** A class expressing upper and lower bounds constraints of type variables, + * as well as their instantiations. + */ + class TypeConstraint(lo0: List[Type], hi0: List[Type], numlo0: Type, numhi0: Type) { + def this(lo0: List[Type], hi0: List[Type]) = this(lo0, hi0, NoType, NoType) + def this() = this(List(), List()) + + private var lobounds = lo0 + private var hibounds = hi0 + private var numlo = numlo0 + private var numhi = numhi0 + + def loBounds: List[Type] = if (numlo == NoType) lobounds else numlo :: lobounds + def hiBounds: List[Type] = if (numhi == NoType) hibounds else numhi :: hibounds + + def addLoBound(tp: Type, isNumericBound: Boolean = false) { + if (isNumericBound && isNumericValueType(tp)) { + if (numlo == NoType || isNumericSubType(numlo, tp)) + numlo = tp + else if (!isNumericSubType(tp, numlo)) + numlo = numericLoBound + } + else lobounds ::= tp + } + + def addHiBound(tp: Type, isNumericBound: Boolean = false) { + if (isNumericBound && isNumericValueType(tp)) { + if (numhi == NoType || isNumericSubType(tp, numhi)) + numhi = tp + else if (!isNumericSubType(numhi, tp)) + numhi = numericHiBound + } + else hibounds ::= tp + } + + def isWithinBounds(tp: Type): Boolean = + lobounds.forall(_ <:< tp) && + hibounds.forall(tp <:< _) && + (numlo == NoType || (numlo weak_<:< tp)) && + (numhi == NoType || (tp weak_<:< numhi)) + + var inst: Type = NoType // @M reduce visibility? + + def instValid = (inst ne null) && (inst ne NoType) + + def cloneInternal = { + val tc = new TypeConstraint(lobounds, hibounds, numlo, numhi) + tc.inst = inst + tc + } + + override def toString = + (loBounds map (_.safeToString)).mkString("[ _>:(", ",", ") ") + + (hiBounds map (_.safeToString)).mkString("| _<:(", ",", ") ] _= ") + + inst.safeToString + } + + /** A prototype for mapping a function over all possible types + */ + abstract class TypeMap extends Function1[Type, Type] { + // deferred inherited: def apply(tp: Type): Type + + /** The variance relative to start. If you want variances to be significant, set + * variance = 1 + * at the top of the typemap. + */ + var variance = 0 + + /** Should this map drop annotations that are not + * type-constraint annotations? + */ + val dropNonConstraintAnnotations = false + + /** Check whether two lists have elements that are eq-equal */ + def allEq[T <: AnyRef](l1: List[T], l2: List[T]) = + (l1 corresponds l2)(_ eq _) + + // #3731: return sym1 for which holds: pre bound sym.name to sym and pre1 now binds sym.name to sym1, conceptually exactly the same symbol as sym + // the selection of sym on pre must be updated to the selection of sym1 on pre1, + // since sym's info was probably updated by the TypeMap to yield a new symbol sym1 with transformed info + // @returns sym1 + protected def coevolveSym(pre: Type, pre1: Type, sym: Symbol): Symbol = + if((pre ne pre1) && sym.isAliasType) // only need to rebind type aliases here, as typeRef already handles abstract types (they are allowed to be rebound more liberally) + (pre, pre1) match { + case (RefinedType(_, decls), RefinedType(_, decls1)) => // don't look at parents -- it would be an error to override alias types anyway + //val sym1 = + decls1.lookup(sym.name) +// assert(decls.lookupAll(sym.name).toList.length == 1) +// assert(decls1.lookupAll(sym.name).toList.length == 1) +// assert(sym1.isAliasType) +// println("coevolved "+ sym +" : "+ sym.info +" to "+ sym1 +" : "+ sym1.info +" with "+ pre +" -> "+ pre1) +// sym1 + case _ => // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre? +// val sym1 = pre1.nonPrivateMember(sym.name).suchThat(sym => sym.isAliasType) +// println("??coevolve "+ sym +" : "+ sym.info +" to "+ sym1 +" : "+ sym1.info +" with "+ pre +" -> "+ pre1) + sym + } + else sym + + /** Map this function over given type */ + def mapOver(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = this(pre) + //val args1 = args mapConserve this(_) + val args1 = if (args.isEmpty) args + else { + val tparams = sym.typeParams + if (tparams.isEmpty) args + else mapOverArgs(args, tparams) + } + if ((pre1 eq pre) && (args1 eq args)) tp + else typeRef(pre1, coevolveSym(pre, pre1, sym), args1) + case ThisType(_) => tp + case SingleType(pre, sym) => + if (sym.isPackageClass) tp // short path + else { + val pre1 = this(pre) + if (pre1 eq pre) tp + else singleType(pre1, sym) + } + case MethodType(params, result) => + variance = -variance + val params1 = mapOver(params) + variance = -variance + val result1 = this(result) + if ((params1 eq params) && (result1 eq result)) tp + // for new dependent types: result1.substSym(params, params1)? + else copyMethodType(tp, params1, result1.substSym(params, params1)) + case PolyType(tparams, result) => + variance = -variance + val tparams1 = mapOver(tparams) + variance = -variance + var result1 = this(result) + if ((tparams1 eq tparams) && (result1 eq result)) tp + else PolyType(tparams1, result1.substSym(tparams, tparams1)) + case NullaryMethodType(result) => + val result1 = this(result) + if (result1 eq result) tp + else NullaryMethodType(result1) + case ConstantType(_) => tp + case SuperType(thistp, supertp) => + val thistp1 = this(thistp) + val supertp1 = this(supertp) + if ((thistp1 eq thistp) && (supertp1 eq supertp)) tp + else SuperType(thistp1, supertp1) + case TypeBounds(lo, hi) => + variance = -variance + val lo1 = this(lo) + variance = -variance + val hi1 = this(hi) + if ((lo1 eq lo) && (hi1 eq hi)) tp + else TypeBounds(lo1, hi1) + case BoundedWildcardType(bounds) => + val bounds1 = this(bounds) + if (bounds1 eq bounds) tp + else BoundedWildcardType(bounds1.asInstanceOf[TypeBounds]) + case rtp @ RefinedType(parents, decls) => + val parents1 = parents mapConserve (this) + val decls1 = mapOver(decls) + //if ((parents1 eq parents) && (decls1 eq decls)) tp + //else refinementOfClass(tp.typeSymbol, parents1, decls1) + copyRefinedType(rtp, parents1, decls1) + case ExistentialType(tparams, result) => + val tparams1 = mapOver(tparams) + var result1 = this(result) + if ((tparams1 eq tparams) && (result1 eq result)) tp + else ExistentialType(tparams1, result1.substSym(tparams, tparams1)) + case OverloadedType(pre, alts) => + val pre1 = if (pre.isInstanceOf[ClassInfoType]) pre else this(pre) + if (pre1 eq pre) tp + else OverloadedType(pre1, alts) + case AntiPolyType(pre, args) => + val pre1 = this(pre) + val args1 = args mapConserve (this) + if ((pre1 eq pre) && (args1 eq args)) tp + else AntiPolyType(pre1, args1) + case tv@TypeVar(_, constr) => + if (constr.instValid) this(constr.inst) + else tv.applyArgs(mapOverArgs(tv.typeArgs, tv.params)) //@M !args.isEmpty implies !typeParams.isEmpty + case NotNullType(tp) => + val tp1 = this(tp) + if (tp1 eq tp) tp + else NotNullType(tp1) + case AnnotatedType(annots, atp, selfsym) => + val annots1 = mapOverAnnotations(annots) + val atp1 = this(atp) + if ((annots1 eq annots) && (atp1 eq atp)) tp + else if (annots1.isEmpty) atp1 + else AnnotatedType(annots1, atp1, selfsym) +/* + case ErrorType => tp + case WildcardType => tp + case NoType => tp + case NoPrefix => tp +*/ + case _ => + tp + // throw new Error("mapOver inapplicable for " + tp); + } + + def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = + map2Conserve(args, tparams) { (arg, tparam) => + val v = variance + if (tparam.isContravariant) variance = -variance + else if (!tparam.isCovariant) variance = 0 + val arg1 = this(arg) + variance = v + arg1 + } + + /** Map this function over given scope */ + def mapOver(scope: Scope): Scope = { + val elems = scope.toList + val elems1 = mapOver(elems) + if (elems1 eq elems) scope + else new Scope(elems1) + } + + /** Map this function over given list of symbols */ + def mapOver(origSyms: List[Symbol]): List[Symbol] = { + val change = origSyms exists { sym => + val v = variance + if (sym.isAliasType) variance = 0 + val result = this(sym.info) + variance = v + result ne sym.info + } + if (!change) origSyms // fast path in case nothing changes due to map + else { // map is not the identity --> do cloning properly + val clonedSyms = origSyms map (_.cloneSymbol) + val clonedInfos = clonedSyms map (_.info.substSym(origSyms, clonedSyms)) + val transformedInfos = clonedInfos mapConserve (this) + (clonedSyms, transformedInfos).zipped map (_ setInfo _) + + clonedSyms + } + } + + + def mapOverAnnotations(annots: List[AnnotationInfo]) + : List[AnnotationInfo] = { + val newAnnots = annots.flatMap(mapOver(_)) + if (allEq(newAnnots, annots)) + annots + else + newAnnots + } + + def mapOver(annot: AnnotationInfo): Option[AnnotationInfo] = { + val AnnotationInfo(atp, args, assocs) = annot + + if (dropNonConstraintAnnotations && + !(atp.typeSymbol isNonBottomSubClass TypeConstraintClass)) + return None + + val atp1 = mapOver(atp) + val args1 = mapOverAnnotArgs(args) + // there is no need to rewrite assocs, as they are constants + + if ((args eq args1) && (atp eq atp1)) + Some(annot) + else if (sameLength(args1, args)) + Some(AnnotationInfo(atp1, args1, assocs).setPos(annot.pos)) + else + None + } + + /** Map over a set of annotation arguments. If any + * of the arguments cannot be mapped, then return Nil. */ + def mapOverAnnotArgs(args: List[Tree]): List[Tree] = { + val args1 = args flatMap (x => mapOver(x)) + if (!sameLength(args1, args)) + Nil + else if (allEq(args, args1)) + args + else + args1 + } + + def mapOver(tree: Tree): Option[Tree] = + Some(mapOver(tree, ()=>return None)) + + /** Map a tree that is part of an annotation argument. + * If the tree cannot be mapped, then invoke giveup(). + * The default is to transform the tree with + * TypeMapTransformer. + */ + def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = + (new TypeMapTransformer).transform(tree) + + /** This transformer leaves the tree alone except to remap + * its types. */ + class TypeMapTransformer extends Transformer { + override def transform(tree: Tree) = { + val tree1 = super.transform(tree) + val tpe1 = TypeMap.this(tree1.tpe) + if ((tree eq tree1) && (tree.tpe eq tpe1)) + tree + else + tree1.shallowDuplicate.setType(tpe1) + } + } + } + + /** A type map that always returns the input type unchanged */ + object IdentityTypeMap extends TypeMap { + def apply(tp: Type) = tp + } + + abstract class TypeTraverser extends TypeMap { + def traverse(tp: Type): Unit + def apply(tp: Type): Type = { traverse(tp); tp } + } + + abstract class TypeCollector[T](initial: T) extends TypeTraverser { + var result: T = _ + def collect(tp: Type) = { + result = initial + traverse(tp) + result + } + } + + private val emptySymMap = immutable.Map[Symbol, Symbol]() + private val emptySymCount = immutable.Map[Symbol, Int]() + + def typeParamsToExistentials(clazz: Symbol, tparams: List[Symbol]): List[Symbol] = { + val eparams = for ((tparam, i) <- tparams.zipWithIndex) yield { + clazz.newExistential(clazz.pos, newTypeName("?"+i)).setInfo(tparam.info.bounds) + } + for (tparam <- eparams) tparam setInfo tparam.info.substSym(tparams, eparams) + eparams + } + + // note: it's important to write the two tests in this order, + // as only typeParams forces the classfile to be read. See #400 + private def isRawIfWithoutArgs(sym: Symbol) = + sym.isClass && sym.typeParams.nonEmpty && sym.isJavaDefined + + def isRaw(sym: Symbol, args: List[Type]) = + !phase.erasedTypes && isRawIfWithoutArgs(sym) && args.isEmpty + + /** Is type tp a ``raw type''? */ + def isRawType(tp: Type) = tp match { + case TypeRef(_, sym, args) => isRaw(sym, args) + case _ => false + } + + /** The raw to existential map converts a ``raw type'' to an existential type. + * It is necessary because we might have read a raw type of a + * parameterized Java class from a class file. At the time we read the type + * the corresponding class file might still not be read, so we do not + * know what the type parameters of the type are. Therefore + * the conversion of raw types to existential types might not have taken place + * in ClassFileparser.sigToType (where it is usually done) + */ + object rawToExistential extends TypeMap { + private var expanded = immutable.Set[Symbol]() + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, List()) if isRawIfWithoutArgs(sym) => + if (expanded contains sym) AnyRefClass.tpe + else try { + expanded += sym + val eparams = mapOver(typeParamsToExistentials(sym, sym.typeParams)) + existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) + } finally { + expanded -= sym + } + case ExistentialType(_, _) => // stop to avoid infinite expansions + tp + case _ => + mapOver(tp) + } + } + + def singletonBounds(hi: Type) = { + TypeBounds.upper(intersectionType(List(hi, SingletonClass.tpe))) + } + + /** A map to compute the asSeenFrom method */ + class AsSeenFromMap(pre: Type, clazz: Symbol) extends TypeMap { + override val dropNonConstraintAnnotations = true + + var capturedParams: List[Symbol] = List() + + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + object annotationArgRewriter extends TypeMapTransformer { + /** Rewrite `This` trees in annotation argument trees */ + def rewriteThis(tree: Tree): Tree = + tree match { + case This(_) + if (tree.symbol isNonBottomSubClass clazz) && + (pre.widen.typeSymbol isNonBottomSubClass tree.symbol) => + if (pre.isStable) { // XXX why is this in this method? pull it out and guard the call `annotationArgRewriter.transform(tree)`? + val termSym = + pre.typeSymbol.owner.newValue( + pre.typeSymbol.pos, + pre.typeSymbol.name.toTermName).setInfo(pre) // what symbol should really be used? + gen.mkAttributedQualifier(pre, termSym) + } else + giveup() + + case tree => tree + } + + override def transform(tree: Tree): Tree = { + val tree1 = rewriteThis(super.transform(tree)) + tree1 + } + } + + annotationArgRewriter.transform(tree) + } + + var capturedPre = emptySymMap + + def stabilize(pre: Type, clazz: Symbol): Type = + capturedPre.getOrElse(clazz, { + val qvar = clazz freshExistential ".type" setInfo singletonBounds(pre) + capturedPre += (clazz -> qvar) + capturedParams = qvar :: capturedParams + qvar + }).tpe + + /** Return pre.baseType(clazz), or if that's NoType and clazz is a refinement, pre itself. + * See bug397.scala for an example where the second alternative is needed. + * The problem is that when forming the base type sequence of an abstract type, + * any refinements in the base type list might be regenerated, and thus acquire + * new class symbols. However, since refinements always have non-interesting prefixes + * it looks OK to me to just take the prefix directly. */ + def base(pre: Type, clazz: Symbol) = { + val b = pre.baseType(clazz) + if (b == NoType && clazz.isRefinementClass) pre + else b + } + + def apply(tp: Type): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) tp + else tp match { + case ThisType(sym) => + def toPrefix(pre: Type, clazz: Symbol): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) tp + else if ((sym isNonBottomSubClass clazz) && + (pre.widen.typeSymbol isNonBottomSubClass sym)) { + val pre1 = pre match { + case SuperType(thistp, _) => thistp + case _ => pre + } + if (!(pre1.isStable || + pre1.typeSymbol.isPackageClass || + pre1.typeSymbol.isModuleClass && pre1.typeSymbol.isStatic)) { + stabilize(pre1, sym) + } else { + pre1 + } + } else { + toPrefix(base(pre, clazz).prefix, clazz.owner); + } + toPrefix(pre, clazz) + case SingleType(pre, sym) => + if (sym.isPackageClass) tp // short path + else { + val pre1 = this(pre) + if (pre1 eq pre) tp + else if (pre1.isStable) singleType(pre1, sym) + else pre1.memberType(sym).resultType //todo: this should be rolled into existential abstraction + } + // AM: Martin, is this description accurate? + // walk the owner chain of `clazz` (the original argument to asSeenFrom) until we find the type param's owner (while rewriting pre as we crawl up the owner chain) + // once we're at the owner, extract the information that pre encodes about the type param, + // by minimally subsuming pre to the type instance of the class that owns the type param, + // the type we're looking for is the type instance's type argument at the position corresponding to the type parameter + // optimisation: skip this type parameter if it's not owned by a class, as those params are not influenced by the prefix through which they are seen + // (concretely: type params of anonymous type functions, which currently can only arise from normalising type aliases, are owned by the type alias of which they are the eta-expansion) + // (skolems also aren't affected: they are ruled out by the isTypeParameter check) + case TypeRef(prefix, sym, args) if (sym.isTypeParameter && sym.owner.isClass) => + def toInstance(pre: Type, clazz: Symbol): Type = + if ((pre eq NoType) || (pre eq NoPrefix) || !clazz.isClass) mapOver(tp) + //@M! see test pos/tcpoly_return_overriding.scala why mapOver is necessary + else { + def throwError = abort("" + tp + sym.locationString + " cannot be instantiated from " + pre.widen) + + def instParam(ps: List[Symbol], as: List[Type]): Type = + if (ps.isEmpty) throwError + else if (sym eq ps.head) + // @M! don't just replace the whole thing, might be followed by type application + appliedType(as.head, args mapConserve (this)) // @M: was as.head + else instParam(ps.tail, as.tail); + val symclazz = sym.owner + if (symclazz == clazz && !pre.isInstanceOf[TypeVar] && (pre.widen.typeSymbol isNonBottomSubClass symclazz)) { + // have to deconst because it may be a Class[T]. + pre.baseType(symclazz).deconst match { + case TypeRef(_, basesym, baseargs) => + //Console.println("instantiating " + sym + " from " + basesym + " with " + basesym.typeParams + " and " + baseargs+", pre = "+pre+", symclazz = "+symclazz);//DEBUG + if (sameLength(basesym.typeParams, baseargs)) { + instParam(basesym.typeParams, baseargs) + } else { + throw new TypeError( + "something is wrong (wrong class file?): "+basesym+ + " with type parameters "+ + basesym.typeParams.map(_.name).mkString("[",",","]")+ + " gets applied to arguments "+baseargs.mkString("[",",","]")+", phase = "+phase) + } + case ExistentialType(tparams, qtpe) => + capturedParams = capturedParams union tparams + toInstance(qtpe, clazz) + case _ => + throwError + } + } else toInstance(base(pre, clazz).prefix, clazz.owner) + } + toInstance(pre, clazz) + case _ => + mapOver(tp) + } + } + + /** A base class to compute all substitutions */ + abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap { + val fromContains = from.toSet // avoiding repeatedly traversing from + assert(sameLength(from, to), "Unsound substitution from "+ from +" to "+ to) + + /** Are `sym' and `sym1' the same. + * Can be tuned by subclasses. + */ + protected def matches(sym: Symbol, sym1: Symbol): Boolean = sym eq sym1 + + /** Map target to type, can be tuned by subclasses */ + protected def toType(fromtp: Type, tp: T): Type + + def subst(tp: Type, sym: Symbol, from: List[Symbol], to: List[T]): Type = + if (from.isEmpty) tp + // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(tp, from)) + else if (matches(from.head, sym)) toType(tp, to.head) + else subst(tp, sym, from.tail, to.tail) + + protected def renameBoundSyms(tp: Type): Type = tp match { + case MethodType(ps, restp) => + val ps1 = cloneSymbols(ps) + copyMethodType(tp, ps1, renameBoundSyms(restp.substSym(ps, ps1))) + case PolyType(bs, restp) => + val bs1 = cloneSymbols(bs) + PolyType(bs1, renameBoundSyms(restp.substSym(bs, bs1))) + case ExistentialType(bs, restp) => + val bs1 = cloneSymbols(bs) + ExistentialType(bs1, restp.substSym(bs, bs1)) + case _ => + tp + } + + def apply(tp0: Type): Type = if (from.isEmpty) tp0 else { + val boundSyms = tp0.boundSyms + val tp1 = if (boundSyms exists fromContains) renameBoundSyms(tp0) else tp0 + val tp = mapOver(tp1) + + tp match { + // @M + // 1) arguments must also be substituted (even when the "head" of the + // applied type has already been substituted) + // example: (subst RBound[RT] from [type RT,type RBound] to + // [type RT&,type RBound&]) = RBound&[RT&] + // 2) avoid loops (which occur because alpha-conversion is + // not performed properly imo) + // e.g. if in class Iterable[a] there is a new Iterable[(a,b)], + // we must replace the a in Iterable[a] by (a,b) + // (must not recurse --> loops) + // 3) replacing m by List in m[Int] should yield List[Int], not just List + case TypeRef(NoPrefix, sym, args) => + appliedType(subst(tp, sym, from, to), args) // if args.isEmpty, appliedType is the identity + case SingleType(NoPrefix, sym) => + subst(tp, sym, from, to) + case _ => + tp + } + } + } + + /** A map to implement the `substSym' method. */ + class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends SubstMap(from, to) { + protected def toType(fromtp: Type, sym: Symbol) = fromtp match { + case TypeRef(pre, _, args) => typeRef(pre, sym, args) + case SingleType(pre, _) => singleType(pre, sym) + } + override def apply(tp: Type): Type = if (from.isEmpty) tp else { + def subst(sym: Symbol, from: List[Symbol], to: List[Symbol]): Symbol = + if (from.isEmpty) sym + // else if (to.isEmpty) error("Unexpected substitution on '%s': from = %s but to == Nil".format(sym, from)) + else if (matches(from.head, sym)) to.head + else subst(sym, from.tail, to.tail) + tp match { + case TypeRef(pre, sym, args) if pre ne NoPrefix => + val newSym = subst(sym, from, to) + // assert(newSym.typeParams.length == sym.typeParams.length, "typars mismatch in SubstSymMap: "+(sym, sym.typeParams, newSym, newSym.typeParams)) + mapOver(typeRef(pre, newSym, args)) // mapOver takes care of subst'ing in args + case SingleType(pre, sym) if pre ne NoPrefix => + mapOver(singleType(pre, subst(sym, from, to))) + case _ => + super.apply(tp) + } + } + + + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + object trans extends TypeMapTransformer { + + def termMapsTo(sym: Symbol) = + if (fromContains(sym)) + Some(to(from.indexOf(sym))) + else + None + + override def transform(tree: Tree) = + tree match { + case tree@Ident(_) => + termMapsTo(tree.symbol) match { + case Some(tosym) => + if (tosym.info.bounds.hi.typeSymbol isSubClass SingletonClass) { + Ident(tosym.existentialToString) + .setSymbol(tosym) + .setPos(tosym.pos) + .setType(dropSingletonType(tosym.info.bounds.hi)) + } else { + giveup() + } + case none => super.transform(tree) + } + case tree => super.transform(tree) + } + } + trans.transform(tree) + } + } + + /** A map to implement the `subst' method. */ + class SubstTypeMap(from: List[Symbol], to: List[Type]) + extends SubstMap(from, to) { + protected def toType(fromtp: Type, tp: Type) = tp + + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + object trans extends TypeMapTransformer { + override def transform(tree: Tree) = + tree match { + case Ident(name) if fromContains(tree.symbol) => + val totpe = to(from.indexOf(tree.symbol)) + if (!totpe.isStable) giveup() + else Ident(name).setPos(tree.pos).setSymbol(tree.symbol).setType(totpe) + + case _ => super.transform(tree) + } + } + trans.transform(tree) + } + + } + + /** A map to implement the `substThis' method. */ + class SubstThisMap(from: Symbol, to: Type) extends TypeMap { + def apply(tp: Type): Type = tp match { + case ThisType(sym) if (sym == from) => to + case _ => mapOver(tp) + } + } + + class SubstSuperMap(from: Type, to: Type) extends TypeMap { + def apply(tp: Type): Type = if (tp eq from) to else mapOver(tp) + } + + class SubstWildcardMap(from: List[Symbol]) extends TypeMap { + def apply(tp: Type): Type = try { + tp match { + case TypeRef(_, sym, _) if from contains sym => + BoundedWildcardType(sym.info.bounds) + case _ => + mapOver(tp) + } + } catch { + case ex: MalformedType => + WildcardType + } + } + +// dependent method types + object IsDependentCollector extends TypeCollector(false) { + def traverse(tp: Type) { + if(tp isImmediatelyDependent) result = true + else if (!result) mapOver(tp) + } + } + + object ApproximateDependentMap extends TypeMap { + def apply(tp: Type): Type = + if(tp isImmediatelyDependent) WildcardType + else mapOver(tp) + } + + class InstantiateDependentMap(params: List[Symbol], actuals: List[Type]) extends TypeMap { + private val actualsIndexed = actuals.toIndexedSeq + override val dropNonConstraintAnnotations = true + + object ParamWithActual { + def unapply(sym: Symbol): Option[Type] = { + val pid = params indexOf sym + if(pid != -1) Some(actualsIndexed(pid)) else None + } + } + + def apply(tp: Type): Type = + mapOver(tp) match { + case SingleType(NoPrefix, ParamWithActual(arg)) if arg.isStable => arg // unsound to replace args by unstable actual #3873 + // (soundly) expand type alias selections on implicit arguments, see depmet_implicit_oopsla* test cases -- typically, `param.isImplicit` + case tp1@TypeRef(SingleType(NoPrefix, ParamWithActual(arg)), sym, targs) => + val res = typeRef(arg, sym, targs) + if(res.typeSymbolDirect isAliasType) res.dealias + else tp1 + case tp1 => tp1 // don't return the original `tp`, which may be different from `tp1`, due to `dropNonConstraintAnnotations` + } + + def existentialsNeeded: List[Symbol] = existSyms.filter(_ ne null).toList + + private val existSyms: Array[Symbol] = new Array(actualsIndexed.size) + private def haveExistential(i: Int) = {assert((i >= 0) && (i <= actualsIndexed.size)); existSyms(i) ne null} + + /* Return the type symbol for referencing a parameter inside the existential quantifier. + * (Only needed if the actual is unstable.) + */ + def existSymFor(actualIdx: Int) = + if (haveExistential(actualIdx)) existSyms(actualIdx) + else { + val oldSym = params(actualIdx) + val symowner = oldSym.owner + val bound = singletonBounds(actualsIndexed(actualIdx)) + + val sym = symowner.newExistential(oldSym.pos, newTypeName(oldSym.name + ".type")) + sym.setInfo(bound) + sym.setFlag(oldSym.flags) + + existSyms(actualIdx) = sym + sym + } + + //AM propagate more info to annotations -- this seems a bit ad-hoc... (based on code by spoon) + override def mapOver(arg: Tree, giveup: ()=>Nothing): Tree = { + object treeTrans extends Transformer { + override def transform(tree: Tree): Tree = { + tree match { + case RefParamAt(pid) => + // TODO: this should be simplified; in the stable case, one can probably + // just use an Ident to the tree.symbol. Why an existential in the non-stable case? + val actual = actualsIndexed(pid) + if (actual.isStable && actual.typeSymbol != NothingClass) { + gen.mkAttributedQualifier(actualsIndexed(pid), tree.symbol) + } else { + val sym = existSymFor(pid) + (Ident(sym.name) + copyAttrs tree + setType typeRef(NoPrefix, sym, Nil)) + } + case _ => super.transform(tree) + } + } + object RefParamAt { + def unapply(tree: Tree): Option[Int] = tree match { + case Ident(_) => Some(params indexOf tree.symbol) filterNot (_ == -1) + case _ => None + } + } + } + + treeTrans.transform(arg) + } + } + + + object StripAnnotationsMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case AnnotatedType(_, atp, _) => + mapOver(atp) + case tp => + mapOver(tp) + } + } + + /** A map to convert every occurrence of a wildcard type to a fresh + * type variable */ + object wildcardToTypeVarMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case WildcardType => + TypeVar(tp, new TypeConstraint) + case BoundedWildcardType(bounds) => + TypeVar(tp, new TypeConstraint(List(bounds.lo), List(bounds.hi))) + case _ => + mapOver(tp) + } + } + + /** A map to convert every occurrence of a type variable to a + wildcard type */ + object typeVarToOriginMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeVar(origin, _) => origin + case _ => mapOver(tp) + } + } + + /** A map to implement the `contains' method */ + class ContainsCollector(sym: Symbol) extends TypeCollector(false) { + def traverse(tp: Type) { + if (!result) { + tp.normalize match { + case TypeRef(_, sym1, _) if (sym == sym1) => result = true + case SingleType(_, sym1) if (sym == sym1) => result = true + case _ => mapOver(tp) + } + } + } + + override def mapOver(arg: Tree) = { + for (t <- arg) { + traverse(t.tpe) + if (t.symbol == sym) + result = true + } + Some(arg) + } + } + + /** A map to implement the `contains' method */ + class ContainsTypeCollector(t: Type) extends TypeCollector(false) { + def traverse(tp: Type) { + if (!result) { + if (tp eq t) result = true + else mapOver(tp) + } + } + override def mapOver(arg: Tree) = { + for (t <- arg) { + traverse(t.tpe) + } + Some(arg) + } + } + + /** A map to implement the `filter' method */ + class FilterTypeCollector(p: Type => Boolean) extends TypeCollector(new ListBuffer[Type]) { + def traverse(tp: Type) { + if (p(tp)) result += tp + mapOver(tp) + } + } + + class ForEachTypeTraverser(f: Type => Unit) extends TypeTraverser { + def traverse(tp: Type) { + f(tp) + mapOver(tp) + } + } + + /** A map to implement the `filter' method */ + class FindTypeCollector(p: Type => Boolean) extends TypeCollector[Option[Type]](None) { + def traverse(tp: Type) { + if (result.isEmpty) { + if (p(tp)) result = Some(tp) + mapOver(tp) + } + } + } + + /** A map to implement the `contains' method */ + object ErroneousCollector extends TypeCollector(false) { + def traverse(tp: Type) { + if (!result) { + result = tp.isError + mapOver(tp) + } + } + } + + /** A map to compute the most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given type. + */ + object commonOwnerMap extends TypeMap { + var result: Symbol = _ + def init() = { result = NoSymbol } + def apply(tp: Type): Type = { + assert(tp ne null) + tp.normalize match { + case ThisType(sym) => + register(sym) + case TypeRef(NoPrefix, sym, args) => + register(sym.owner); args foreach apply + case SingleType(NoPrefix, sym) => + register(sym.owner) + case _ => + mapOver(tp) + } + tp + } + private def register(sym: Symbol) { + while (result != NoSymbol && sym != result && !(sym isNestedIn result)) + result = result.owner; + } + } + + class MissingAliasControl extends ControlThrowable + val missingAliasException = new MissingAliasControl + class MissingTypeControl extends ControlThrowable + + object adaptToNewRunMap extends TypeMap { + private def adaptToNewRun(pre: Type, sym: Symbol): Symbol = { + if (phase.flatClasses) { + sym + } else if (sym.isModuleClass) { + adaptToNewRun(pre, sym.sourceModule).moduleClass + } else if ((pre eq NoPrefix) || (pre eq NoType) || sym.isPackageClass) { + sym + } else { + var rebind0 = pre.findMember(sym.name, BRIDGE, 0, true) + if (rebind0 == NoSymbol) { + if (sym.isAliasType) throw missingAliasException + if (settings.debug.value) println(pre+"."+sym+" does no longer exist, phase = "+phase) + throw new MissingTypeControl // For build manager and presentation compiler purposes + //assert(false, pre+"."+sym+" does no longer exist, phase = "+phase) + } + /** The two symbols have the same fully qualified name */ + def corresponds(sym1: Symbol, sym2: Symbol): Boolean = + sym1.name == sym2.name && (sym1.isPackageClass || corresponds(sym1.owner, sym2.owner)) + if (!corresponds(sym.owner, rebind0.owner)) { + if (settings.debug.value) + log("ADAPT1 pre = "+pre+", sym = "+sym+sym.locationString+", rebind = "+rebind0+rebind0.locationString) + val bcs = pre.baseClasses.dropWhile(bc => !corresponds(bc, sym.owner)); + if (bcs.isEmpty) + assert(pre.typeSymbol.isRefinementClass, pre) // if pre is a refinementclass it might be a structural type => OK to leave it in. + else + rebind0 = pre.baseType(bcs.head).member(sym.name) + if (settings.debug.value) log( + "ADAPT2 pre = " + pre + + ", bcs.head = " + bcs.head + + ", sym = " + sym+sym.locationString + + ", rebind = " + rebind0 + ( + if (rebind0 == NoSymbol) "" + else rebind0.locationString + ) + ) + } + val rebind = rebind0.suchThat(sym => sym.isType || sym.isStable) + if (rebind == NoSymbol) { + if (settings.debug.value) log("" + phase + " " +phase.flatClasses+sym.owner+sym.name+" "+sym.isType) + throw new MalformedType(pre, sym.nameString) + } + rebind + } + } + def apply(tp: Type): Type = tp match { + case ThisType(sym) => + try { + val sym1 = adaptToNewRun(sym.owner.thisType, sym) + if (sym1 == sym) tp else ThisType(sym1) + } catch { + case ex: MissingTypeControl => + tp + } + case SingleType(pre, sym) => + if (sym.isPackage) tp + else { + val pre1 = this(pre) + val sym1 = adaptToNewRun(pre1, sym) + if ((pre1 eq pre) && (sym1 eq sym)) tp + else singleType(pre1, sym1) + } + case TypeRef(pre, sym, args) => + if (sym.isPackageClass) tp + else { + val pre1 = this(pre) + val args1 = args mapConserve (this) + try { + val sym1 = adaptToNewRun(pre1, sym) + if ((pre1 eq pre) && (sym1 eq sym) && (args1 eq args)/* && sym.isExternal*/) tp + else typeRef(pre1, sym1, args1) + } catch { + case ex: MissingAliasControl => + apply(tp.dealias) + case _: MissingTypeControl => + tp + } + } + case MethodType(params, restp) => + val restp1 = this(restp) + if (restp1 eq restp) tp + else copyMethodType(tp, params, restp1) + case NullaryMethodType(restp) => + val restp1 = this(restp) + if (restp1 eq restp) tp + else NullaryMethodType(restp1) + case PolyType(tparams, restp) => + val restp1 = this(restp) + if (restp1 eq restp) tp + else PolyType(tparams, restp1) + + // Lukas: we need to check (together) whether we should also include parameter types + // of PolyType and MethodType in adaptToNewRun + + case ClassInfoType(parents, decls, clazz) => + if (clazz.isPackageClass) tp + else { + val parents1 = parents mapConserve (this) + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) + } + case RefinedType(parents, decls) => + val parents1 = parents mapConserve (this) + if (parents1 eq parents) tp + else refinedType(parents1, tp.typeSymbol.owner, decls, tp.typeSymbol.owner.pos) + case SuperType(_, _) => mapOver(tp) + case TypeBounds(_, _) => mapOver(tp) + case TypeVar(_, _) => mapOver(tp) + case AnnotatedType(_,_,_) => mapOver(tp) + case NotNullType(_) => mapOver(tp) + case ExistentialType(_, _) => mapOver(tp) + case _ => tp + } + } + + class SubTypePair(val tp1: Type, val tp2: Type) { + override def hashCode = tp1.hashCode * 41 + tp2.hashCode + override def equals(other: Any) = other match { + case stp: SubTypePair => + (tp1 =:= stp.tp1) && (tp2 =:= stp.tp2) + case _ => + false + } + override def toString = tp1+" <:<? "+tp2 + } + +// Helper Methods ------------------------------------------------------------- + + final val LubGlbMargin = 0 + + /** The maximum allowable depth of lubs or glbs over types `ts' + * This is the maximum depth of all types in the base type sequences + * of each of the types `ts', plus LubGlbMargin + */ + def lubDepth(ts: List[Type]) = { + var d = 0 + for (tp <- ts) d = math.max(d, tp.baseTypeSeqDepth) + d + LubGlbMargin + } + + /** Is intersection of given types populated? That is, + * for all types tp1, tp2 in intersection + * for all common base classes bc of tp1 and tp2 + * let bt1, bt2 be the base types of tp1, tp2 relative to class bc + * Then: + * bt1 and bt2 have the same prefix, and + * any corresponding non-variant type arguments of bt1 and bt2 are the same + */ + def isPopulated(tp1: Type, tp2: Type): Boolean = { + def isConsistent(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => + assert(sym1 == sym2) + pre1 =:= pre2 && + ((args1, args2, sym1.typeParams).zipped forall { + (arg1, arg2, tparam) => + //if (tparam.variance == 0 && !(arg1 =:= arg2)) Console.println("inconsistent: "+arg1+"!="+arg2)//DEBUG + if (tparam.variance == 0) arg1 =:= arg2 + else if (arg1.isInstanceOf[TypeVar]) + // if left-hand argument is a typevar, make it compatible with variance + // this is for more precise pattern matching + // todo: work this in the spec of this method + // also: think what happens if there are embedded typevars? + if (tparam.variance < 0) arg1 <:< arg2 else arg2 <:< arg1 + else true + }) + case (et: ExistentialType, _) => + et.withTypeVars(isConsistent(_, tp2)) + case (_, et: ExistentialType) => + et.withTypeVars(isConsistent(tp1, _)) + } + + def check(tp1: Type, tp2: Type) = + if (tp1.typeSymbol.isClass && tp1.typeSymbol.hasFlag(FINAL)) + tp1 <:< tp2 || isNumericValueClass(tp1.typeSymbol) && isNumericValueClass(tp2.typeSymbol) + else tp1.baseClasses forall (bc => + tp2.baseTypeIndex(bc) < 0 || isConsistent(tp1.baseType(bc), tp2.baseType(bc))) + + check(tp1, tp2)/* && check(tp2, tp1)*/ // need to investgate why this can't be made symmetric -- neg/gadts1 fails, and run/existials also. + } + + /** Does a pattern of type `patType' need an outer test when executed against + * selector type `selType' in context defined by `currentOwner'? + */ + def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol) = { + def createDummyClone(pre: Type): Type = { + val dummy = currentOwner.enclClass.newValue(NoPosition, nme.ANYNAME).setInfo(pre.widen) + singleType(ThisType(currentOwner.enclClass), dummy) + } + def maybeCreateDummyClone(pre: Type, sym: Symbol): Type = pre match { + case SingleType(pre1, sym1) => + if (sym1.isModule && sym1.isStatic) { + NoType + } else if (sym1.isModule && sym.owner == sym1.moduleClass) { + val pre2 = maybeCreateDummyClone(pre1, sym1) + if (pre2 eq NoType) pre2 + else singleType(pre2, sym1) + } else { + createDummyClone(pre) + } + case ThisType(clazz) => + if (clazz.isModuleClass) + maybeCreateDummyClone(clazz.typeOfThis, sym) + else if (sym.owner == clazz && (sym.hasFlag(PRIVATE) || sym.privateWithin == clazz)) + NoType + else + createDummyClone(pre) + case _ => + NoType + } + patType match { + case TypeRef(pre, sym, args) => + val pre1 = maybeCreateDummyClone(pre, sym) + (pre1 ne NoType) && isPopulated(typeRef(pre1, sym, args), selType) + case _ => + false + } + } + + private var subsametypeRecursions: Int = 0 + + private def isUnifiable(pre1: Type, pre2: Type) = + (beginsWithTypeVarOrIsRefined(pre1) || beginsWithTypeVarOrIsRefined(pre2)) && (pre1 =:= pre2) + + /** Returns true iff we are past phase specialize, + * sym1 and sym2 are two existential skolems with equal names and bounds, + * and pre1 and pre2 are equal prefixes + */ + private def isSameSpecializedSkolem(sym1: Symbol, sym2: Symbol, pre1: Type, pre2: Type) = { + sym1.isExistentialSkolem && sym2.isExistentialSkolem && + sym1.name == sym2.name && + phase.specialized && + sym1.info =:= sym2.info && + pre1 =:= pre2 + } + + private def equalSymsAndPrefixes(sym1: Symbol, pre1: Type, sym2: Symbol, pre2: Type): Boolean = + if (sym1 == sym2) sym1.hasPackageFlag || phase.erasedTypes || pre1 =:= pre2 + else (sym1.name == sym2.name) && isUnifiable(pre1, pre2) + + /** Do `tp1' and `tp2' denote equivalent types? + */ + def isSameType(tp1: Type, tp2: Type): Boolean = try { + incCounter(sametypeCount) + subsametypeRecursions += 1 + undoLog undoUnless { + isSameType1(tp1, tp2) + } + } finally { + subsametypeRecursions -= 1 + // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) + // it doesn't help to keep separate recursion counts for the three methods that now share it + // if (subsametypeRecursions == 0) undoLog.clear() + } + + def isDifferentType(tp1: Type, tp2: Type): Boolean = try { + subsametypeRecursions += 1 + undoLog undo { // undo type constraints that arise from operations in this block + !isSameType1(tp1, tp2) + } + } finally { + subsametypeRecursions -= 1 + // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) + // it doesn't help to keep separate recursion counts for the three methods that now share it + // if (subsametypeRecursions == 0) undoLog.clear() + } + + def isDifferentTypeConstructor(tp1: Type, tp2: Type): Boolean = tp1 match { + case TypeRef(pre1, sym1, _) => + tp2 match { + case TypeRef(pre2, sym2, _) => sym1 != sym2 || isDifferentType(pre1, pre2) + case _ => true + } + case _ => true + } + + def normalizePlus(tp: Type) = + if (isRawType(tp)) rawToExistential(tp) + else tp.normalize + + /* + todo: change to: + def normalizePlus(tp: Type) = tp match { + case TypeRef(pre, sym, List()) => + if (!sym.isInitialized) sym.rawInfo.load(sym) + if (sym.isJavaDefined && !sym.typeParams.isEmpty) rawToExistential(tp) + else tp.normalize + case _ => tp.normalize + } + */ +/* + private def isSameType0(tp1: Type, tp2: Type): Boolean = { + if (tp1 eq tp2) return true + ((tp1, tp2) match { + case (ErrorType, _) => true + case (WildcardType, _) => true + case (_, ErrorType) => true + case (_, WildcardType) => true + + case (NoType, _) => false + case (NoPrefix, _) => tp2.typeSymbol.isPackageClass + case (_, NoType) => false + case (_, NoPrefix) => tp1.typeSymbol.isPackageClass + + case (ThisType(sym1), ThisType(sym2)) + if (sym1 == sym2) => + true + case (SingleType(pre1, sym1), SingleType(pre2, sym2)) + if (equalSymsAndPrefixes(sym1, pre1, sym2, pre2)) => + true +/* + case (SingleType(pre1, sym1), ThisType(sym2)) + if (sym1.isModule && + sym1.moduleClass == sym2 && + pre1 =:= sym2.owner.thisType) => + true + case (ThisType(sym1), SingleType(pre2, sym2)) + if (sym2.isModule && + sym2.moduleClass == sym1 && + pre2 =:= sym1.owner.thisType) => + true +*/ + case (ConstantType(value1), ConstantType(value2)) => + value1 == value2 + case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => + equalSymsAndPrefixes(sym1, pre1, sym2, pre2) && + ((tp1.isHigherKinded && tp2.isHigherKinded && tp1.normalize =:= tp2.normalize) || + isSameTypes(args1, args2)) + // @M! normalize reduces higher-kinded case to PolyType's + case (RefinedType(parents1, ref1), RefinedType(parents2, ref2)) => + def isSubScope(s1: Scope, s2: Scope): Boolean = s2.toList.forall { + sym2 => + var e1 = s1.lookupEntry(sym2.name) + (e1 ne null) && { + val substSym = sym2.info.substThis(sym2.owner, e1.sym.owner.thisType) + var isEqual = false + while (!isEqual && (e1 ne null)) { + isEqual = e1.sym.info =:= substSym + e1 = s1.lookupNextEntry(e1) + } + isEqual + } + } + //Console.println("is same? " + tp1 + " " + tp2 + " " + tp1.typeSymbol.owner + " " + tp2.typeSymbol.owner)//DEBUG + isSameTypes(parents1, parents2) && isSubScope(ref1, ref2) && isSubScope(ref2, ref1) + case (MethodType(params1, res1), MethodType(params2, res2)) => + // new dependent types: probably fix this, use substSym as done for PolyType + (isSameTypes(tp1.paramTypes, tp2.paramTypes) && + res1 =:= res2 && + tp1.isImplicit == tp2.isImplicit) + case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => + // assert((tparams1 map (_.typeParams.length)) == (tparams2 map (_.typeParams.length))) + (tparams1.length == tparams2.length) && (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && // @M looks like it might suffer from same problem as #2210 + res1 =:= res2.substSym(tparams2, tparams1) + case (ExistentialType(tparams1, res1), ExistentialType(tparams2, res2)) => + (tparams1.length == tparams2.length) && (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && // @M looks like it might suffer from same problem as #2210 + res1 =:= res2.substSym(tparams2, tparams1) + case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => + lo1 =:= lo2 && hi1 =:= hi2 + case (BoundedWildcardType(bounds), _) => + bounds containsType tp2 + case (_, BoundedWildcardType(bounds)) => + bounds containsType tp1 + case (tv @ TypeVar(_,_), tp) => + tv.registerTypeEquality(tp, true) + case (tp, tv @ TypeVar(_,_)) => + tv.registerTypeEquality(tp, false) + case (AnnotatedType(_,_,_), _) => + annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + case (_, AnnotatedType(_,_,_)) => + annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + case (_: SingletonType, _: SingletonType) => + var origin1 = tp1 + while (origin1.underlying.isInstanceOf[SingletonType]) { + assert(origin1 ne origin1.underlying, origin1) + origin1 = origin1.underlying + } + var origin2 = tp2 + while (origin2.underlying.isInstanceOf[SingletonType]) { + assert(origin2 ne origin2.underlying, origin2) + origin2 = origin2.underlying + } + ((origin1 ne tp1) || (origin2 ne tp2)) && (origin1 =:= origin2) + case _ => + false + }) || { + val tp1n = normalizePlus(tp1) + val tp2n = normalizePlus(tp2) + ((tp1n ne tp1) || (tp2n ne tp2)) && isSameType(tp1n, tp2n) + } + } +*/ + private def isSameType1(tp1: Type, tp2: Type): Boolean = { + if ((tp1 eq tp2) || + (tp1 eq ErrorType) || (tp1 eq WildcardType) || + (tp2 eq ErrorType) || (tp2 eq WildcardType)) + true + else if ((tp1 eq NoType) || (tp2 eq NoType)) + false + else if (tp1 eq NoPrefix) + tp2.typeSymbol.isPackageClass + else if (tp2 eq NoPrefix) + tp1.typeSymbol.isPackageClass + else { + isSameType2(tp1, tp2) || { + val tp1n = normalizePlus(tp1) + val tp2n = normalizePlus(tp2) + ((tp1n ne tp1) || (tp2n ne tp2)) && isSameType(tp1n, tp2n) + } + } + } + + def isSameType2(tp1: Type, tp2: Type): Boolean = { + tp1 match { + case tr1: TypeRef => + tp2 match { + case tr2: TypeRef => + return (equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre) && + ((tp1.isHigherKinded && tp2.isHigherKinded && tp1.normalize =:= tp2.normalize) || + isSameTypes(tr1.args, tr2.args))) || + ((tr1.pre, tr2.pre) match { + case (tv @ TypeVar(_,_), _) => tv.registerTypeSelection(tr1.sym, tr2) + case (_, tv @ TypeVar(_,_)) => tv.registerTypeSelection(tr2.sym, tr1) + case _ => false + }) + case _ => + } + case tt1: ThisType => + tp2 match { + case tt2: ThisType => + if (tt1.sym == tt2.sym) return true + case _ => + } + case st1: SingleType => + tp2 match { + case st2: SingleType => + if (equalSymsAndPrefixes(st1.sym, st1.pre, st2.sym, st2.pre)) return true + case _ => + } + case ct1: ConstantType => + tp2 match { + case ct2: ConstantType => + return (ct1.value == ct2.value) + case _ => + } + case rt1: RefinedType => + tp2 match { + case rt2: RefinedType => // + def isSubScope(s1: Scope, s2: Scope): Boolean = s2.toList.forall { + sym2 => + var e1 = s1.lookupEntry(sym2.name) + (e1 ne null) && { + val substSym = sym2.info.substThis(sym2.owner, e1.sym.owner.thisType) + var isEqual = false + while (!isEqual && (e1 ne null)) { + isEqual = e1.sym.info =:= substSym + e1 = s1.lookupNextEntry(e1) + } + isEqual + } + } + //Console.println("is same? " + tp1 + " " + tp2 + " " + tp1.typeSymbol.owner + " " + tp2.typeSymbol.owner)//DEBUG + return isSameTypes(rt1.parents, rt2.parents) && { + val decls1 = rt1.decls + val decls2 = rt2.decls + isSubScope(decls1, decls2) && isSubScope(decls2, decls1) + } + case _ => + } + case mt1: MethodType => + tp2 match { + case mt2: MethodType => + // DEPMETTODO new dependent types: probably fix this, use substSym as done for PolyType + return isSameTypes(mt1.paramTypes, mt2.paramTypes) && + mt1.resultType =:= mt2.resultType && + mt1.isImplicit == mt2.isImplicit + // note: no case NullaryMethodType(restpe) => return mt1.params.isEmpty && mt1.resultType =:= restpe + case _ => + } + case NullaryMethodType(restpe1) => + tp2 match { + // note: no case mt2: MethodType => return mt2.params.isEmpty && restpe =:= mt2.resultType + case NullaryMethodType(restpe2) => + return restpe1 =:= restpe2 + case _ => + } + case PolyType(tparams1, res1) => + tp2 match { + case PolyType(tparams2, res2) => +// assert((tparams1 map (_.typeParams.length)) == (tparams2 map (_.typeParams.length))) + // @M looks like it might suffer from same problem as #2210 + return ( + (sameLength(tparams1, tparams2)) && // corresponds does not check length of two sequences before checking the predicate + (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && + res1 =:= res2.substSym(tparams2, tparams1) + ) + case _ => + } + case ExistentialType(tparams1, res1) => + tp2 match { + case ExistentialType(tparams2, res2) => + // @M looks like it might suffer from same problem as #2210 + return ( + // corresponds does not check length of two sequences before checking the predicate -- faster & needed to avoid crasher in #2956 + sameLength(tparams1, tparams2) && + (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && + res1 =:= res2.substSym(tparams2, tparams1) + ) + case _ => + } + case TypeBounds(lo1, hi1) => + tp2 match { + case TypeBounds(lo2, hi2) => + return lo1 =:= lo2 && hi1 =:= hi2 + case _ => + } + case BoundedWildcardType(bounds) => + return bounds containsType tp2 + case _ => + } + tp2 match { + case BoundedWildcardType(bounds) => + return bounds containsType tp1 + case _ => + } + tp1 match { + case tv @ TypeVar(_,_) => + return tv.registerTypeEquality(tp2, true) + case _ => + } + tp2 match { + case tv @ TypeVar(_,_) => + return tv.registerTypeEquality(tp1, false) + case _ => + } + tp1 match { + case _: AnnotatedType => + return annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + case _ => + } + tp2 match { + case _: AnnotatedType => + return annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + case _ => + } + tp1 match { + case _: SingletonType => + tp2 match { + case _: SingletonType => + @inline def chaseDealiasedUnderlying(tp: Type): Type = { + var origin = tp + var next = origin.underlying.dealias + while (next.isInstanceOf[SingletonType]) { + assert(origin ne next, origin) + origin = next + next = origin.underlying.dealias + } + origin + } + val origin1 = chaseDealiasedUnderlying(tp1) + val origin2 = chaseDealiasedUnderlying(tp2) + ((origin1 ne tp1) || (origin2 ne tp2)) && (origin1 =:= origin2) + case _ => + false + } + case _ => + false + } + } + + /** Are `tps1' and `tps2' lists of pairwise equivalent + * types? + */ + def isSameTypes(tps1: List[Type], tps2: List[Type]): Boolean = (tps1 corresponds tps2)(_ =:= _) + + /** True if two lists have the same length. Since calling length on linear sequences + * is O(n), it is an inadvisable way to test length equality. + */ + final def sameLength(xs1: List[_], xs2: List[_]) = compareLengths(xs1, xs2) == 0 + @tailrec final def compareLengths(xs1: List[_], xs2: List[_]): Int = + if (xs1.isEmpty) { if (xs2.isEmpty) 0 else -1 } + else if (xs2.isEmpty) 1 + else compareLengths(xs1.tail, xs2.tail) + + /** Again avoiding calling length, but the lengthCompare interface is clunky. + */ + final def hasLength(xs: List[_], len: Int) = xs.lengthCompare(len) == 0 + + private val pendingSubTypes = new mutable.HashSet[SubTypePair] + private var basetypeRecursions: Int = 0 + private val pendingBaseTypes = new mutable.HashSet[Type] + + def isSubType(tp1: Type, tp2: Type): Boolean = isSubType(tp1, tp2, AnyDepth) + + def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = try { + subsametypeRecursions += 1 + + undoLog undoUnless { // if subtype test fails, it should not affect constraints on typevars + if (subsametypeRecursions >= LogPendingSubTypesThreshold) { + val p = new SubTypePair(tp1, tp2) + if (pendingSubTypes(p)) + false + else + try { + pendingSubTypes += p + isSubType2(tp1, tp2, depth) + } finally { + pendingSubTypes -= p + } + } else { + isSubType2(tp1, tp2, depth) + } + } + } finally { + subsametypeRecursions -= 1 + // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) + // it doesn't help to keep separate recursion counts for the three methods that now share it + // if (subsametypeRecursions == 0) undoLog.clear() + } + + /** Does this type have a prefix that begins with a type variable, + * or is it a refinement type? For type prefixes that fulfil this condition, + * type selections with the same name of equal (wrt) =:= prefixes are + * considered equal wrt =:= + */ + def beginsWithTypeVarOrIsRefined(tp: Type): Boolean = tp match { + case SingleType(pre, sym) => + !(sym hasFlag PACKAGE) && beginsWithTypeVarOrIsRefined(pre) + case tv@TypeVar(_, constr) => + !tv.instValid || beginsWithTypeVarOrIsRefined(constr.inst) + case RefinedType(_, _) => + true + case _ => + false + } + + def instTypeVar(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + typeRef(instTypeVar(pre), sym, args) + case SingleType(pre, sym) => + singleType(instTypeVar(pre), sym) + case TypeVar(_, constr) => + instTypeVar(constr.inst) + case _ => + tp + } + + def isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType) + + def isSingleType(tp: Type) = tp match { + case ThisType(_) | SuperType(_, _) | SingleType(_, _) => true + case _ => false + } + + def isConstantType(tp: Type) = tp match { + case ConstantType(_) => true + case _ => false + } + + // @assume tp1.isHigherKinded || tp2.isHigherKinded + def isHKSubType0(tp1: Type, tp2: Type, depth: Int): Boolean = ( + tp1.typeSymbol == NothingClass + || + tp2.typeSymbol == AnyClass // @M Any and Nothing are super-type resp. subtype of every well-kinded type + || // @M! normalize reduces higher-kinded case to PolyType's + ((tp1.normalize.withoutAnnotations , tp2.normalize.withoutAnnotations) match { + case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => // @assume tp1.isHigherKinded && tp2.isHigherKinded (as they were both normalized to PolyType) + sameLength(tparams1, tparams2) && { + if (tparams1.head.owner.isMethod) { // fast-path: polymorphic method type -- type params cannot be captured + (tparams1 corresponds tparams2)((p1, p2) => p2.info.substSym(tparams2, tparams1) <:< p1.info) && + res1 <:< res2.substSym(tparams2, tparams1) + } else { // normalized higher-kinded type + //@M for an example of why we need to generate fresh symbols, see neg/tcpoly_ticket2101.scala + val tpsFresh = cloneSymbols(tparams1) + + (tparams1 corresponds tparams2)((p1, p2) => + p2.info.substSym(tparams2, tpsFresh) <:< p1.info.substSym(tparams1, tpsFresh)) && + res1.substSym(tparams1, tpsFresh) <:< res2.substSym(tparams2, tpsFresh) + + //@M the forall in the previous test could be optimised to the following, + // but not worth the extra complexity since it only shaves 1s from quick.comp + // (List.forall2(tpsFresh/*optimisation*/, tparams2)((p1, p2) => + // p2.info.substSym(tparams2, tpsFresh) <:< p1.info /*optimisation, == (p1 from tparams1).info.substSym(tparams1, tpsFresh)*/) && + // this optimisation holds because inlining cloneSymbols in `val tpsFresh = cloneSymbols(tparams1)` gives: + // val tpsFresh = tparams1 map (_.cloneSymbol) + // for (tpFresh <- tpsFresh) tpFresh.setInfo(tpFresh.info.substSym(tparams1, tpsFresh)) + } + } && annotationsConform(tp1.normalize, tp2.normalize) + case (_, _) => false // @assume !tp1.isHigherKinded || !tp2.isHigherKinded + // --> thus, cannot be subtypes (Any/Nothing has already been checked) + })) + + /** True if all three arguments have the same number of elements and + * the function is true for all the triples. + */ + @tailrec final def corresponds3[A, B, C](xs1: List[A], xs2: List[B], xs3: List[C], f: (A, B, C) => Boolean): Boolean = { + if (xs1.isEmpty) xs2.isEmpty && xs3.isEmpty + else !xs2.isEmpty && !xs3.isEmpty && f(xs1.head, xs2.head, xs3.head) && corresponds3(xs1.tail, xs2.tail, xs3.tail, f) + } + + def isSubArg(t1: Type, t2: Type, variance: Int) = + (variance > 0 || t2 <:< t1) && (variance < 0 || t1 <:< t2) + + def isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = + corresponds3(tps1, tps2, tparams map (_.variance), isSubArg) + + def differentOrNone(tp1: Type, tp2: Type) = if (tp1 eq tp2) NoType else tp1 + + /** Does type `tp1' conform to `tp2'? + */ + private def isSubType2(tp1: Type, tp2: Type, depth: Int): Boolean = { + if ((tp1 eq tp2) || isErrorOrWildcard(tp1) || isErrorOrWildcard(tp2)) return true + if ((tp1 eq NoType) || (tp2 eq NoType)) return false + if (tp1 eq NoPrefix) return (tp2 eq NoPrefix) || tp2.typeSymbol.isPackageClass + if (tp2 eq NoPrefix) return tp1.typeSymbol.isPackageClass + if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2)) return tp1 =:= tp2 + if (tp1.isHigherKinded || tp2.isHigherKinded) return isHKSubType0(tp1, tp2, depth) + + /** First try, on the right: + * - unwrap Annotated types, BoundedWildcardTypes, + * - bind TypeVars on the right, if lhs is not Annotated nor BoundedWildcard + * - handle common cases for first-kind TypeRefs on both sides as a fast path. + */ + def firstTry = tp2 match { + // fast path: two typerefs, none of them HK + case tr2: TypeRef => + tp1 match { + case tr1: TypeRef => + val sym1 = tr1.sym + val sym2 = tr2.sym + val pre1 = tr1.pre + val pre2 = tr2.pre + (((if (sym1 == sym2) phase.erasedTypes || pre1 <:< pre2 + else (sym1.name == sym2.name && !sym1.isModuleClass && !sym2.isModuleClass && + (isUnifiable(pre1, pre2) || isSameSpecializedSkolem(sym1, sym2, pre1, pre2)))) && + isSubArgs(tr1.args, tr2.args, sym1.typeParams)) + || + sym2.isClass && { + val base = tr1 baseType sym2 + (base ne tr1) && base <:< tr2 + } + || + thirdTryRef(tr1, tr2)) + case _ => + secondTry + } + case AnnotatedType(_, _, _) => + tp1.withoutAnnotations <:< tp2.withoutAnnotations && annotationsConform(tp1, tp2) + case BoundedWildcardType(bounds) => + tp1 <:< bounds.hi + case tv2 @ TypeVar(_, constr2) => + tp1 match { + case AnnotatedType(_, _, _) | BoundedWildcardType(_) => + secondTry + case _ => + tv2.registerBound(tp1, true) + } + case _ => + secondTry + } + + /** Second try, on the left: + * - unwrap AnnotatedTypes, BoundedWildcardTypes, + * - bind typevars, + * - handle existential types by skolemization. + */ + def secondTry = tp1 match { + case AnnotatedType(_, _, _) => + tp1.withoutAnnotations <:< tp2.withoutAnnotations && annotationsConform(tp1, tp2) + case BoundedWildcardType(bounds) => + tp1.bounds.lo <:< tp2 + case tv @ TypeVar(_,_) => + tv.registerBound(tp2, false) + case ExistentialType(_, _) => + try { + skolemizationLevel += 1 + tp1.skolemizeExistential <:< tp2 + } finally { + skolemizationLevel -= 1 + } + case _ => + thirdTry + } + + def thirdTryRef(tp1: Type, tp2: TypeRef): Boolean = { + val sym2 = tp2.sym + sym2 match { + case NotNullClass => tp1.isNotNull + case SingletonClass => tp1.isStable || fourthTry + case _: ClassSymbol => + if (isRaw(sym2, tp2.args)) + isSubType(tp1, rawToExistential(tp2), depth) + else if (sym2.name == tpnme.REFINE_CLASS_NAME) + isSubType(tp1, sym2.info, depth) + else + fourthTry + case _: TypeSymbol => + if (sym2 hasFlag DEFERRED) { + val tp2a = tp2.bounds.lo + isDifferentTypeConstructor(tp2, tp2a) && tp1 <:< tp2a || fourthTry + } else { + isSubType(tp1.normalize, tp2.normalize, depth) + } + case _ => + fourthTry + } + } + + /** Third try, on the right: + * - decompose refined types. + * - handle typerefs, existentials, and notnull types. + * - handle left+right method types, polytypes, typebounds + */ + def thirdTry = tp2 match { + case tr2: TypeRef => + thirdTryRef(tp1, tr2) + case rt2: RefinedType => + (rt2.parents forall (tp1 <:< _)) && + (rt2.decls.toList forall tp1.specializes) + case et2: ExistentialType => + et2.withTypeVars(tp1 <:< _, depth) || fourthTry + case nn2: NotNullType => + tp1.isNotNull && tp1 <:< nn2.underlying + case mt2: MethodType => + tp1 match { + case mt1 @ MethodType(params1, res1) => + val params2 = mt2.params + val res2 = mt2.resultType + (sameLength(params1, params2) && + matchingParams(params1, params2, mt1.isJava, mt2.isJava) && + (res1 <:< res2) && + mt1.isImplicit == mt2.isImplicit) + // TODO: if mt1.params.isEmpty, consider NullaryMethodType? + case _ => + false + } + case pt2 @ NullaryMethodType(_) => + tp1 match { + // TODO: consider MethodType mt for which mt.params.isEmpty?? + case pt1 @ NullaryMethodType(_) => + pt1.resultType <:< pt2.resultType + case _ => + false + } + case TypeBounds(lo2, hi2) => + tp1 match { + case TypeBounds(lo1, hi1) => + lo2 <:< lo1 && hi1 <:< hi2 + case _ => + false + } + case _ => + fourthTry + } + + /** Fourth try, on the left: + * - handle typerefs, refined types, notnull and singleton types. + */ + def fourthTry = tp1 match { + case tr1 @ TypeRef(_, sym1, _) => + sym1 match { + case NothingClass => true + case NullClass => + tp2 match { + case TypeRef(_, sym2, _) => + sym2.isClass && (sym2 isNonBottomSubClass ObjectClass) && + !(tp2.normalize.typeSymbol isNonBottomSubClass NotNullClass) + case _ => + isSingleType(tp2) && tp1 <:< tp2.widen + } + case _: ClassSymbol => + if (isRaw(sym1, tr1.args)) + isSubType(rawToExistential(tp1), tp2, depth) + else + sym1.name == tpnme.REFINE_CLASS_NAME && + isSubType(sym1.info, tp2, depth) + case _: TypeSymbol => + if (sym1 hasFlag DEFERRED) { + val tp1a = tp1.bounds.hi + isDifferentTypeConstructor(tp1, tp1a) && tp1a <:< tp2 + } else { + isSubType(tp1.normalize, tp2.normalize, depth) + } + case _ => + false + } + case RefinedType(parents1, _) => + parents1 exists (_ <:< tp2) + case _: SingletonType | _: NotNullType => + tp1.underlying <:< tp2 + case _ => + false + } + + firstTry + } + + /** Are `tps1' and `tps2' lists of equal length such + * that all elements of `tps1' conform to corresponding elements + * of `tps2'? + */ + def isSubTypes(tps1: List[Type], tps2: List[Type]): Boolean = (tps1 corresponds tps2)(_ <:< _) + + /** Does type `tp' implement symbol `sym' with same or + * stronger type? Exact only if `sym' is a member of some + * refinement type, otherwise we might return false negatives. + */ + def specializesSym(tp: Type, sym: Symbol): Boolean = + tp.typeSymbol == NothingClass || + tp.typeSymbol == NullClass && (sym.owner isSubClass ObjectClass) || + (tp.nonPrivateMember(sym.name).alternatives exists + (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym))) + + /** Does member `sym1' of `tp1' have a stronger type + * than member `sym2' of `tp2'? + */ + private def specializesSym(tp1: Type, sym1: Symbol, tp2: Type, sym2: Symbol): Boolean = { + val info1 = tp1.memberInfo(sym1) + val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1) + //System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG + sym2.isTerm && (info1 <:< info2) /*&& (!sym2.isStable || sym1.isStable) */ || + sym2.isAbstractType && { + val memberTp1 = tp1.memberType(sym1) + // println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner))) + info2.bounds.containsType(memberTp1) && + kindsConform(List(sym2), List(memberTp1), tp1, sym1.owner) + } || + sym2.isAliasType && tp2.memberType(sym2).substThis(tp2.typeSymbol, tp1) =:= tp1.memberType(sym1) //@MAT ok + } + + /** A function implementing `tp1' matches `tp2' */ + final def matchesType(tp1: Type, tp2: Type, alwaysMatchSimple: Boolean): Boolean = { + def matchesQuantified(tparams1: List[Symbol], tparams2: List[Symbol], res1: Type, res2: Type): Boolean = ( + sameLength(tparams1, tparams2) && + matchesType(res1, res2.substSym(tparams2, tparams1), alwaysMatchSimple) + ) + def lastTry = + tp2 match { + case ExistentialType(_, res2) if alwaysMatchSimple => + matchesType(tp1, res2, true) + case MethodType(_, _) => + false + case PolyType(tparams2, res2) => + tparams2.isEmpty && matchesType(tp1, res2, alwaysMatchSimple) + case _ => + alwaysMatchSimple || tp1 =:= tp2 + } + tp1 match { + case mt1 @ MethodType(params1, res1) => + tp2 match { + case mt2 @ MethodType(params2, res2) => + sameLength(params1, params2) && // useful pre-screening optimization + matchingParams(params1, params2, mt1.isJava, mt2.isJava) && + matchesType(res1, res2, alwaysMatchSimple) && + mt1.isImplicit == mt2.isImplicit + case NullaryMethodType(res2) => + if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple) + else matchesType(tp1, res2, alwaysMatchSimple) + case ExistentialType(_, res2) => + alwaysMatchSimple && matchesType(tp1, res2, true) + case _ => + false + } + case mt1 @ NullaryMethodType(res1) => + tp2 match { + case mt2 @ MethodType(Nil, res2) => // could never match if params nonEmpty, and !mt2.isImplicit is implied by empty param list + matchesType(res1, res2, alwaysMatchSimple) + case NullaryMethodType(res2) => + matchesType(res1, res2, alwaysMatchSimple) + case ExistentialType(_, res2) => + alwaysMatchSimple && matchesType(tp1, res2, true) + case _ => + matchesType(res1, tp2, alwaysMatchSimple) + } + case PolyType(tparams1, res1) => + tp2 match { + case PolyType(tparams2, res2) => + matchesQuantified(tparams1, tparams2, res1, res2) + case ExistentialType(_, res2) => + alwaysMatchSimple && matchesType(tp1, res2, true) + case _ => + false // remember that tparams1.nonEmpty is now an invariant of PolyType + } + case ExistentialType(tparams1, res1) => + tp2 match { + case ExistentialType(tparams2, res2) => + matchesQuantified(tparams1, tparams2, res1, res2) + case _ => + if (alwaysMatchSimple) matchesType(res1, tp2, true) + else lastTry + } + case _ => + lastTry + } + } + +/** matchesType above is an optimized version of the following implementation: + + def matchesType2(tp1: Type, tp2: Type, alwaysMatchSimple: Boolean): Boolean = { + def matchesQuantified(tparams1: List[Symbol], tparams2: List[Symbol], res1: Type, res2: Type): Boolean = + tparams1.length == tparams2.length && + matchesType(res1, res2.substSym(tparams2, tparams1), alwaysMatchSimple) + (tp1, tp2) match { + case (MethodType(params1, res1), MethodType(params2, res2)) => + params1.length == params2.length && // useful pre-secreening optimization + matchingParams(params1, params2, tp1.isInstanceOf[JavaMethodType], tp2.isInstanceOf[JavaMethodType]) && + matchesType(res1, res2, alwaysMatchSimple) && + tp1.isImplicit == tp2.isImplicit + case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => + matchesQuantified(tparams1, tparams2, res1, res2) + case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => + matchesType(rtp1, rtp2, alwaysMatchSimple) + case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => + matchesType(rtp1, rtp2, alwaysMatchSimple) + case (ExistentialType(tparams1, res1), ExistentialType(tparams2, res2)) => + matchesQuantified(tparams1, tparams2, res1, res2) + case (ExistentialType(_, res1), _) if alwaysMatchSimple => + matchesType(res1, tp2, alwaysMatchSimple) + case (_, ExistentialType(_, res2)) if alwaysMatchSimple => + matchesType(tp1, res2, alwaysMatchSimple) + case (NullaryMethodType(rtp1), _) => + matchesType(rtp1, tp2, alwaysMatchSimple) + case (_, NullaryMethodType(rtp2)) => + matchesType(tp1, rtp2, alwaysMatchSimple) + case (MethodType(_, _), _) => false + case (PolyType(_, _), _) => false + case (_, MethodType(_, _)) => false + case (_, PolyType(_, _)) => false + case _ => + alwaysMatchSimple || tp1 =:= tp2 + } + } +*/ + + /** Are `syms1' and `syms2' parameter lists with pairwise equivalent types? */ + private def matchingParams(syms1: List[Symbol], syms2: List[Symbol], syms1isJava: Boolean, syms2isJava: Boolean): Boolean = syms1 match { + case Nil => + syms2.isEmpty + case sym1 :: rest1 => + syms2 match { + case Nil => + false + case sym2 :: rest2 => + val tp1 = sym1.tpe + val tp2 = sym2.tpe + (tp1 =:= tp2 || + syms1isJava && tp2.typeSymbol == ObjectClass && tp1.typeSymbol == AnyClass || + syms2isJava && tp1.typeSymbol == ObjectClass && tp2.typeSymbol == AnyClass) && + matchingParams(rest1, rest2, syms1isJava, syms2isJava) + } + } + + /** like map2, but returns list `xs' itself - instead of a copy - if function + * `f' maps all elements to themselves. + */ + def map2Conserve[A <: AnyRef, B](xs: List[A], ys: List[B])(f: (A, B) => A): List[A] = + if (xs.isEmpty) xs + else { + val x1 = f(xs.head, ys.head) + val xs1 = map2Conserve(xs.tail, ys.tail)(f) + if ((x1 eq xs.head) && (xs1 eq xs.tail)) xs + else x1 :: xs1 + } + + /** Solve constraint collected in types `tvars'. + * + * @param tvars All type variables to be instantiated. + * @param tparams The type parameters corresponding to `tvars' + * @param variances The variances of type parameters; need to reverse + * solution direction for all contravariant variables. + * @param upper When `true' search for max solution else min. + */ + def solve(tvars: List[TypeVar], tparams: List[Symbol], + variances: List[Int], upper: Boolean): Boolean = + solve(tvars, tparams, variances, upper, AnyDepth) + + def solve(tvars: List[TypeVar], tparams: List[Symbol], + variances: List[Int], upper: Boolean, depth: Int): Boolean = { + val config = tvars zip (tparams zip variances) + + def solveOne(tvar: TypeVar, tparam: Symbol, variance: Int) { + if (tvar.constr.inst == NoType) { + val up = if (variance != CONTRAVARIANT) upper else !upper + tvar.constr.inst = null + val bound: Type = if (up) tparam.info.bounds.hi else tparam.info.bounds.lo + //Console.println("solveOne0(tv, tp, v, b)="+(tvar, tparam, variance, bound)) + var cyclic = bound contains tparam + for ((tvar2, (tparam2, variance2)) <- config) { + if (tparam2 != tparam && + ((bound contains tparam2) || + up && (tparam2.info.bounds.lo =:= tparam.tpe) || + !up && (tparam2.info.bounds.hi =:= tparam.tpe))) { + if (tvar2.constr.inst eq null) cyclic = true + solveOne(tvar2, tparam2, variance2) + } + } + if (!cyclic) { + if (up) { + if (bound.typeSymbol != AnyClass) + tvar addHiBound bound.instantiateTypeParams(tparams, tvars) + for (tparam2 <- tparams) + tparam2.info.bounds.lo.dealias match { + case TypeRef(_, `tparam`, _) => + tvar addHiBound tparam2.tpe.instantiateTypeParams(tparams, tvars) + case _ => + } + } else { + if (bound.typeSymbol != NothingClass && bound.typeSymbol != tparam) { + tvar addLoBound bound.instantiateTypeParams(tparams, tvars) + } + for (tparam2 <- tparams) + tparam2.info.bounds.hi.dealias match { + case TypeRef(_, `tparam`, _) => + tvar addLoBound tparam2.tpe.instantiateTypeParams(tparams, tvars) + case _ => + } + } + } + tvar.constr.inst = NoType // necessary because hibounds/lobounds may contain tvar + + //println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds)+((if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds) map (_.widen))) + + tvar setInst ( + if (up) { + if (depth != AnyDepth) glb(tvar.constr.hiBounds, depth) else glb(tvar.constr.hiBounds) + } else { + if (depth != AnyDepth) lub(tvar.constr.loBounds, depth) else lub(tvar.constr.loBounds) + }) + + //Console.println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds)+((if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds) map (_.widen))+" = "+tvar.constr.inst)//@MDEBUG + } + } + + // println("solving "+tvars+"/"+tparams+"/"+(tparams map (_.info))) + for ((tvar, (tparam, variance)) <- config) + solveOne(tvar, tparam, variance) + + tvars forall (tvar => tvar.constr.isWithinBounds(tvar.constr.inst)) + } + + /** Do type arguments `targs' conform to formal parameters + * `tparams'? + * + * @param tparams ... + * @param targs ... + * @return ... + */ + def isWithinBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): Boolean = { + var bounds = instantiatedBounds(pre, owner, tparams, targs) + if (targs.exists(_.annotations.nonEmpty)) + bounds = adaptBoundsToAnnotations(bounds, tparams, targs) + (bounds corresponds targs)(_ containsType _) + } + + def instantiatedBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = + tparams map (_.info.asSeenFrom(pre, owner).instantiateTypeParams(tparams, targs).bounds) + +// Lubs and Glbs --------------------------------------------------------- + + /** The least sorted upwards closed upper bound of a non-empty list + * of lists of types relative to the following ordering <= between lists of types: + * + * xs <= ys iff forall y in ys exists x in xs such that x <: y + * + * @See baseTypeSeq for a definition of sorted and upwards closed. + */ + private def lubList(tss: List[List[Type]], depth: Int): List[Type] = + if (tss.tail.isEmpty) tss.head + else if (tss exists (_.isEmpty)) List() + else { + val ts0 = tss map (_.head) + val sym = minSym(ts0) + if (ts0 forall (_.typeSymbol == sym)) + mergePrefixAndArgs(elimSub(ts0, depth), 1, depth).toList ::: lubList(tss map (_.tail), depth) + else + lubList(tss map (ts => if (ts.head.typeSymbol == sym) ts.tail else ts), depth) + } + + private def lubBaseTypeSeq(tss: List[BaseTypeSeq], depth: Int): List[Type] = + lubList(tss map (_.toList), depth) + + /** The minimal symbol (wrt Symbol.isLess) of a list of types */ + private def minSym(tps: List[Type]): Symbol = + (tps.head.typeSymbol /: tps.tail) { + (sym1, tp2) => if (tp2.typeSymbol isLess sym1) tp2.typeSymbol else sym1 + } + + /** A minimal type list which has a given list of types as its base type sequence */ + def spanningTypes(ts: List[Type]): List[Type] = ts match { + case List() => List() + case first :: rest => + first :: spanningTypes( + rest filter (t => !first.typeSymbol.isSubClass(t.typeSymbol))) + } + + /** Eliminate from list of types all elements which are a supertype + * of some other element of the list. */ + private def elimSuper(ts: List[Type]): List[Type] = ts match { + case List() => List() + case t :: ts1 => + val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) + if (rest exists (t1 => t1 <:< t)) rest else t :: rest + } + + /** A collector that tests for existential types appearing at given variance in a type */ + class ContainsVariantExistentialCollector(v: Int) extends TypeCollector(false) { + def traverse(tp: Type) = tp match { + case ExistentialType(_, _) if (variance == v) => result = true + case _ => mapOver(tp) + } + def init() = { + variance = 1 + this + } + } + + val containsCovariantExistentialCollector = new ContainsVariantExistentialCollector(1) + val containsContravariantExistentialCollector = new ContainsVariantExistentialCollector(-1) + + /** Eliminate from list of types all elements which are a subtype + * of some other element of the list. */ + private def elimSub(ts: List[Type], depth: Int): List[Type] = { + def elimAnonymousClass(t: Type) = t match { + case TypeRef(pre, clazz, List()) if clazz.isAnonymousClass => + clazz.classBound.asSeenFrom(pre, clazz.owner) + case _ => + t + } + def elimSub0(ts: List[Type]): List[Type] = ts match { + case List() => List() + case t :: ts1 => + val rest = elimSub0(ts1 filter (t1 => !isSubType(t1, t, decr(depth)))) + if (rest exists (t1 => isSubType(t, t1, decr(depth)))) rest else t :: rest + } + val ts0 = elimSub0(ts) + if (ts0.isEmpty || ts0.tail.isEmpty) ts0 + else { + val ts1 = ts0 mapConserve (t => elimAnonymousClass(t.underlying)) + if (ts1 eq ts0) ts0 + else elimSub(ts1, depth) + } + } + + private def stripExistentialsAndTypeVars(ts: List[Type]): (List[Type], List[Symbol]) = { + val quantified = ts flatMap { + case ExistentialType(qs, _) => qs + case t => List() + } + def stripType(tp: Type) = tp match { + case ExistentialType(_, res) => + res + case TypeVar(_, constr) => + if (constr.instValid) constr.inst + else abort("trying to do lub/glb of typevar "+tp) + case t => t + } + val strippedTypes = ts mapConserve stripType + (strippedTypes, quantified) + } + + def weakLub(ts: List[Type]) = + if (ts.nonEmpty && (ts forall isNumericValueType)) (numericLub(ts), true) + else if (ts.nonEmpty && (ts exists (_.annotations.nonEmpty))) + (annotationsLub(lub(ts map (_.withoutAnnotations)), ts), true) + else (lub(ts), false) + + def weakGlb(ts: List[Type]) = { + if (ts.nonEmpty && (ts forall isNumericValueType)) { + val nglb = numericGlb(ts) + if (nglb != NoType) (nglb, true) + else (glb(ts), false) + } else if (ts.nonEmpty && (ts exists (_.annotations.nonEmpty))) { + (annotationsGlb(glb(ts map (_.withoutAnnotations)), ts), true) + } else (glb(ts), false) + } + + def numericLub(ts: List[Type]) = + ts reduceLeft ((t1, t2) => + if (isNumericSubType(t1, t2)) t2 + else if (isNumericSubType(t2, t1)) t1 + else IntClass.tpe) + + def numericGlb(ts: List[Type]) = + ts reduceLeft ((t1, t2) => + if (isNumericSubType(t1, t2)) t1 + else if (isNumericSubType(t2, t1)) t2 + else NoType) + + def isWeakSubType(tp1: Type, tp2: Type) = + tp1.deconst.normalize match { + case TypeRef(_, sym1, _) if isNumericValueClass(sym1) => + tp2.deconst.normalize match { + case TypeRef(_, sym2, _) if isNumericValueClass(sym2) => + isNumericSubClass(sym1, sym2) + case tv2 @ TypeVar(_, _) => + tv2.registerBound(tp1, isLowerBound = true, isNumericBound = true) + case _ => + isSubType(tp1, tp2) + } + case tv1 @ TypeVar(_, _) => + tp2.deconst.normalize match { + case TypeRef(_, sym2, _) if isNumericValueClass(sym2) => + tv1.registerBound(tp2, isLowerBound = false, isNumericBound = true) + case _ => + isSubType(tp1, tp2) + } + case _ => + isSubType(tp1, tp2) + } + + def isNumericSubType(tp1: Type, tp2: Type) = + isNumericValueType(tp1) && isNumericValueType(tp2) && + isNumericSubClass(tp1.typeSymbol, tp2.typeSymbol) + + private val lubResults = new mutable.HashMap[(Int, List[Type]), Type] + private val glbResults = new mutable.HashMap[(Int, List[Type]), Type] + + def lub(ts: List[Type]): Type = try { + lub(ts, lubDepth(ts)) + } finally { + lubResults.clear() + glbResults.clear() + } + + /** The least upper bound wrt <:< of a list of types */ + def lub(ts: List[Type], depth: Int): Type = { + def lub0(ts0: List[Type]): Type = elimSub(ts0, depth) match { + case List() => NothingClass.tpe + case List(t) => t + case ts @ PolyType(tparams, _) :: _ => + val tparams1 = (tparams, matchingBounds(ts, tparams).transpose).zipped map + ((tparam, bounds) => tparam.cloneSymbol.setInfo(glb(bounds, depth))) + PolyType(tparams1, lub0(matchingInstTypes(ts, tparams1))) + case ts @ MethodType(params, _) :: rest => + MethodType(params, lub0(matchingRestypes(ts, params map (_.tpe)))) + case ts @ NullaryMethodType(_) :: rest => + NullaryMethodType(lub0(matchingRestypes(ts, Nil))) + case ts @ TypeBounds(_, _) :: rest => + TypeBounds(glb(ts map (_.bounds.lo), depth), lub(ts map (_.bounds.hi), depth)) + case ts => + lubResults get (depth, ts) match { + case Some(lubType) => + lubType + case None => + lubResults((depth, ts)) = AnyClass.tpe + val res = if (depth < 0) AnyClass.tpe else lub1(ts) + lubResults((depth, ts)) = res + res + } + } + def lub1(ts0: List[Type]): Type = { + val (ts, tparams) = stripExistentialsAndTypeVars(ts0) + val bts: List[BaseTypeSeq] = ts map (_.baseTypeSeq) + val lubBaseTypes: List[Type] = lubBaseTypeSeq(bts, depth) + val lubParents = spanningTypes(lubBaseTypes) + val lubOwner = commonOwner(ts) + val lubBase = intersectionType(lubParents, lubOwner) + val lubType = + if (phase.erasedTypes || depth == 0) lubBase + else { + val lubRefined = refinedType(lubParents, lubOwner) + val lubThisType = lubRefined.typeSymbol.thisType + val narrowts = ts map (_.narrow) + def lubsym(proto: Symbol): Symbol = { + val prototp = lubThisType.memberInfo(proto) + val syms = narrowts map (t => + t.nonPrivateMember(proto.name).suchThat(sym => + sym.tpe matches prototp.substThis(lubThisType.typeSymbol, t))) + if (syms contains NoSymbol) NoSymbol + else { + val symtypes = + (narrowts, syms).zipped map ((t, sym) => t.memberInfo(sym).substThis(t.typeSymbol, lubThisType)) + if (proto.isTerm) // possible problem: owner of info is still the old one, instead of new refinement class + proto.cloneSymbol(lubRefined.typeSymbol).setInfoOwnerAdjusted(lub(symtypes, decr(depth))) + else if (symtypes.tail forall (symtypes.head =:=)) + proto.cloneSymbol(lubRefined.typeSymbol).setInfoOwnerAdjusted(symtypes.head) + else { + def lubBounds(bnds: List[TypeBounds]): TypeBounds = + TypeBounds(glb(bnds map (_.lo), decr(depth)), lub(bnds map (_.hi), decr(depth))) + lubRefined.typeSymbol.newAbstractType(proto.pos, proto.name.toTypeName) + .setInfoOwnerAdjusted(lubBounds(symtypes map (_.bounds))) + } + } + } + def refines(tp: Type, sym: Symbol): Boolean = { + val syms = tp.nonPrivateMember(sym.name).alternatives; + !syms.isEmpty && (syms forall (alt => + // todo alt != sym is strictly speaking not correct, but without it we lose + // efficiency. + alt != sym && !specializesSym(lubThisType, sym, tp, alt))) + } + for (sym <- lubBase.nonPrivateMembers) { + // add a refinement symbol for all non-class members of lubBase + // which are refined by every type in ts. + if (!sym.isClass && !sym.isConstructor && (narrowts forall (t => refines(t, sym)))) + try { + val lsym = lubsym(sym) + if (lsym != NoSymbol) addMember(lubThisType, lubRefined, lubsym(sym)) + } catch { + case ex: NoCommonType => + } + } + if (lubRefined.decls.isEmpty) lubBase + else { +// println("refined lub of "+ts+"/"+narrowts+" is "+lubRefined+", baseclasses = "+(ts map (_.baseTypeSeq) map (_.toList))) + lubRefined + } + } + existentialAbstraction(tparams, lubType) + } + if (printLubs) { + println(indent + "lub of " + ts + " at depth "+depth)//debug + indent = indent + " " + assert(indent.length <= 100) + } + val res = lub0(ts) + if (printLubs) { + indent = indent dropRight 2 + println(indent + "lub of " + ts + " is " + res)//debug + } + if (ts forall (_.isNotNull)) res.notNull else res + } + + val GlbFailure = new Throwable + + /** A global counter for glb calls in the `specializes' query connected to the `addMembers' + * call in `glb'. There's a possible infinite recursion when `specializes' calls + * memberType, which calls baseTypeSeq, which calls mergePrefixAndArgs, which calls glb. + * The counter breaks this recursion after two calls. + * If the recursion is broken, no member is added to the glb. + */ + private var globalGlbDepth = 0 + private final val globalGlbLimit = 2 + + def glb(ts: List[Type]): Type = try { + glb(ts, lubDepth(ts)) + } finally { + lubResults.clear() + glbResults.clear() + } + + /** The greatest lower bound wrt <:< of a list of types */ + private def glb(ts: List[Type], depth: Int): Type = { + def glb0(ts0: List[Type]): Type = elimSuper(ts0) match { + case List() => AnyClass.tpe + case List(t) => t + case ts @ PolyType(tparams, _) :: _ => + val tparams1 = (tparams, matchingBounds(ts, tparams).transpose).zipped map + ((tparam, bounds) => tparam.cloneSymbol.setInfo(lub(bounds, depth))) + PolyType(tparams1, glb0(matchingInstTypes(ts, tparams1))) + case ts @ MethodType(params, _) :: rest => + MethodType(params, glb0(matchingRestypes(ts, params map (_.tpe)))) + case ts @ NullaryMethodType(_) :: rest => + NullaryMethodType(glb0(matchingRestypes(ts, Nil))) + case ts @ TypeBounds(_, _) :: rest => + TypeBounds(lub(ts map (_.bounds.lo), depth), glb(ts map (_.bounds.hi), depth)) + case ts => + glbResults get (depth, ts) match { + case Some(glbType) => + glbType + case _ => + glbResults((depth, ts)) = NothingClass.tpe + val res = if (depth < 0) NothingClass.tpe else glb1(ts) + glbResults((depth, ts)) = res + res + } + } + def glb1(ts0: List[Type]): Type = { + try { + val (ts, tparams) = stripExistentialsAndTypeVars(ts0) + val glbOwner = commonOwner(ts) + def refinedToParents(t: Type): List[Type] = t match { + case RefinedType(ps, _) => ps flatMap refinedToParents + case _ => List(t) + } + def refinedToDecls(t: Type): List[Scope] = t match { + case RefinedType(ps, decls) => + val dss = ps flatMap refinedToDecls + if (decls.isEmpty) dss else decls :: dss + case _ => List() + } + val ts1 = ts flatMap refinedToParents + val glbBase = intersectionType(ts1, glbOwner) + val glbType = + if (phase.erasedTypes || depth == 0) glbBase + else { + val glbRefined = refinedType(ts1, glbOwner) + val glbThisType = glbRefined.typeSymbol.thisType + def glbsym(proto: Symbol): Symbol = { + val prototp = glbThisType.memberInfo(proto) + val syms = for (t <- ts; + alt <- (t.nonPrivateMember(proto.name).alternatives); + if glbThisType.memberInfo(alt) matches prototp + ) yield alt + val symtypes = syms map glbThisType.memberInfo + assert(!symtypes.isEmpty) + proto.cloneSymbol(glbRefined.typeSymbol).setInfoOwnerAdjusted( + if (proto.isTerm) glb(symtypes, decr(depth)) + else { + def isTypeBound(tp: Type) = tp match { + case TypeBounds(_, _) => true + case _ => false + } + def glbBounds(bnds: List[Type]): TypeBounds = { + val lo = lub(bnds map (_.bounds.lo), decr(depth)) + val hi = glb(bnds map (_.bounds.hi), decr(depth)) + if (lo <:< hi) TypeBounds(lo, hi) + else throw GlbFailure + } + val symbounds = symtypes filter isTypeBound + var result: Type = + if (symbounds.isEmpty) + TypeBounds.empty + else glbBounds(symbounds) + for (t <- symtypes if !isTypeBound(t)) + if (result.bounds containsType t) result = t + else throw GlbFailure + result + }) + } + if (globalGlbDepth < globalGlbLimit) + try { + globalGlbDepth += 1 + val dss = ts flatMap refinedToDecls + for (ds <- dss; val sym <- ds.iterator) + if (globalGlbDepth < globalGlbLimit && !(glbThisType specializes sym)) + try { + addMember(glbThisType, glbRefined, glbsym(sym)) + } catch { + case ex: NoCommonType => + } + } finally { + globalGlbDepth -= 1 + } + if (glbRefined.decls.isEmpty) glbBase else glbRefined + } + existentialAbstraction(tparams, glbType) + } catch { + case GlbFailure => + if (ts forall (t => NullClass.tpe <:< t)) NullClass.tpe + else NothingClass.tpe + } + } + // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG + + val res = glb0(ts) + + // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG + + if (ts exists (_.isNotNull)) res.notNull else res + } + + /** The most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given type. + */ + private def commonOwner(t: Type): Symbol = { + commonOwnerMap.init + commonOwnerMap.apply(t) + commonOwnerMap.result + } + + /** The most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given list + * of types. + */ + private def commonOwner(tps: List[Type]): Symbol = { + // if (settings.debug.value) log("computing common owner of types " + tps)//DEBUG + commonOwnerMap.init + tps foreach { tp => commonOwnerMap.apply(tp); () } + commonOwnerMap.result + } + + /** Compute lub (if variance == 1) or glb (if variance == -1) of given list + * of types `tps'. All types in `tps' are typerefs or singletypes + * with the same symbol. + * Return `Some(x)' if the computation succeeds with result `x'. + * Return `None' if the computation fails. + */ + def mergePrefixAndArgs(tps: List[Type], variance: Int, depth: Int): Option[Type] = tps match { + case List(tp) => + Some(tp) + case TypeRef(_, sym, _) :: rest => + val pres = tps map (_.prefix) // prefix normalizes automatically + val pre = if (variance == 1) lub(pres, depth) else glb(pres, depth) + val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments + val capturedParams = new ListBuffer[Symbol] + try { + if (sym == ArrayClass && phase.erasedTypes) { + // special treatment for lubs of array types after erasure: + // if argss contain one value type and some other type, the lub is Object + // if argss contain several reference types, the lub is an array over lub of argtypes + if (argss exists (_.isEmpty)) { + None // something is wrong: an array without a type arg. + } else { + val args = argss map (_.head) + if (args.tail forall (_ =:= args.head)) Some(typeRef(pre, sym, List(args.head))) + else if (args exists (arg => isValueClass(arg.typeSymbol))) Some(ObjectClass.tpe) + else Some(typeRef(pre, sym, List(lub(args)))) + } + } else { + val args = (sym.typeParams, argss.transpose).zipped map { + (tparam, as) => + if (depth == 0) + if (tparam.variance == variance) AnyClass.tpe + else if (tparam.variance == -variance) NothingClass.tpe + else NoType + else + if (tparam.variance == variance) lub(as, decr(depth)) + else if (tparam.variance == -variance) glb(as, decr(depth)) + else { + val l = lub(as, decr(depth)) + val g = glb(as, decr(depth)) + if (l <:< g) l + else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we + // just err on the conservative side, i.e. with a bound that is too high. + // if(!(tparam.info.bounds contains tparam)){ //@M can't deal with f-bounds, see #2251 + + val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) + capturedParams += qvar + qvar.tpe + } + } + } + if (args contains NoType) None + else Some(existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))) + } + } catch { + case ex: MalformedType => None + case ex: IndexOutOfBoundsException => // transpose freaked out because of irregular argss + // catching just in case (shouldn't happen, but also doesn't cost us) + if (settings.debug.value) log("transposed irregular matrix!?"+ (tps, argss)) + None + } + case SingleType(_, sym) :: rest => + val pres = tps map (_.prefix) + val pre = if (variance == 1) lub(pres, depth) else glb(pres, depth) + try { + Some(singleType(pre, sym)) + } catch { + case ex: MalformedType => None + } + case ExistentialType(tparams, quantified) :: rest => + mergePrefixAndArgs(quantified :: rest, variance, depth) map (existentialAbstraction(tparams, _)) + case _ => + assert(false, tps); None + } + + /** Make symbol `sym' a member of scope `tp.decls' + * where `thistp' is the narrowed owner type of the scope. + */ + def addMember(thistp: Type, tp: Type, sym: Symbol) { + assert(sym != NoSymbol) + // if (settings.debug.value) log("add member " + sym+":"+sym.info+" to "+thistp) //DEBUG + if (!(thistp specializes sym)) { + if (sym.isTerm) + for (alt <- tp.nonPrivateDecl(sym.name).alternatives) + if (specializesSym(thistp, sym, thistp, alt)) + tp.decls unlink alt; + tp.decls enter sym + } + } + + /** All types in list must be polytypes with type parameter lists of + * same length as tparams. + * Returns list of list of bounds infos, where corresponding type + * parameters are renamed to tparams. + */ + private def matchingBounds(tps: List[Type], tparams: List[Symbol]): List[List[Type]] = + tps map { + case PolyType(tparams1, _) if sameLength(tparams1, tparams) => + tparams1 map (tparam => tparam.info.substSym(tparams1, tparams)) + case _ => + throw new NoCommonType(tps) + } + + /** All types in list must be polytypes with type parameter lists of + * same length as tparams. + * Returns list of instance types, where corresponding type + * parameters are renamed to tparams. + */ + private def matchingInstTypes(tps: List[Type], tparams: List[Symbol]): List[Type] = + tps map { + case PolyType(tparams1, restpe) if sameLength(tparams1, tparams) => + restpe.substSym(tparams1, tparams) + case _ => + throw new NoCommonType(tps) + } + + /** All types in list must be method types with equal parameter types. + * Returns list of their result types. + */ + private def matchingRestypes(tps: List[Type], pts: List[Type]): List[Type] = + tps map { + case MethodType(params1, res) if (isSameTypes(params1 map (_.tpe), pts)) => + res + case NullaryMethodType(res) if pts isEmpty => + res + case _ => + throw new NoCommonType(tps) + } + + +// TODO: this desperately needs to be cleaned up +// plan: split into kind inference and subkinding +// every Type has a (cached) Kind + def kindsConform(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): Boolean = checkKindBounds0(tparams, targs, pre, owner, false).isEmpty + + /** Check well-kindedness of type application (assumes arities are already checked) -- @M + * + * This check is also performed when abstract type members become concrete (aka a "type alias") -- then tparams.length==1 + * (checked one type member at a time -- in that case, prefix is the name of the type alias) + * + * Type application is just like value application: it's "contravariant" in the sense that + * the type parameters of the supplied type arguments must conform to the type parameters of + * the required type parameters: + * - their bounds must be less strict + * - variances must match (here, variances are absolute, the variance of a type parameter does not influence the variance of its higher-order parameters) + * - @M TODO: are these conditions correct,sufficient&necessary? + * + * e.g. class Iterable[t, m[+x <: t]] --> the application Iterable[Int, List] is okay, since + * List's type parameter is also covariant and its bounds are weaker than <: Int + */ + def checkKindBounds0(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol, explainErrors: Boolean): List[(Type, Symbol, List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)])] = { + var error = false + + def transform(tp: Type, clazz: Symbol): Type = tp.asSeenFrom(pre, clazz) // instantiate type params that come from outside the abstract type we're currently checking + def transformedBounds(p: Symbol, o: Symbol) = transform(p.info.instantiateTypeParams(tparams, targs).bounds, o) + + /** Check whether <arg>sym1</arg>'s variance conforms to <arg>sym2</arg>'s variance + * + * If <arg>sym2</arg> is invariant, <arg>sym1</arg>'s variance is irrelevant. Otherwise they must be equal. + */ + def variancesMatch(sym1: Symbol, sym2: Symbol): Boolean = (sym2.variance==0 || sym1.variance==sym2.variance) + + // check that the type parameters <arg>hkargs</arg> to a higher-kinded type conform to the expected params <arg>hkparams</arg> + def checkKindBoundsHK(hkargs: List[Symbol], arg: Symbol, param: Symbol, paramowner: Symbol, underHKParams: List[Symbol], withHKArgs: List[Symbol]): (List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)]) = { + def bindHKParams(tp: Type) = tp.substSym(underHKParams, withHKArgs) + // @M sometimes hkargs != arg.typeParams, the symbol and the type may have very different type parameters + val hkparams = param.typeParams + + if (settings.debug.value) { + log("checkKindBoundsHK expected: "+ param +" with params "+ hkparams +" by definition in "+ paramowner) + log("checkKindBoundsHK supplied: "+ arg +" with params "+ hkargs +" from "+ owner) + log("checkKindBoundsHK under params: "+ underHKParams +" with args "+ withHKArgs) + } + + if (!sameLength(hkargs, hkparams)) { + if(arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded + else {error = true; (List((arg, param)), Nil, Nil)} // shortcut: always set error, whether explainTypesOrNot + } else { + val _arityMismatches = if(explainErrors) new ListBuffer[(Symbol, Symbol)] else null + val _varianceMismatches = if(explainErrors) new ListBuffer[(Symbol, Symbol)] else null + val _stricterBounds = if(explainErrors)new ListBuffer[(Symbol, Symbol)] else null + def varianceMismatch(a: Symbol, p: Symbol) { if(explainErrors) _varianceMismatches += ((a, p)) else error = true} + def stricterBound(a: Symbol, p: Symbol) { if(explainErrors) _stricterBounds += ((a, p)) else error = true } + def arityMismatches(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _arityMismatches ++= as } + def varianceMismatches(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _varianceMismatches ++= as } + def stricterBounds(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _stricterBounds ++= as } + + for ((hkarg, hkparam) <- hkargs zip hkparams) { + if (hkparam.typeParams.isEmpty && hkarg.typeParams.isEmpty) { // base-case: kind * + if (!variancesMatch(hkarg, hkparam)) + varianceMismatch(hkarg, hkparam) + + // instantiateTypeParams(tparams, targs) --> higher-order bounds may contain references to type arguments + // substSym(hkparams, hkargs) --> these types are going to be compared as types of kind * + // --> their arguments use different symbols, but are conceptually the same + // (could also replace the types by polytypes, but can't just strip the symbols, as ordering is lost then) + if (!(bindHKParams(transformedBounds(hkparam, paramowner)) <:< transform(hkarg.info.bounds, owner))) + stricterBound(hkarg, hkparam) + + if (settings.debug.value) { + log("checkKindBoundsHK base case: "+ hkparam +" declared bounds: "+ transformedBounds(hkparam, paramowner) +" after instantiating earlier hkparams: "+ bindHKParams(transformedBounds(hkparam, paramowner))) + log("checkKindBoundsHK base case: "+ hkarg +" has bounds: "+ transform(hkarg.info.bounds, owner)) + } + } else { + if(settings.debug.value) log("checkKindBoundsHK recursing to compare params of "+ hkparam +" with "+ hkarg) + val (am, vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkarg, hkparam, paramowner, underHKParams ++ hkparam.typeParams, withHKArgs ++ hkarg.typeParams) + arityMismatches(am) + varianceMismatches(vm) + stricterBounds(sb) + } + if(!explainErrors && error) return (Nil, Nil, Nil) // stop as soon as we encountered an error + } + if(!explainErrors) (Nil, Nil, Nil) + else (_arityMismatches.toList, _varianceMismatches.toList, _stricterBounds.toList) + } + } + + val errors = new ListBuffer[(Type, Symbol, List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)])] + (tparams zip targs).foreach{ case (tparam, targ) if (targ.isHigherKinded || !tparam.typeParams.isEmpty) => + // @M must use the typeParams of the type targ, not the typeParams of the symbol of targ!! + targ.typeSymbolDirect.info // force symbol load for #4205 + val tparamsHO = targ.typeParams + + val (arityMismatches, varianceMismatches, stricterBounds) = + checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) // NOTE: *not* targ.typeSymbol, which normalizes + + if(!explainErrors) {if(error) return List((NoType, NoSymbol, Nil, Nil, Nil))} + else if (arityMismatches.nonEmpty || varianceMismatches.nonEmpty || stricterBounds.nonEmpty) { + errors += ((targ, tparam, arityMismatches, varianceMismatches, stricterBounds)) + } + // case (tparam, targ) => println("no check: "+(tparam, targ, tparam.typeParams.isEmpty)) + case _ => + } + + errors.toList + } + +// Errors and Diagnostics ----------------------------------------------------- + + /** A throwable signalling a type error */ + class TypeError(var pos: Position, val msg: String) extends Throwable(msg) { + def this(msg: String) = this(NoPosition, msg) + } + + class NoCommonType(tps: List[Type]) extends Throwable( + "lub/glb of incompatible types: " + tps.mkString("", " and ", "")) with ControlThrowable + + /** A throwable signalling a malformed type */ + class MalformedType(msg: String) extends TypeError(msg) { + def this(pre: Type, tp: String) = this("malformed type: " + pre + "#" + tp) + } + + /** An exception signalling a variance annotation/usage conflict */ + class VarianceError(msg: String) extends TypeError(msg) + + /** The current indentation string for traces */ + private var indent: String = "" + + /** Perform operation `p' on arguments `tp1', + * `arg2' and print trace of computation. + */ + private def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { + Console.println(indent + tp1 + " " + op + " " + arg2 + "?" /* + "("+tp1.getClass+","+arg2.asInstanceOf[AnyRef].getClass+")"*/) + indent = indent + " " + val result = p(tp1, arg2) + indent = indent dropRight 2 + Console.println(indent + result) + result + } + + /** If option `explaintypes' is set, print a subtype trace for + * `found <:< required'. + */ + def explainTypes(found: Type, required: Type) { + if (settings.explaintypes.value) withTypesExplained(found <:< required) + } + + /** If option `explaintypes' is set, print a subtype trace for + * `op(found, required)'. + */ + def explainTypes(op: (Type, Type) => Any, found: Type, required: Type) { + if (settings.explaintypes.value) withTypesExplained(op(found, required)) + } + + /** Execute `op' while printing a trace of the operations on types executed. + */ + def withTypesExplained[A](op: => A): A = { + val s = explainSwitch + try { explainSwitch = true; op } finally { explainSwitch = s } + } + + def objToAny(tp: Type): Type = + if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe + else tp + + val shorthands = Set( + "scala.collection.immutable.List", + "scala.collection.immutable.Nil", + "scala.collection.Seq", + "scala.collection.Traversable", + "scala.collection.Iterable", + "scala.collection.mutable.StringBuilder", + "scala.collection.IndexedSeq", + "scala.collection.Iterator") + + + /** The maximum number of recursions allowed in toString + */ + final val maxTostringRecursions = 50 + + private var tostringRecursions = 0 +} diff --git a/src/compiler/scala/reflect/common/pickling/UnPickler.scala b/src/compiler/scala/reflect/common/pickling/UnPickler.scala new file mode 100644 index 0000000000..6b0f933f9b --- /dev/null +++ b/src/compiler/scala/reflect/common/pickling/UnPickler.scala @@ -0,0 +1,841 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package common +package pickling + +import java.io.IOException +import java.lang.Float.intBitsToFloat +import java.lang.Double.longBitsToDouble + +import Flags._ +import PickleFormat._ +import collection.mutable.{HashMap, ListBuffer} +import annotation.switch + +/** @author Martin Odersky + * @version 1.0 + */ +abstract class UnPickler /*extends reflect.generic.UnPickler*/ { + val global: SymbolTable + import global._ + + /** Unpickle symbol table information descending from a class and/or module root + * from an array of bytes. + * @param bytes bytearray from which we unpickle + * @param offset offset from which unpickling starts + * @param classroot the top-level class which is unpickled, or NoSymbol if inapplicable + * @param moduleroot the top-level module which is unpickled, or NoSymbol if inapplicable + * @param filename filename associated with bytearray, only used for error messages + */ + def unpickle(bytes: Array[Byte], offset: Int, classRoot: Symbol, moduleRoot: Symbol, filename: String) { + try { + new Scan(bytes, offset, classRoot, moduleRoot, filename).run() + } catch { + case ex: IOException => + throw ex + case ex: Throwable => + /*if (settings.debug.value)*/ ex.printStackTrace() + throw new RuntimeException("error reading Scala signature of "+filename+": "+ex.getMessage()) + } + } + + class Scan(bytes: Array[Byte], offset: Int, classRoot: Symbol, moduleRoot: Symbol, filename: String) extends PickleBuffer(bytes, offset, -1) { + //println("unpickle " + classRoot + " and " + moduleRoot)//debug + + protected def debug = settings.debug.value + + checkVersion() + + /** A map from entry numbers to array offsets */ + private val index = createIndex + + /** A map from entry numbers to symbols, types, or annotations */ + private val entries = new Array[AnyRef](index.length) + + /** A map from symbols to their associated `decls' scopes */ + private val symScopes = new HashMap[Symbol, Scope] + + //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + + def run() { + // read children last, fix for #3951 + val queue = new collection.mutable.ListBuffer[() => Unit]() + def delay(i: Int, action: => Unit) { + queue += (() => at(i, {() => action; null})) + } + + for (i <- 0 until index.length) { + if (isSymbolEntry(i)) + at(i, readSymbol) + else if (isSymbolAnnotationEntry(i)) + delay(i, readSymbolAnnotation()) + else if (isChildrenEntry(i)) + delay(i, readChildren()) + } + + for (action <- queue) + action() + } + + private def checkVersion() { + val major = readNat() + val minor = readNat() + if (major != MajorVersion || minor > MinorVersion) + throw new IOException("Scala signature " + classRoot.decodedName + + " has wrong version\n expected: " + + MajorVersion + "." + MinorVersion + + "\n found: " + major + "." + minor + + " in "+filename) + } + + /** The `decls' scope associated with given symbol */ + protected def symScope(sym: Symbol) = symScopes.getOrElseUpdate(sym, newScope) + + /** Does entry represent an (internal) symbol */ + protected def isSymbolEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + (firstSymTag <= tag && tag <= lastSymTag && + (tag != CLASSsym || !isRefinementSymbolEntry(i))) + } + + /** Does entry represent an (internal or external) symbol */ + protected def isSymbolRef(i: Int): Boolean = { + val tag = bytes(index(i)) + (firstSymTag <= tag && tag <= lastExtSymTag) + } + + /** Does entry represent a name? */ + protected def isNameEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + tag == TERMname || tag == TYPEname + } + + /** Does entry represent a symbol annotation? */ + protected def isSymbolAnnotationEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + tag == SYMANNOT + } + + /** Does the entry represent children of a symbol? */ + protected def isChildrenEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + tag == CHILDREN + } + + /** Does entry represent a refinement symbol? + * pre: Entry is a class symbol + */ + protected def isRefinementSymbolEntry(i: Int): Boolean = { + val savedIndex = readIndex + readIndex = index(i) + val tag = readByte().toInt + assert(tag == CLASSsym) + + readNat(); // read length + val result = readNameRef() == tpnme.REFINE_CLASS_NAME + readIndex = savedIndex + result + } + + /** If entry at <code>i</code> is undefined, define it by performing + * operation <code>op</code> with <code>readIndex at start of i'th + * entry. Restore <code>readIndex</code> afterwards. + */ + protected def at[T <: AnyRef](i: Int, op: () => T): T = { + var r = entries(i) + if (r eq null) { + val savedIndex = readIndex + readIndex = index(i) + r = op() + assert(entries(i) eq null, entries(i)) + entries(i) = r + readIndex = savedIndex + } + r.asInstanceOf[T] + } + + /** Read a name */ + protected def readName(): Name = { + val tag = readByte() + val len = readNat() + tag match { + case TERMname => newTermName(bytes, readIndex, len) + case TYPEname => newTypeName(bytes, readIndex, len) + case _ => errorBadSignature("bad name tag: " + tag) + } + } + + /** Read a symbol */ + protected def readSymbol(): Symbol = { + val tag = readByte() + val end = readNat() + readIndex + def atEnd = readIndex == end + + def readExtSymbol(): Symbol = { + val name = readNameRef() + val owner = if (atEnd) definitions.RootClass else readSymbolRef() + + def fromName(name: Name) = mkTermName(name) match { + case nme.ROOT => definitions.RootClass + case nme.ROOTPKG => definitions.RootPackage + case _ => + val s = owner.info.decl(name) + if (tag == EXTref) s else s.moduleClass + } + def nestedObjectSymbol: Symbol = { + // If the owner is overloaded (i.e. a method), it's not possible to select the + // right member, so return NoSymbol. This can only happen when unpickling a tree. + // the "case Apply" in readTree() takes care of selecting the correct alternative + // after parsing the arguments. + if (owner.isOverloaded) + return NoSymbol + + if (tag == EXTMODCLASSref) { + val moduleVar = owner.info.decl(nme.moduleVarName(name)) + if (moduleVar.isLazyAccessor) + return moduleVar.lazyAccessor.lazyAccessor + } + NoSymbol + } + + // (1) Try name. + fromName(name) orElse { + // (2) Try with expanded name. Can happen if references to private + // symbols are read from outside: for instance when checking the children + // of a class. See #1722. + fromName(nme.expandedName(name, owner)) orElse { + // (3) Try as a nested object symbol. + nestedObjectSymbol orElse { + // (4) Otherwise, fail. + errorMissingRequirement(name, owner) + } + } + } + } + + tag match { + case NONEsym => return NoSymbol + case EXTref | EXTMODCLASSref => return readExtSymbol() + case _ => () + } + + // symbols that were pickled with Pickler.writeSymInfo + val nameref = readNat() + val name = at(nameref, readName) + val owner = readSymbolRef() + val flags = pickledToRawFlags(readLongNat()) + var inforef = readNat() + val privateWithin = + if (!isSymbolRef(inforef)) NoSymbol + else { + val pw = at(inforef, readSymbol) + inforef = readNat() + pw + } + + def isModuleFlag = (flags & MODULE) != 0L + def isMethodFlag = (flags & METHOD) != 0L + def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) + def isModuleRoot = (name == moduleRoot.name) && (owner == moduleRoot.owner) + + def finishSym(sym: Symbol): Symbol = { + sym.flags = flags & PickledFlags + sym.privateWithin = privateWithin + sym.info = ( + if (atEnd) { + assert(!sym.isSuperAccessor, sym) + newLazyTypeRef(inforef) + } + else { + assert(sym.isSuperAccessor || sym.isParamAccessor, sym) + newLazyTypeRefAndAlias(inforef, readNat()) + } + ) + if (sym.owner.isClass && sym != classRoot && sym != moduleRoot && + !sym.isModuleClass && !sym.isRefinementClass && !sym.isTypeParameter && !sym.isExistentiallyBound) + symScope(sym.owner) enter sym + + sym + } + + finishSym(tag match { + case TYPEsym => owner.newAbstractType(mkTypeName(name)) + case ALIASsym => owner.newAliasType(mkTypeName(name)) + case CLASSsym => + val sym = (isClassRoot, isModuleFlag) match { + case (true, true) => moduleRoot.moduleClass + case (true, false) => classRoot + case (false, true) => owner.newModuleClass(mkTypeName(name)) + case (false, false) => owner.newClass(mkTypeName(name)) + } + if (!atEnd) + sym.typeOfThis = newLazyTypeRef(readNat()) + + sym + case MODULEsym => + val clazz = at(inforef, () => readType()).typeSymbol // after the NMT_TRANSITION period, we can leave off the () => ... () + if (isModuleRoot) moduleRoot + else { + val m = owner.newModule(name, clazz) + clazz.sourceModule = m + m + } + case VALsym => + if (isModuleRoot) { assert(false); NoSymbol } + else if (isMethodFlag) owner.newMethod(name) + else owner.newValue(name) + + case _ => + errorBadSignature("bad symbol tag: " + tag) + }) + } + + /** Read a type + * + * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) + * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe)) + * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor) + */ + protected def readType(forceProperType: Boolean = false): Type = { + val tag = readByte() + val end = readNat() + readIndex + (tag: @switch) match { + case NOtpe => + NoType + case NOPREFIXtpe => + NoPrefix + case THIStpe => + ThisType(readSymbolRef()) + case SINGLEtpe => + SingleType(readTypeRef(), readSymbolRef()) // !!! was singleType + case SUPERtpe => + val thistpe = readTypeRef() + val supertpe = readTypeRef() + SuperType(thistpe, supertpe) + case CONSTANTtpe => + ConstantType(readConstantRef()) + case TYPEREFtpe => + val pre = readTypeRef() + val sym = readSymbolRef() + var args = until(end, readTypeRef) + TypeRef(pre, sym, args) + case TYPEBOUNDStpe => + TypeBounds(readTypeRef(), readTypeRef()) + case REFINEDtpe => + val clazz = readSymbolRef() + RefinedType(until(end, readTypeRef), symScope(clazz), clazz) + case CLASSINFOtpe => + val clazz = readSymbolRef() + ClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) + case METHODtpe | IMPLICITMETHODtpe => + val restpe = readTypeRef() + val params = until(end, readSymbolRef) + // if the method is overloaded, the params cannot be determined (see readSymbol) => return NoType. + // Only happen for trees, "case Apply" in readTree() takes care of selecting the correct + // alternative after parsing the arguments. + if (params.contains(NoSymbol) || restpe == NoType) NoType + else MethodType(params, restpe) + case POLYtpe => + val restpe = readTypeRef() + val typeParams = until(end, readSymbolRef) + if(typeParams nonEmpty) { + // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe)) + // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet) + def transitionNMT(restpe: Type) = { + val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here) + if(forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) { assert(!resTpeCls.contains("ClassInfoType")) + NullaryMethodType(restpe) } + else restpe + } + PolyType(typeParams, transitionNMT(restpe)) + } + else + NullaryMethodType(restpe) + case EXISTENTIALtpe => + val restpe = readTypeRef() + ExistentialType(until(end, readSymbolRef), restpe) + case ANNOTATEDtpe => + var typeRef = readNat() + val selfsym = if (isSymbolRef(typeRef)) { + val s = at(typeRef, readSymbol) + typeRef = readNat() + s + } else NoSymbol // selfsym can go. + val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION + val annots = until(end, readAnnotationRef) + if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) + else tp + case _ => + noSuchTypeTag(tag, end) + } + } + + def noSuchTypeTag(tag: Int, end: Int): Type = + errorBadSignature("bad type tag: " + tag) + + /** Read a constant */ + protected def readConstant(): Constant = { + val tag = readByte().toInt + val len = readNat() + (tag: @switch) match { + case LITERALunit => Constant(()) + case LITERALboolean => Constant(readLong(len) != 0L) + case LITERALbyte => Constant(readLong(len).toByte) + case LITERALshort => Constant(readLong(len).toShort) + case LITERALchar => Constant(readLong(len).toChar) + case LITERALint => Constant(readLong(len).toInt) + case LITERALlong => Constant(readLong(len)) + case LITERALfloat => Constant(intBitsToFloat(readLong(len).toInt)) + case LITERALdouble => Constant(longBitsToDouble(readLong(len))) + case LITERALstring => Constant(readNameRef().toString()) + case LITERALnull => Constant(null) + case LITERALclass => Constant(readTypeRef()) + case LITERALenum => Constant(readSymbolRef()) + case _ => noSuchConstantTag(tag, len) + } + } + + def noSuchConstantTag(tag: Int, len: Int): Constant = + errorBadSignature("bad constant tag: " + tag) + + /** Read children and store them into the corresponding symbol. + */ + protected def readChildren() { + val tag = readByte() + assert(tag == CHILDREN) + val end = readNat() + readIndex + val target = readSymbolRef() + while (readIndex != end) target addChild readSymbolRef() + } + + /** Read an annotation argument, which is pickled either + * as a Constant or a Tree. + */ + protected def readAnnotArg(i: Int): Tree = bytes(index(i)) match { + case TREE => at(i, readTree) + case _ => + val const = at(i, readConstant) + Literal(const) setType const.tpe + } + + /** Read a ClassfileAnnotArg (argument to a classfile annotation) + */ + private def readArrayAnnot() = { + readByte() // skip the `annotargarray` tag + val end = readNat() + readIndex + until(end, () => readClassfileAnnotArg(readNat())).toArray(classfileAnnotArgManifest) + } + protected def readClassfileAnnotArg(i: Int): ClassfileAnnotArg = bytes(index(i)) match { + case ANNOTINFO => NestedAnnotArg(at(i, readAnnotation)) + case ANNOTARGARRAY => at(i, () => ArrayAnnotArg(readArrayAnnot())) + case _ => LiteralAnnotArg(at(i, readConstant)) + } + + /** Read an AnnotationInfo. Not to be called directly, use + * readAnnotation or readSymbolAnnotation + */ + protected def readAnnotationInfo(end: Int): AnnotationInfo = { + val atp = readTypeRef() + val args = new ListBuffer[Tree] + val assocs = new ListBuffer[(Name, ClassfileAnnotArg)] + while (readIndex != end) { + val argref = readNat() + if (isNameEntry(argref)) { + val name = at(argref, readName) + val arg = readClassfileAnnotArg(readNat()) + assocs += ((name, arg)) + } + else + args += readAnnotArg(argref) + } + AnnotationInfo(atp, args.toList, assocs.toList) + } + + /** Read an annotation and as a side effect store it into + * the symbol it requests. Called at top-level, for all + * (symbol, annotInfo) entries. */ + protected def readSymbolAnnotation() { + val tag = readByte() + if (tag != SYMANNOT) + errorBadSignature("symbol annotation expected ("+ tag +")") + val end = readNat() + readIndex + val target = readSymbolRef() + target.addAnnotation(readAnnotationInfo(end)) + } + + /** Read an annotation and return it. Used when unpickling + * an ANNOTATED(WSELF)tpe or a NestedAnnotArg */ + protected def readAnnotation(): AnnotationInfo = { + val tag = readByte() + if (tag != ANNOTINFO) + errorBadSignature("annotation expected (" + tag + ")") + val end = readNat() + readIndex + readAnnotationInfo(end) + } + + /* Read an abstract syntax tree */ + protected def readTree(): Tree = { + val outerTag = readByte() + if (outerTag != TREE) + errorBadSignature("tree expected (" + outerTag + ")") + val end = readNat() + readIndex + val tag = readByte() + val tpe = if (tag == EMPTYtree) NoType else readTypeRef() + + // Set by the three functions to follow. If symbol is non-null + // after the the new tree 't' has been created, t has its Symbol + // set to symbol; and it always has its Type set to tpe. + var symbol: Symbol = null + var mods: Modifiers = null + var name: Name = null + + /** Read a Symbol, Modifiers, and a Name */ + def setSymModsName() { + symbol = readSymbolRef() + mods = readModifiersRef() + name = readNameRef() + } + /** Read a Symbol and a Name */ + def setSymName() { + symbol = readSymbolRef() + name = readNameRef() + } + /** Read a Symbol */ + def setSym() { + symbol = readSymbolRef() + } + + val t = tag match { + case EMPTYtree => + EmptyTree + + case PACKAGEtree => + setSym() + val pid = readTreeRef().asInstanceOf[RefTree] + val stats = until(end, readTreeRef) + PackageDef(pid, stats) + + case CLASStree => + setSymModsName() + val impl = readTemplateRef() + val tparams = until(end, readTypeDefRef) + ClassDef(mods, mkTypeName(name), tparams, impl) + + case MODULEtree => + setSymModsName() + ModuleDef(mods, name, readTemplateRef()) + + case VALDEFtree => + setSymModsName() + val tpt = readTreeRef() + val rhs = readTreeRef() + ValDef(mods, name, tpt, rhs) + + case DEFDEFtree => + setSymModsName() + val tparams = times(readNat(), readTypeDefRef) + val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) + val tpt = readTreeRef() + val rhs = readTreeRef() + DefDef(mods, name, tparams, vparamss, tpt, rhs) + + case TYPEDEFtree => + setSymModsName() + val rhs = readTreeRef() + val tparams = until(end, readTypeDefRef) + TypeDef(mods, mkTypeName(name), tparams, rhs) + + case LABELtree => + setSymName() + val rhs = readTreeRef() + val params = until(end, readIdentRef) + LabelDef(name, params, rhs) + + case IMPORTtree => + setSym() + val expr = readTreeRef() + val selectors = until(end, () => { + val from = readNameRef() + val to = readNameRef() + ImportSelector(from, -1, to, -1) + }) + + Import(expr, selectors) + + case TEMPLATEtree => + setSym() + val parents = times(readNat(), readTreeRef) + val self = readValDefRef() + val body = until(end, readTreeRef) + + Template(parents, self, body) + + case BLOCKtree => + val expr = readTreeRef() + val stats = until(end, readTreeRef) + Block(stats, expr) + + case CASEtree => + val pat = readTreeRef() + val guard = readTreeRef() + val body = readTreeRef() + CaseDef(pat, guard, body) + + case ALTERNATIVEtree => + Alternative(until(end, readTreeRef)) + + case STARtree => + Star(readTreeRef()) + + case BINDtree => + setSymName() + Bind(name, readTreeRef()) + + case UNAPPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + UnApply(fun, args) + + case ARRAYVALUEtree => + val elemtpt = readTreeRef() + val trees = until(end, readTreeRef) + ArrayValue(elemtpt, trees) + + case FUNCTIONtree => + setSym() + val body = readTreeRef() + val vparams = until(end, readValDefRef) + Function(vparams, body) + + case ASSIGNtree => + val lhs = readTreeRef() + val rhs = readTreeRef() + Assign(lhs, rhs) + + case IFtree => + val cond = readTreeRef() + val thenp = readTreeRef() + val elsep = readTreeRef() + If(cond, thenp, elsep) + + case MATCHtree => + val selector = readTreeRef() + val cases = until(end, readCaseDefRef) + Match(selector, cases) + + case RETURNtree => + setSym() + Return(readTreeRef()) + + case TREtree => + val block = readTreeRef() + val finalizer = readTreeRef() + val catches = until(end, readCaseDefRef) + Try(block, catches, finalizer) + + case THROWtree => + Throw(readTreeRef()) + + case NEWtree => + New(readTreeRef()) + + case TYPEDtree => + val expr = readTreeRef() + val tpt = readTreeRef() + Typed(expr, tpt) + + case TYPEAPPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + TypeApply(fun, args) + + case APPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + if (fun.symbol.isOverloaded) { + fun.setType(fun.symbol.info) + inferMethodAlternative(fun, args map (_.tpe), tpe) + } + Apply(fun, args) + + case APPLYDYNAMICtree => + setSym() + val qual = readTreeRef() + val args = until(end, readTreeRef) + ApplyDynamic(qual, args) + + case SUPERtree => + setSym() + val qual = readTreeRef() + val mix = readTypeNameRef() + Super(qual, mix) + + case THIStree => + setSym() + This(readTypeNameRef()) + + case SELECTtree => + setSym() + val qualifier = readTreeRef() + val selector = readNameRef() + Select(qualifier, selector) + + case IDENTtree => + setSymName() + Ident(name) + + case LITERALtree => + Literal(readConstantRef()) + + case TYPEtree => + TypeTree() + + case ANNOTATEDtree => + val annot = readTreeRef() + val arg = readTreeRef() + Annotated(annot, arg) + + case SINGLETONTYPEtree => + SingletonTypeTree(readTreeRef()) + + case SELECTFROMTYPEtree => + val qualifier = readTreeRef() + val selector = readTypeNameRef() + SelectFromTypeTree(qualifier, selector) + + case COMPOUNDTYPEtree => + CompoundTypeTree(readTemplateRef()) + + case APPLIEDTYPEtree => + val tpt = readTreeRef() + val args = until(end, readTreeRef) + AppliedTypeTree(tpt, args) + + case TYPEBOUNDStree => + val lo = readTreeRef() + val hi = readTreeRef() + TypeBoundsTree(lo, hi) + + case EXISTENTIALTYPEtree => + val tpt = readTreeRef() + val whereClauses = until(end, readTreeRef) + ExistentialTypeTree(tpt, whereClauses) + + case _ => + noSuchTreeTag(tag, end) + } + + if (symbol == null) t setType tpe + else t setSymbol symbol setType tpe + } + + def noSuchTreeTag(tag: Int, end: Int) = + errorBadSignature("unknown tree type (" + tag + ")") + + def readModifiers(): Modifiers = { + val tag = readNat() + if (tag != MODIFIERS) + errorBadSignature("expected a modifiers tag (" + tag + ")") + val end = readNat() + readIndex + val pflagsHi = readNat() + val pflagsLo = readNat() + val pflags = (pflagsHi.toLong << 32) + pflagsLo + val flags = pickledToRawFlags(pflags) + val privateWithin = readNameRef() + Modifiers(flags, privateWithin, Nil, Map.empty) + } + + /* Read a reference to a pickled item */ + protected def readNameRef(): Name = at(readNat(), readName) + protected def readSymbolRef(): Symbol = at(readNat(), readSymbol) + protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () + protected def readConstantRef(): Constant = at(readNat(), readConstant) + protected def readAnnotationRef(): AnnotationInfo = at(readNat(), readAnnotation) + protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers) + protected def readTreeRef(): Tree = at(readNat(), readTree) + + protected def readTypeNameRef(): TypeName = mkTypeName(readNameRef()) + protected def readTermNameRef(): TermName = mkTermName(readNameRef()) + + protected def readTemplateRef(): Template = + readTreeRef() match { + case templ:Template => templ + case other => + errorBadSignature("expected a template (" + other + ")") + } + protected def readCaseDefRef(): CaseDef = + readTreeRef() match { + case tree:CaseDef => tree + case other => + errorBadSignature("expected a case def (" + other + ")") + } + protected def readValDefRef(): ValDef = + readTreeRef() match { + case tree:ValDef => tree + case other => + errorBadSignature("expected a ValDef (" + other + ")") + } + protected def readIdentRef(): Ident = + readTreeRef() match { + case tree:Ident => tree + case other => + errorBadSignature("expected an Ident (" + other + ")") + } + protected def readTypeDefRef(): TypeDef = + readTreeRef() match { + case tree:TypeDef => tree + case other => + errorBadSignature("expected an TypeDef (" + other + ")") + } + + protected def errorBadSignature(msg: String) = + throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg) + + protected def errorMissingRequirement(msg: String): Nothing = + if (debug) errorBadSignature(msg) + else throw new IOException("class file needed by "+classRoot.name+" is missing.\n"+msg) + + protected def errorMissingRequirement(name: Name, owner: Symbol): Nothing = + errorMissingRequirement( + "reference " + (if (name.isTypeName) "type " else "value ") + + name.decode + " of " + owner.tpe.widen + " refers to nonexisting symbol.") + + def inferMethodAlternative(fun: Tree, argtpes: List[Type], restpe: Type) {} // can't do it; need a compiler for that. + + def newLazyTypeRef(i: Int): LazyType = new LazyTypeRef(i) + def newLazyTypeRefAndAlias(i: Int, j: Int): LazyType = new LazyTypeRefAndAlias(i, j) + + /** A lazy type which when completed returns type at index `i`. */ + private class LazyTypeRef(i: Int) extends LazyType { + private val definedAtRunId = currentRunId + private val p = phase + override def complete(sym: Symbol) : Unit = { + val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` + if (p != phase) atPhase(p) (sym setInfo tp) + else sym setInfo tp + if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) + } + override def load(sym: Symbol) { complete(sym) } + } + + /** A lazy type which when completed returns type at index `i` and sets alias + * of completed symbol to symbol at index `j`. + */ + private class LazyTypeRefAndAlias(i: Int, j: Int) extends LazyTypeRef(i) { + override def complete(sym: Symbol) { + super.complete(sym) + var alias = at(j, readSymbol) + if (alias.isOverloaded) { + atPhase(picklerPhase) { + alias = alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)) + } + } + sym.asInstanceOf[TermSymbol].setAlias(alias) + } + } + } +} diff --git a/src/compiler/scala/reflect/common/util/Chars.scala b/src/compiler/scala/reflect/common/util/Chars.scala deleted file mode 100644 index 4175455f1f..0000000000 --- a/src/compiler/scala/reflect/common/util/Chars.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2011 LAMP/EPFL - * @author Martin Odersky - */ -package scala.reflect.common.util - -import annotation.{ tailrec, switch } -import java.lang.{ Character => JCharacter } - -/** Contains constants and classifier methods for characters */ -trait Chars { - // Be very careful touching these. - // Apparently trivial changes to the way you write these constants - // will cause Scanners.scala to go from a nice efficient switch to - // a ghastly nested if statement which will bring the type checker - // to its knees. See ticket #1456 - // Martin: (this should be verified now that the pattern rules have been redesigned). - final val LF = '\u000A' - final val FF = '\u000C' - final val CR = '\u000D' - final val SU = '\u001A' - - /** Convert a character digit to an Int according to given base, - * -1 if no success */ - def digit2int(ch: Char, base: Int): Int = { - if ('0' <= ch && ch <= '9' && ch < '0' + base) - ch - '0' - else if ('A' <= ch && ch < 'A' + base - 10) - ch - 'A' + 10 - else if ('a' <= ch && ch < 'a' + base - 10) - ch - 'a' + 10 - else - -1 - } - - /** Convert a character to a backslash-u escape */ - def char2uescape(c: Char): String = { - var rest = c.toInt - val buf = new StringBuilder - for (i <- 1 to 4) { - buf ++= (rest % 16).toHexString - rest = rest / 16 - } - "\\u" + buf.toString.reverse - } - - /** Is character a line break? */ - @inline def isLineBreakChar(c: Char) = (c: @switch) match { - case LF|FF|CR|SU => true - case _ => false - } - - /** Is character a whitespace character (but not a new line)? */ - def isWhitespace(c: Char) = - c == ' ' || c == '\t' || c == CR - - /** Can character form part of a doc comment variable $xxx? */ - def isVarPart(c: Char) = - '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' - - /** Can character start an alphanumeric Scala identifier? */ - def isIdentifierStart(c: Char): Boolean = - (c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c) - - /** Can character form part of an alphanumeric Scala identifier? */ - def isIdentifierPart(c: Char) = - (c == '$') || Character.isUnicodeIdentifierPart(c) - - /** Is character a math or other symbol in Unicode? */ - def isSpecial(c: Char) = { - val chtp = Character.getType(c) - chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt - } - - private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_' - private final val letterGroups = { - import JCharacter._ - Set[Byte](LOWERCASE_LETTER, UPPERCASE_LETTER, OTHER_LETTER, TITLECASE_LETTER, LETTER_NUMBER) - } - def isScalaLetter(ch: Char) = letterGroups(JCharacter.getType(ch).toByte) || otherLetters(ch) - - /** Can character form part of a Scala operator name? */ - def isOperatorPart(c : Char) : Boolean = (c: @switch) match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '/' | '\\' => true - case c => isSpecial(c) - } -} - -object Chars extends Chars { } |