diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-06-08 02:36:10 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-06-08 15:32:28 +0200 |
commit | 0b2f1bcf75d31c59b25e19eebcb80f39c155365b (patch) | |
tree | 8d9dfc50ef01ca48c068b232af7e67a723325388 /src/reflect | |
parent | 13213e3df0384b1fd815c0798758a22284572cdb (diff) | |
download | scala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.tar.gz scala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.tar.bz2 scala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.zip |
Introduces scala-reflect.jar
Diffstat (limited to 'src/reflect')
120 files changed, 30111 insertions, 0 deletions
diff --git a/src/reflect/scala/reflect/api/AnnotationInfos.scala b/src/reflect/scala/reflect/api/AnnotationInfos.scala new file mode 100644 index 0000000000..d9f35024d9 --- /dev/null +++ b/src/reflect/scala/reflect/api/AnnotationInfos.scala @@ -0,0 +1,27 @@ +package scala.reflect +package api + +trait AnnotationInfos extends base.AnnotationInfos { self: Universe => + + override type AnnotationInfo >: Null <: AnyRef with AnnotationInfoApi + trait AnnotationInfoApi { + def atp: Type + def args: List[Tree] + def assocs: List[(Name, ClassfileAnnotArg)] + } + + override type LiteralAnnotArg >: Null <: ClassfileAnnotArg with LiteralAnnotArgApi + trait LiteralAnnotArgApi { + def const: Constant + } + + override type ArrayAnnotArg >: Null <: ClassfileAnnotArg with ArrayAnnotArgApi + trait ArrayAnnotArgApi { + def args: Array[ClassfileAnnotArg] + } + + override type NestedAnnotArg >: Null <: ClassfileAnnotArg with NestedAnnotArgApi + trait NestedAnnotArgApi { + def annInfo: AnnotationInfo + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/Constants.scala b/src/reflect/scala/reflect/api/Constants.scala new file mode 100644 index 0000000000..7862ab0d25 --- /dev/null +++ b/src/reflect/scala/reflect/api/Constants.scala @@ -0,0 +1,33 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package api + +trait Constants extends base.Constants { + self: Universe => + + override type Constant >: Null <: AnyRef with ConstantApi + + abstract class ConstantApi { + val value: Any + def tpe: Type + def isNaN: Boolean + + def booleanValue: Boolean + def byteValue: Byte + def shortValue: Short + def charValue: Char + def intValue: Int + def longValue: Long + def floatValue: Float + def doubleValue: Double + def stringValue: String + def typeValue: Type + def symbolValue: Symbol + + def convertTo(pt: Type): Constant + } +} diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala new file mode 100644 index 0000000000..bda125a1a1 --- /dev/null +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -0,0 +1,62 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package api + +import scala.reflect.base.TreeCreator + +trait Exprs { self: Universe => + + /** An expression tree tagged with its type */ + trait Expr[+T] extends Equals with Serializable { + val mirror: Mirror + def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # Expr[T] + + def tree: Tree + def staticTpe: Type + def actualTpe: Type + + def splice: T + val value: T + + /** case class accessories */ + override def canEqual(x: Any) = x.isInstanceOf[Expr[_]] + override def equals(x: Any) = x.isInstanceOf[Expr[_]] && this.mirror == x.asInstanceOf[Expr[_]].mirror && this.tree == x.asInstanceOf[Expr[_]].tree + override def hashCode = mirror.hashCode * 31 + tree.hashCode + override def toString = "Expr["+staticTpe+"]("+tree+")" + } + + object Expr { + def apply[T: TypeTag](mirror: MirrorOf[self.type], treec: TreeCreator): Expr[T] = new ExprImpl[T](mirror.asInstanceOf[Mirror], treec) + def unapply[T](expr: Expr[T]): Option[Tree] = Some(expr.tree) + } + + private class ExprImpl[+T: TypeTag](val mirror: Mirror, val treec: TreeCreator) extends Expr[T] { + def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # Expr[T] = { + val otherMirror1 = otherMirror.asInstanceOf[MirrorOf[otherMirror.universe.type]] + val tag1 = (implicitly[TypeTag[T]] in otherMirror).asInstanceOf[otherMirror.universe.TypeTag[T]] + otherMirror.universe.Expr[T](otherMirror1, treec)(tag1) + } + + lazy val tree: Tree = treec[Exprs.this.type](mirror) + // [Eugene++] this is important + // !!! remove when we have improved type inference for singletons + // search for .type] to find other instances + lazy val staticTpe: Type = implicitly[TypeTag[T]].tpe + def actualTpe: Type = tree.tpe + + def splice: T = throw new UnsupportedOperationException(""" + |the function you're calling has not been spliced by the compiler. + |this means there is a cross-stage evaluation involved, and it needs to be invoked explicitly. + |if you're sure this is not an oversight, add scala-compiler.jar to the classpath, + |import `scala.tools.reflect.Eval` and call `<your expr>.eval` instead.""".trim.stripMargin) + lazy val value: T = throw new UnsupportedOperationException(""" + |the value you're calling is only meant to be used in cross-stage path-dependent types. + |if you want to splice the underlying expression, use `<your expr>.splice`. + |if you want to get a value of the underlying expression, add scala-compiler.jar to the classpath, + |import `scala.tools.reflect.Eval` and call `<your expr>.eval` instead.""".trim.stripMargin) + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala new file mode 100644 index 0000000000..969176d641 --- /dev/null +++ b/src/reflect/scala/reflect/api/FlagSets.scala @@ -0,0 +1,112 @@ +package scala.reflect +package api + +import scala.language.implicitConversions + +trait FlagSets { self: Universe => + + type FlagSet + + trait FlagOps extends Any { + def | (right: FlagSet): FlagSet + def & (right: FlagSet): FlagSet + def containsAll (right: FlagSet): Boolean + } + + implicit def addFlagOps(left: FlagSet): FlagOps + + val Flag: FlagValues + + type FlagValues >: Null <: FlagValuesApi + + // [Eugene++] any other flags we would like to expose? + + trait FlagValuesApi { + + /** Flag indicating that symbol or tree represents a trait */ + val TRAIT: FlagSet + + /** Flag indicating that symbol or tree represents a module or its internal module class */ + val MODULE: FlagSet + + /** Flag indicating that symbol or tree represents a mutable variable */ + val MUTABLE: FlagSet + + /** Flag indicating that symbol or tree represents a package or its internal package class */ + val PACKAGE: FlagSet + + /** Flag indicating that symbol or tree represents a method */ + val METHOD: FlagSet + + /** Flag indicating that symbol or tree represents a macro definition. */ + val MACRO: FlagSet + + /** Flag indicating that symbol or tree represents an abstract type, method, or value */ + val DEFERRED: FlagSet + + /** Flag indicating that symbol or tree represents an abstract class */ + val ABSTRACT: FlagSet + + /** Flag indicating that symbol or tree has `final` modifier set */ + val FINAL: FlagSet + + /** Flag indicating that symbol or tree has `sealed` modifier set */ + val SEALED: FlagSet + + /** Flag indicating that symbol or tree has `implicit` modifier set */ + val IMPLICIT: FlagSet + + /** Flag indicating that symbol or tree has `lazy` modifier set */ + val LAZY: FlagSet + + /** Flag indicating that symbol or tree has `override` modifier set */ + val OVERRIDE: FlagSet + + /** Flag indicating that symbol or tree has `private` modifier set */ + val PRIVATE: FlagSet + + /** Flag indicating that symbol or tree has `protected` modifier set */ + val PROTECTED: FlagSet + + /** Flag indicating that symbol or tree has `case` modifier set */ + val CASE: FlagSet + + /** Flag indicating that symbol or tree has `abstract` and `override` modifiers set */ + val ABSOVERRIDE: FlagSet + + /** Flag indicating that symbol or tree represents a by-name parameter */ + val BYNAMEPARAM: FlagSet + + /** Flag indicating that symbol or tree represents a class or parameter. + * Both type and value parameters carry the flag. */ + val PARAM: FlagSet + + /** Flag indicating that symbol or tree represents a field of a class + * that was generated from a parameter of that class */ + val PARAMACCESSOR: FlagSet + + /** Flag indicating that symbol or tree represents a field of a case class + * that corresponds to a parameter in the first parameter list of the + * primary constructor of that class */ + val CASEACCESSOR: FlagSet + + /** Flag indicating that symbol or tree represents a contravariant + * type parameter (marked with `+`). */ + val COVARIANT: FlagSet + + /** Flag indicating that symbol or tree represents a contravariant + * type parameter (marked with `-`). */ + val CONTRAVARIANT: FlagSet + + /** Flag indicating that parameter has a default value */ + val DEFAULTPARAM: FlagSet + + /** Flag indicating that trait has neither method implementations nor fields. + * This means the trait can be represented as a Java interface. */ + val INTERFACE: FlagSet + + def union(flags: FlagSet*): FlagSet + def intersection(flag: FlagSet*): FlagSet + def containsAll(superset: FlagSet, subset: FlagSet): Boolean + } +} diff --git a/src/reflect/scala/reflect/api/FrontEnds.scala b/src/reflect/scala/reflect/api/FrontEnds.scala new file mode 100644 index 0000000000..a201b83444 --- /dev/null +++ b/src/reflect/scala/reflect/api/FrontEnds.scala @@ -0,0 +1,72 @@ +package scala.reflect +package api + +// [Martin to Eugene] Todo: Needs to be evicted from API +// [Eugene++ to Martin] but how? we need them for macros +trait FrontEnds { + + type Position >: Null + + trait FrontEnd { + object severity extends Enumeration + class Severity(val id: Int) extends severity.Value { + var count: Int = 0 + override def toString() = this match { + case INFO => "INFO" + case WARNING => "WARNING" + case ERROR => "ERROR" + case _ => "<unknown>" + } + } + val INFO = new Severity(0) + val WARNING = new Severity(1) + val ERROR = new Severity(2) + + def hasErrors = ERROR.count > 0 + def hasWarnings = WARNING.count > 0 + + case class Info(val pos: Position, val msg: String, val severity: Severity) + val infos = new collection.mutable.LinkedHashSet[Info] + + /** Handles incoming info */ + def log(pos: Position, msg: String, severity: Severity) { + infos += new Info(pos, msg, severity) + severity.count += 1 + display(infos.last) + } + + /** Displays incoming info */ + def display(info: Info): Unit + + /** Services a request to drop into interactive mode */ + def interactive(): Unit + + /** Refreshes the UI */ + def flush(): Unit = {} + + /** Resets the reporter */ + def reset(): Unit = { + INFO.count = 0 + WARNING.count = 0 + ERROR.count = 0 + infos.clear() + } + } + + class SilentFrontEnd extends FrontEnd { + def display(info: Info) {} + def interactive() {} + } + + /** Creates a UI-less reporter that simply accumulates all the messages + */ + def mkSilentFrontEnd(): FrontEnd = new SilentFrontEnd() + + /** Creates a reporter that prints messages to the console according to the settings. + * + * ``minSeverity'' determines minimum severity of the messages to be printed. + * 0 stands for INFO, 1 stands for WARNING and 2 stands for ERROR. + */ + // todo. untangle warningsAsErrors from Reporters. I don't feel like moving this flag here! + def mkConsoleFrontEnd(minSeverity: Int = 1): FrontEnd +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/Importers.scala b/src/reflect/scala/reflect/api/Importers.scala new file mode 100644 index 0000000000..69d6414f4f --- /dev/null +++ b/src/reflect/scala/reflect/api/Importers.scala @@ -0,0 +1,21 @@ +package scala.reflect +package api + +// [Martin] Importers need to be made mirror aware. +// [Eugene++] this is important +trait Importers { self: Universe => + + def mkImporter(from0: Universe): Importer { val from: from0.type } + + trait Importer { + val from: Universe + + val reverse: from.Importer { val from: self.type } + + def importSymbol(sym: from.Symbol): Symbol + + def importType(tpe: from.Type): Type + + def importTree(tree: from.Tree): Tree + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/JavaUniverse.scala b/src/reflect/scala/reflect/api/JavaUniverse.scala new file mode 100644 index 0000000000..8bf62a357c --- /dev/null +++ b/src/reflect/scala/reflect/api/JavaUniverse.scala @@ -0,0 +1,19 @@ +package scala.reflect +package api + +// [Martin] Moved to compiler because it needs to see runtime.Universe +// The two will be united in scala-reflect anyway. +trait JavaUniverse extends Universe with Mirrors with TagInterop { self => + + type RuntimeClass = java.lang.Class[_] + + override type Mirror >: Null <: JavaMirror + + trait JavaMirror extends MirrorOf[self.type] with RuntimeMirror { + val classLoader: ClassLoader + override def toString = s"JavaMirror with ${runtime.ReflectionUtils.show(classLoader)}" + } + + def runtimeMirror(cl: ClassLoader): Mirror +} + diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala new file mode 100644 index 0000000000..2fcee8f227 --- /dev/null +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -0,0 +1,213 @@ +package scala.reflect +package api + +trait Mirrors { self: Universe => + + type RuntimeClass >: Null + + // [Eugene++ to Martin] how do we reflect against inner classes? + // presumably, we should add `reflectClass` to both InstanceMirror (inner classes) and TemplateMirror (nested classes) + // in the former case, the resulting ClassMirror should remember the outer instance that spawned it to use it in reflective construction + + // [Eugene] also, it might make sense to provide shortcuts for the API + // + // for example, right now to invoke the same method for several different instances, you need: + // 1) get the method symbol + // 2) get the instance mirror for every instance + // 3) call reflectMethod on the instance mirrors for every instance + // 4) call apply for every instance (okay, this can be united with step #3, but still) + // + // I have several suggestions that we can discuss later: + // 1) For every `reflectXXX(sym: Symbol): XXXMirror`, add `reflectXXX(name: String, types: Type*): XXXMirror` and `reflectXXXs(): List[XXXMirror]` + // 2) Provide a way to skip obtaining InstanceMirror (step #2 in the outline provided above) + + // [Eugene] another improvement would be have mirrors reproduce the structure of the reflection domain + // e.g. a ClassMirror could also have a list of fields, methods, constructors and so on + // read up more on the proposed design in "Reflecting Scala" by Y. Coppel + + /** A mirror that reflects a runtime value */ + trait InstanceMirror { + + /** The instance value reflected by this mirror */ + def instance: Any + + /** The mirror corresponding to the run-time class of the reflected instance. */ + def reflectClass: ClassMirror + + /** Get value of field in reflected instance. + * @field A field symbol that should represent a field of the instance class. + * @return The value associated with that field in the reflected instance + * @throws ??? + */ + def reflectField(field: TermSymbol): FieldMirror + + /** Invokes a method on the reflected instance. + * @param meth A method symbol that should represent a method of the instance class + * @param args The arguments to pass to the method + * @return The result of invoking `meth(args)` on the reflected instance. + * @throws ??? + */ + def reflectMethod(method: MethodSymbol): MethodMirror + } + + /** A mirror that reflects a field */ + trait FieldMirror { + + /** The object containing the field */ + def receiver: AnyRef + + /** The field symbol representing the field */ + def field: TermSymbol + + /** Retrieves the value stored in the field */ + def get: Any + + /** Updates the value stored in the field */ + def set(value: Any): Unit + } + + /** A mirror that reflects a method handle */ + trait MethodMirror { + + /** The receiver object of the method */ + def receiver: AnyRef + + /** The method symbol representing the method */ + def method: MethodSymbol + + /** The result of applying the method to the given arguments */ + def apply(args: Any*): Any + } + + /** A mirror that reflects the instance or static parts of a runtime class */ + trait TemplateMirror { + + /** The runtime class reflected by this mirror */ + def runtimeClass: RuntimeClass + + /** True if the mirror represents the static part + * if a runtime class or the companion object of a Scala class. + * One has: + * + * this.isStatic == this.isInstanceOf[ModuleMirror] + * !this.isStatic == this.isInstanceOf[ClassMirror] + */ + def isStatic: Boolean + + /** The Scala symbol corresponding to the reflected runtime class or module. */ + def symbol: Symbol + + // [Eugene++ to Martin] I've removed `typeSignature`, because we can obtain it via `symbol.typeSignature` + + /** Optionally, the mirror of the companion reflected by this mirror. + * If this mirror reflects a Scala object, the mirror for the companion class, or None + * if the mirror represents a Scala object that comes without a class. + * Otherwise, if the mirror represents the static part of a runtime class, the + * mirror representing the instance part of the same class. + * Otherwise, if the mirror represents a Scala instance class, the mirror for the companion + * object of that class, or None if no such object exists. + * Otherwise, if the mirror represents a runtime instance class, a mirror representing the static + * part of the same class. + */ + def companion: Option[TemplateMirror] + } + + /** A mirror that reflects a Scala object definition or the static parts of a runtime class */ + trait ModuleMirror extends TemplateMirror { + + /** The Scala module symbol corresponding to the reflected module. */ + override def symbol: ModuleSymbol + + /** If the reflected runtime class corresponds to a Scala object definition, + * returns the single instance representing that object. + * If this mirror reflects the static part of a runtime class, returns `null`. + */ + def instance: Any + + /** Optionally, the mirror of the companion class if the object reflected by this mirror. + * If this mirror reflects a Scala object, the mirror for the companion class, or None + * if the mirror represents a Scala object that comes without a class. + * Otherwise, if the mirror represents the static part of a runtime class, the + * mirror representing the instance part of the same class. + */ + def companion: Option[ClassMirror] + } + + /** A mirror that reflects the instance parts of a runtime class */ + trait ClassMirror extends TemplateMirror { + + /** The Scala class symbol corresponding to the reflected class. */ + override def symbol: ClassSymbol + + /** Returns a fresh instance of by invoking that constructor. + * @throws InstantiationException if the class does not have a public + * constructor with an empty parameter list. + * @throws IllegalAccessException if the class or its constructor is not accessible. + * @throws ExceptionInInitializerError if the initialization of the constructor fails. + * @throws SecurityException if creating a new instance is not permitted. + */ + def reflectConstructor(constructor: MethodSymbol): MethodMirror + + /** Optionally, the mirror of the companion object of the class reflected by this mirror. + * If this mirror represents a Scala instance class, the mirror for the companion + * object of that class, or None if no such object exists. + * Otherwise, if the mirror represents a runtime instance class, a mirror representing the static + * part of the same class. + */ + def companion: Option[ModuleMirror] + } + + /** The API of a mirror for a reflective universe */ + trait RuntimeMirror extends MirrorOf[Mirrors.this.type] { self => + + /** A reflective mirror for the given object + * @param obj An arbitrary value + * @return The mirror for `obj`. + */ + def reflect(obj: Any): InstanceMirror + + /** A reflective mirror for the given Runtime class + * @param runtimeClass A Runtime class object + * @return The mirror for `runtimeClass` + */ + def reflectClass(runtimeClass: RuntimeClass): ClassMirror + + /** A reflective mirror for the Runtime class with the given name in the + * current classloader. + * @param name The fully qualified name of the class + * @return The mirror for the runtime class with fully qualified name + * `name` in the current class loader. + * @throws java.lang.ClassNotFoundException if no class with that name exists + * to do: throws anything else? + */ + def reflectClass(fullName: String): ClassMirror + + /** A reflective mirror for the given Runtime class + * @param runtimeClass A Runtime class object + * @return The mirror for `runtimeClass` + */ + def reflectModule(runtimeClass: RuntimeClass): ModuleMirror + + /** A reflective mirror for the Runtime class with the given name in the + * current classloader. + * @param name The fully qualified name of the class + * @return The mirror for the runtime class with fully qualified name + * `name` in the current class loader. + * @throws java.lang.ClassNotFoundException if no class with that name exists + * to do: throws anything else? + */ + def reflectModule(fullName: String): ModuleMirror + + /** Maps a Scala type to the corresponding Java class object + */ + def runtimeClass(tpe: Type): RuntimeClass + + /** Maps a Scala class symbol to the corresponding Java class object + * @throws ClassNotFoundException if there is no Java class + * corresponding to the given Scala class symbol. + * Note: If the Scala symbol is ArrayClass, a ClassNotFound exception is thrown + * because there is no unique Java class corresponding to a Scala generic array + */ + def runtimeClass(cls: ClassSymbol): RuntimeClass + } +} diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala new file mode 100644 index 0000000000..222ee5024b --- /dev/null +++ b/src/reflect/scala/reflect/api/Names.scala @@ -0,0 +1,44 @@ +package scala.reflect +package api + +/** A trait that manages names. + * A name is a string in one of two name universes: terms and types. + * The same string can be a name in both universes. + * Two names are equal if they represent the same string and they are + * members of the same universe. + * + * Names are interned. That is, for two names `name11 and `name2`, + * `name1 == name2` implies `name1 eq name2`. + */ +trait Names extends base.Names { + + /** The abstract type of names */ + type Name >: Null <: NameApi + + /** The extended API of names that's supported on reflect mirror via an + * implicit conversion in reflect.ops + */ + abstract class NameApi extends NameBase { + + // [Eugene++] this functionality should be in base + // this is because stuff will be reified in mangled state, and people will need a way to figure it out + + /** Replaces all occurrences of \$op_names in this name by corresponding operator symbols. + * Example: `foo_\$plus\$eq` becomes `foo_+=` + */ + def decoded: String + + /** Replaces all occurrences of operator symbols in this name by corresponding \$op_names. + * Example: `foo_+=` becomes `foo_\$plus\$eq`. + */ + def encoded: String + + /** The decoded name, still represented as a name. + */ + def decodedName: Name + + /** The encoded name, still represented as a name. + */ + def encodedName: Name + } +} diff --git a/src/reflect/scala/reflect/api/Positions.scala b/src/reflect/scala/reflect/api/Positions.scala new file mode 100644 index 0000000000..9d3d90d9f8 --- /dev/null +++ b/src/reflect/scala/reflect/api/Positions.scala @@ -0,0 +1,196 @@ +package scala.reflect +package api + +trait Positions extends base.Positions { + self: Universe => + + /** .. */ + type Position >: Null <: PositionApi { type Pos = Position } + + /** 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 + + /** 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 + + /** 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]) + // [Eugene++] can this method be of use for macros? +} + +/** The Position class and its subclasses represent positions of ASTs and symbols. + * Except for NoPosition and FakePos, every position refers to a SourceFile + * and to an offset in the sourcefile (its `point`). For batch compilation, + * that's all. For interactive IDE's there are also RangePositions + * and TransparentPositions. A RangePosition indicates a start and an end + * in addition to its point. TransparentPositions are a subclass of RangePositions. + * Range positions that are not transparent are called opaque. + * Trees with RangePositions need to satisfy the following invariants. + * + * INV1: A tree with an offset position never contains a child + * with a range position + * INV2: If the child of a tree with a range position also has a range position, + * then the child's range is contained in the parent's range. + * INV3: Opaque range positions of children of the same node are non-overlapping + * (this means their overlap is at most a single point). + * + * The following tests are useful on positions: + * + * pos.isDefined true if position is not a NoPosition nor a FakePosition + * pos.isRange true if position is a range + * pos.isOpaqueRange true if position is an opaque range + * + * The following accessor methods are provided: + * + * pos.source The source file of the position, which must be defined + * pos.point The offset of the position's point, which must be defined + * pos.start The start of the position, which must be a range + * pos.end The end of the position, which must be a range + * + * There are also convenience methods, such as + * + * pos.startOrPoint + * pos.endOrPoint + * pos.pointOrElse(default) + * + * These are less strict about the kind of position on which they can be applied. + * + * The following conversion methods are often used: + * + * pos.focus converts a range position to an offset position, keeping its point; + * returns all other positions unchanged. + * pos.makeTransparent converts an opaque range position into a transparent one. + * returns all other positions unchanged. + */ +trait PositionApi extends Attachments { + + type Pos >: Null <: PositionApi + + /** Java file corresponding to the source file of this position. + */ + def fileInfo: java.io.File + + /** Content of the source file that contains this position. + */ + def fileContent: Array[Char] + + /** Is this position neither a NoPosition nor a FakePosition? + * If isDefined is true, offset and source are both defined. + */ + def isDefined: Boolean + + /** Is this position a range position? */ + def isRange: Boolean + + /** Is this position a transparent position? */ + def isTransparent: Boolean + + /** Is this position a non-transparent range position? */ + def isOpaqueRange: Boolean + + /** if opaque range, make this position transparent */ + def makeTransparent: Pos + + /** The start of the position's range, error if not a range position */ + def start: Int + + /** The start of the position's range, or point if not a range position */ + def startOrPoint: Int + + /** The point (where the ^ is) of the position */ + def point: Int + + /** The point (where the ^ is) of the position, or else `default` if undefined */ + def pointOrElse(default: Int): Int + + /** The end of the position's range, error if not a range position */ + def end: Int + + /** The end of the position's range, or point if not a range position */ + def endOrPoint: Int + + /** The same position with a different start value (if a range) */ + def withStart(off: Int): Pos + + /** The same position with a different end value (if a range) */ + def withEnd(off: Int): Pos + + /** The same position with a different point value (if a range or offset) */ + def withPoint(off: Int): Pos + + /** If this is a range, the union with the other range, with the point of this position. + * Otherwise, this position + */ + def union(pos: Pos): Pos + + /** If this is a range position, the offset position of its point. + * Otherwise the position itself + */ + def focus: Pos + + /** If this is a range position, the offset position of its start. + * Otherwise the position itself + */ + def focusStart: Pos + + /** If this is a range position, the offset position of its end. + * Otherwise the position itself + */ + def focusEnd: Pos + + /** Does this position include the given position `pos`. + * This holds if `this` is a range position and its range [start..end] + * is the same or covers the range of the given position, which may or may not be a range position. + */ + def includes(pos: Pos): Boolean + + /** Does this position properly include the given position `pos` ("properly" meaning their + * ranges are not the same)? + */ + def properlyIncludes(pos: Pos): Boolean + + /** Does this position precede that position? + * This holds if both positions are defined and the end point of this position + * is not larger than the start point of the given position. + */ + def precedes(pos: Pos): Boolean + + /** Does this position properly precede the given position `pos` ("properly" meaning their ranges + * do not share a common point). + */ + def properlyPrecedes(pos: Pos): Boolean + + /** Does this position overlap with that position? + * This holds if both positions are ranges and there is an interval of + * non-zero length that is shared by both position ranges. + */ + def overlaps(pos: Pos): Boolean + + /** Does this position cover the same range as that position? + * Holds only if both position are ranges + */ + def sameRange(pos: Pos): Boolean + + def line: Int + + def column: Int + + /** Convert this to a position around `point` that spans a single source line */ + def toSingleLine: Pos + + def lineContent: String + + def show: String +} diff --git a/src/reflect/scala/reflect/api/StandardDefinitions.scala b/src/reflect/scala/reflect/api/StandardDefinitions.scala new file mode 100644 index 0000000000..c2a89f92dd --- /dev/null +++ b/src/reflect/scala/reflect/api/StandardDefinitions.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect +package api + +trait StandardDefinitions extends base.StandardDefinitions { + self: Universe => + + val definitions: DefinitionsApi + + trait DefinitionsApi extends DefinitionsBase { + def JavaLangPackageClass: ClassSymbol + def JavaLangPackage: ModuleSymbol + def ArrayModule: ModuleSymbol + def ArrayModule_overloadedApply: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def Array_apply: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def Array_clone: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def Array_length: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def Array_update: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def ByNameParamClass: ClassSymbol + def ConsClass: ClassSymbol + def FunctionClass : Array[ClassSymbol] + def IterableClass: ClassSymbol + def IteratorClass: ClassSymbol + def IteratorModule: ModuleSymbol + def Iterator_apply: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def JavaRepeatedParamClass: ClassSymbol + def ListModule: ModuleSymbol + def List_apply: TermSymbol // todo. fix the bug in Definitions.getMemberMethod + def NilModule: ModuleSymbol + def NoneModule: ModuleSymbol + def OptionClass: ClassSymbol + def ProductClass : Array[ClassSymbol] + def RepeatedParamClass: ClassSymbol + def SeqClass: ClassSymbol + def SeqModule: ModuleSymbol + def SomeClass: ClassSymbol + def SomeModule: ModuleSymbol + def StringBuilderClass: ClassSymbol + def SymbolClass : ClassSymbol + def TraversableClass: ClassSymbol + def TupleClass: Array[Symbol] // cannot make it Array[ClassSymbol], because TupleClass(0) is supposed to be NoSymbol. weird + def ScalaPrimitiveValueClasses: List[ClassSymbol] + def ScalaNumericValueClasses: List[ClassSymbol] + } +} diff --git a/src/reflect/scala/reflect/api/StandardNames.scala b/src/reflect/scala/reflect/api/StandardNames.scala new file mode 100644 index 0000000000..9ec66b8531 --- /dev/null +++ b/src/reflect/scala/reflect/api/StandardNames.scala @@ -0,0 +1,163 @@ +/* NSC -- new Scala compiler +* Copyright 2005-2011 LAMP/EPFL +* @author Martin Odersky +*/ +package scala.reflect +package api + +trait StandardNames extends base.StandardNames { + self: Universe => + + val nme: TermNamesApi + val tpnme: TypeNamesApi + + trait NamesApi extends NamesBase { + val ANON_CLASS_NAME: NameType + val ANON_FUN_NAME: NameType + val EMPTY: NameType + val ERROR: NameType + val IMPORT: NameType + val MODULE_VAR_SUFFIX: NameType + val PACKAGE: NameType + val ROOT: NameType + val SPECIALIZED_SUFFIX: NameType + + def flattenedName(segments: Name*): NameType + } + + trait TermNamesApi extends NamesApi with TermNamesBase { + val EXPAND_SEPARATOR_STRING: String + val IMPL_CLASS_SUFFIX: String + val INTERPRETER_IMPORT_WRAPPER: String + val INTERPRETER_LINE_PREFIX: String + val INTERPRETER_VAR_PREFIX: String + val INTERPRETER_WRAPPER_SUFFIX: String + val LOCALDUMMY_PREFIX: String + val LOCAL_SUFFIX_STRING: String + val MODULE_SUFFIX_NAME: TermName + val NAME_JOIN_NAME: TermName + val PROTECTED_PREFIX: String + val PROTECTED_SET_PREFIX: String + val SETTER_SUFFIX: TermName + val SINGLETON_SUFFIX: String + val SUPER_PREFIX_STRING: String + val TRAIT_SETTER_SEPARATOR_STRING: String + + val ANYNAME: TermName + val FAKE_LOCAL_THIS: TermName + val INITIALIZER: TermName + val LAZY_LOCAL: TermName + val UNIVERSE_BUILD: NameType + val UNIVERSE_BUILD_PREFIX: NameType + val UNIVERSE_PREFIX: NameType + val UNIVERSE_SHORT: NameType + val MIRROR_PREFIX: NameType + val MIRROR_SHORT: NameType + val MIRROR_UNTYPED: NameType + val REIFY_FREE_PREFIX: NameType + val REIFY_FREE_THIS_SUFFIX: NameType + val REIFY_FREE_VALUE_SUFFIX: NameType + val REIFY_SYMDEF_PREFIX: NameType + val MIXIN_CONSTRUCTOR: TermName + val MODULE_INSTANCE_FIELD: TermName + val OUTER: TermName + val OUTER_LOCAL: TermName + val OUTER_SYNTH: TermName + val SELECTOR_DUMMY: TermName + val SELF: TermName + val SPECIALIZED_INSTANCE: TermName + val STAR: TermName + val THIS: TermName + + val BITMAP_NORMAL: TermName + val BITMAP_TRANSIENT: TermName + val BITMAP_CHECKINIT: TermName + val BITMAP_CHECKINIT_TRANSIENT: TermName + + val ROOTPKG: TermName + + val ADD: TermName + val AND: TermName + val ASR: TermName + val DIV: TermName + val EQ: TermName + val EQL: TermName + val GE: TermName + val GT: TermName + val HASHHASH: TermName + val LE: TermName + val LSL: TermName + val LSR: TermName + val LT: TermName + val MINUS: TermName + val MOD: TermName + val MUL: TermName + val NE: TermName + val OR: TermName + val PLUS : TermName + val SUB: TermName + val XOR: TermName + val ZAND: TermName + val ZOR: TermName + + val UNARY_~ : TermName + val UNARY_+ : TermName + val UNARY_- : TermName + val UNARY_! : TermName + + val ??? : TermName + + def isConstructorName(name: Name): Boolean + def isExceptionResultName(name: Name): Boolean + def isImplClassName(name: Name): Boolean + def isLocalDummyName(name: Name): Boolean + def isLocalName(name: Name): Boolean + def isLoopHeaderLabel(name: Name): Boolean + def isModuleName(name: Name): Boolean + def isOpAssignmentName(name: Name): Boolean + def isProtectedAccessorName(name: Name): Boolean + def isReplWrapperName(name: Name): Boolean + def isSetterName(name: Name): Boolean + def isSingletonName(name: Name): Boolean + def isSuperAccessorName(name: Name): Boolean + def isTraitSetterName(name: Name): Boolean + + def defaultGetterName(name: Name, pos: Int): TermName + def defaultGetterToMethod(name: Name): TermName + def expandedName(name: TermName, base: Symbol, separator: String): TermName + def expandedSetterName(name: TermName, base: Symbol): TermName + def getterName(name: TermName): TermName + def getterToLocal(name: TermName): TermName + def getterToSetter(name: TermName): TermName + def localDummyName(clazz: Symbol): TermName + def localToGetter(name: TermName): TermName + def protName(name: Name): TermName + def protSetterName(name: Name): TermName + def setterToGetter(name: TermName): TermName + def superName(name: Name): TermName + + def dropLocalSuffix(name: Name): Name + def originalName(name: Name): Name + def stripModuleSuffix(name: Name): Name + def unspecializedName(name: Name): Name + def segments(name: String, assumeTerm: Boolean): List[Name] + def splitSpecializedName(name: Name): (Name, String, String) + } + + trait TypeNamesApi extends NamesApi with TypeNamesBase { + val BYNAME_PARAM_CLASS_NAME: TypeName + val EQUALS_PATTERN_NAME: TypeName + val JAVA_REPEATED_PARAM_CLASS_NAME: TypeName + val LOCAL_CHILD: TypeName + val REFINE_CLASS_NAME: TypeName + val REPEATED_PARAM_CLASS_NAME: TypeName + val WILDCARD_STAR: TypeName + val REIFY_TYPECREATOR_PREFIX: NameType + val REIFY_TREECREATOR_PREFIX: NameType + + def dropSingletonName(name: Name): TypeName + def implClassName(name: Name): TypeName + def interfaceName(implname: Name): TypeName + def singletonName(name: Name): TypeName + } +} diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala new file mode 100644 index 0000000000..1d266dc778 --- /dev/null +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -0,0 +1,268 @@ +package scala.reflect +package api + +trait Symbols extends base.Symbols { self: Universe => + + override type Symbol >: Null <: SymbolApi + override type TypeSymbol >: Null <: Symbol with TypeSymbolApi + override type TermSymbol >: Null <: Symbol with TermSymbolApi + override type MethodSymbol >: Null <: TermSymbol with MethodSymbolApi + override type ModuleSymbol >: Null <: TermSymbol with ModuleSymbolApi + override type ClassSymbol >: Null <: TypeSymbol with ClassSymbolApi + override type FreeTermSymbol >: Null <: TermSymbol with FreeTermSymbolApi + override type FreeTypeSymbol >: Null <: TypeSymbol with FreeTypeSymbolApi + + trait HasFlagsApi { + def flags: FlagSet + def hasFlag(fs: FlagSet): Boolean + def hasAllFlags(fs: FlagSet): Boolean + def flagString: String + } + + /** The API of symbols */ + trait SymbolApi extends SymbolBase with HasFlagsApi { this: Symbol => + + /** The position of this symbol + */ + def pos: Position + + /** A list of annotations attached to this Symbol. + */ + // [Eugene++] we cannot expose the `annotations` method because it doesn't auto-initialize a symbol (see SI-5423) + // there was an idea to use the `isCompilerUniverse` flag and auto-initialize symbols in `annotations` whenever this flag is false + // but it doesn't work, because the unpickler (that is shared between reflective universes and global universes) is very picky about initialization + // scala.reflect.internal.Types$TypeError: bad reference while unpickling scala.collection.immutable.Nil: type Nothing not found in scala.type not found. + // at scala.reflect.internal.pickling.UnPickler$Scan.toTypeError(UnPickler.scala:836) + // at scala.reflect.internal.pickling.UnPickler$Scan$LazyTypeRef.complete(UnPickler.scala:849) // auto-initialize goes boom + // at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1140) + // at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1272) // this triggers auto-initialize + // at scala.reflect.internal.Symbols$Symbol.annotations(Symbols.scala:1438) // unpickler first tries to get pre-existing annotations + // at scala.reflect.internal.Symbols$Symbol.addAnnotation(Symbols.scala:1458) // unpickler tries to add the annotation being read + // at scala.reflect.internal.pickling.UnPickler$Scan.readSymbolAnnotation(UnPickler.scala:489) // unpickler detects an annotation + // at scala.reflect.internal.pickling.UnPickler$Scan.run(UnPickler.scala:88) + // at scala.reflect.internal.pickling.UnPickler.unpickle(UnPickler.scala:37) + // at scala.reflect.runtime.JavaMirrors$JavaMirror.unpickleClass(JavaMirrors.scala:253) // unpickle from within a reflexive mirror + // def annotations: List[AnnotationInfo] + def getAnnotations: List[AnnotationInfo] + + /** Whether this symbol carries an annotation for which the given + * symbol is its typeSymbol. + */ + def hasAnnotation(sym: Symbol): Boolean + + /** ... + */ + def orElse(alt: => Symbol): Symbol + + /** ... + */ + def filter(cond: Symbol => Boolean): Symbol + + /** If this is a NoSymbol, returns NoSymbol, otherwise + * returns the result of applying `f` to this symbol. + */ + def map(f: Symbol => Symbol): Symbol + + /** ... + */ + def suchThat(cond: Symbol => Boolean): Symbol + + /** + * Set when symbol has a modifier of the form private[X], NoSymbol otherwise. + * + * 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) && (privateWithin == NoSymbol) + * java package: !hasFlag(PRIVATE | PROTECTED) && (privateWithin == enclosingPackage) + * java protected: hasFlag(PROTECTED) && (privateWithin == enclosingPackage) + * java public: !hasFlag(PRIVATE | PROTECTED) && (privateWithin == NoSymbol) + */ + def privateWithin: Symbol + + /** For a class: the module or case class factory with the same name in the same package. + * For a module: the class with the same name in the same package. + * For all others: NoSymbol + */ + def companionSymbol: Symbol + + /** If this symbol is a package class, this symbol; otherwise the next enclosing + * package class, or `NoSymbol` if none exists. + */ + def enclosingPackageClass: Symbol + + /** If this symbol is a top-level class, this symbol; otherwise the next enclosing + * top-level class, or `NoSymbol` if none exists. + */ + def enclosingTopLevelClass: Symbol + + /** Does this symbol represent a value, i.e. not a module and not a method? + * If yes, `isTerm` is also guaranteed to be true. + * [Eugene++] I need a review of the implementation + */ + def isValue: Boolean + + /** Does this symbol represent a mutable value? + * If yes, `isTerm` and `isValue` are also guaranteed to be true. + */ + def isVariable: Boolean + + /** Does this symbol represent the definition of a package? + * If yes, `isTerm` is also guaranteed to be true. + */ + def isPackage: Boolean + + /** Does this symbol represent a package class? + * If yes, `isClass` is also guaranteed to be true. + */ + def isPackageClass: Boolean + + /** Is this symbol an overloaded method? + */ + def isOverloaded : Boolean + + /** Does this symbol represent the definition of a primitive class? + * Namely, is it one of [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + * [[scala.Short]], [[scala.Byte]], [[scala.Unit]] or [[scala.Boolean]]? + */ + def isPrimitiveValueClass: Boolean + + /** Does this symbol represent the definition of a numeric value class? + * Namely, is it one of [[scala.Double]], [[scala.Float]], [[scala.Long]], [[scala.Int]], [[scala.Char]], + * [[scala.Short]], [[scala.Byte]], [[scala.Unit]] or [[scala.Boolean]]? + */ + def isNumericValueClass: Boolean + + /** Does this symbol represent the definition of a custom value class? + * Namely, is AnyVal among its parent classes? + * TODO: Why not just have in reflect.internal? + * [Eugene++] because it's useful for macros + */ + def isDerivedValueClass: Boolean + + /** Does this symbol represent the definition of a type alias? + * If yes, `isType` is also guaranteed to be true. + */ + def isAliasType : Boolean + + /** Does this symbol represent the definition of an abstract type? + * If yes, `isType` is also guaranteed to be true. + */ + def isAbstractType : Boolean + + /** Does this symbol represent an existentially bound type? + * If yes, `isType` is also guaranteed to be true. + */ + def isExistential : Boolean + + /** Does this symbol represent a free type captured by reification? + */ + def isFreeType : Boolean + + /** Does this symbol or its underlying type represent a typechecking error? + */ + def isErroneous : Boolean + + /** The type signature of this symbol seen as a member of given type `site`. + */ + def typeSignatureIn(site: Type): Type + + /** The type signature of this symbol. + * Note if the symbol is a member of a class, one almost always is interested + * in `typeSignatureIn` with a site type instead. + */ + def typeSignature: Type + + /** The string discriminator of this symbol; useful for debugging */ + def kind: String + } + + /** The API of term symbols */ + trait TermSymbolApi extends SymbolApi with HasFlagsApi with TermSymbolBase { this: TermSymbol => + /** The overloaded alternatives of this symbol */ + def alternatives: List[Symbol] + + def resolveOverloaded(pre: Type = NoPrefix, targs: Seq[Type] = List(), actuals: Seq[Type]): Symbol + } + + /** The API of type symbols */ + trait TypeSymbolApi extends SymbolApi with HasFlagsApi with TypeSymbolBase { this: TypeSymbol => + /** Is the type parameter represented by this symbol contravariant? + */ + def isContravariant : Boolean + + /** Is the type parameter represented by this symbol contravariant? + */ + def isCovariant : Boolean + + /** Does this symbol represent the definition of a skolem? + * Skolems are used during typechecking to represent type parameters viewed from inside their scopes. + * If yes, `isType` is also guaranteed to be true. + */ + def isSkolem : Boolean + + /** A type reference that refers to this type symbol seen + * as a member of given type `site`. + */ + def asTypeIn(site: Type): Type + + /** A type reference that refers to this type symbol + * Note if symbol is a member of a class, one almost always is interested + * in `asTypeIn` with a site type instead. + * + * Example: Given a class declaration `class C[T] { ... } `, that generates a symbol + * `C`. Then `C.asType` is the type `C[T]`. + * + * By contrast, `C.typeSignature` would be a type signature of form + * `PolyType(ClassInfoType(...))` that describes type parameters, value + * parameters, parent types, and members of `C`. + */ + def asType: Type // !!! Same as typeSignature. + } + + /** The API of method symbols */ + type MethodSymbolApi = MethodSymbolBase + + /** The API of module symbols */ + type ModuleSymbolApi = ModuleSymbolBase + + /** The API of class symbols */ + trait ClassSymbolApi extends TypeSymbolApi with ClassSymbolBase { this: ClassSymbol => + /** If this symbol is a class or trait, its self type, otherwise the type + * of the symbol itself. + */ + def selfType: Type + + /** The type `C.this`, where `C` is the current class */ + def thisPrefix: Type + } + + /** The API of free term symbols */ + trait FreeTermSymbolApi extends TermSymbolApi with FreeTermSymbolBase { this: FreeTermSymbol => + /** The place where this symbol has been spawned */ + def origin: String + + /** The valus this symbol refers to */ + def value: Any + } + + /** The API of free term symbols */ + trait FreeTypeSymbolApi extends TypeSymbolApi with FreeTypeSymbolBase { this: FreeTypeSymbol => + /** The place where this symbol has been spawned */ + def origin: String + } +} diff --git a/src/reflect/scala/reflect/api/TagInterop.scala b/src/reflect/scala/reflect/api/TagInterop.scala new file mode 100644 index 0000000000..e10b89d1c6 --- /dev/null +++ b/src/reflect/scala/reflect/api/TagInterop.scala @@ -0,0 +1,38 @@ +package scala.reflect +package api + +import scala.reflect.base.TypeCreator +import scala.reflect.base.{Universe => BaseUniverse} + +// [Martin] Moved to compiler because it needs to see runtime.Universe +// The two will be united in scala-reflect anyway. +trait TagInterop { self: JavaUniverse => + + // [Eugene++] would be great if we could approximate the interop without any mirrors + // todo. think how to implement that + + override def concreteTypeTagToManifest[T: ClassTag](mirror0: Any, tag: base.Universe # ConcreteTypeTag[T]): Manifest[T] = { + // [Eugene++] implement more sophisticated logic + // Martin said it'd be okay to simply copypaste `Implicits.manifestOfType` + val mirror = mirror0.asInstanceOf[Mirror] + val runtimeClass = mirror.runtimeClass(tag.in(mirror).tpe) + Manifest.classType(runtimeClass).asInstanceOf[Manifest[T]] + } + + override def manifestToConcreteTypeTag[T](mirror0: Any, manifest: Manifest[T]): base.Universe # ConcreteTypeTag[T] = + ConcreteTypeTag(mirror0.asInstanceOf[Mirror], new TypeCreator { + def apply[U <: BaseUniverse with Singleton](mirror: MirrorOf[U]): U # Type = { + mirror.universe match { + case ju: JavaUniverse => + val jm = mirror.asInstanceOf[ju.Mirror] + val sym = jm.reflectClass(manifest.erasure).symbol + val tpe = + if (manifest.typeArguments.isEmpty) sym.asType + else ju.appliedType(sym.asTypeConstructor, manifest.typeArguments map (targ => ju.manifestToConcreteTypeTag(jm, targ)) map (_.in(jm).tpe)) + tpe.asInstanceOf[U # Type] + case u => + u.manifestToConcreteTypeTag(mirror.asInstanceOf[u.Mirror], manifest).in(mirror).tpe + } + } + }) +} diff --git a/src/reflect/scala/reflect/api/TreePrinters.scala b/src/reflect/scala/reflect/api/TreePrinters.scala new file mode 100644 index 0000000000..08a08e7b90 --- /dev/null +++ b/src/reflect/scala/reflect/api/TreePrinters.scala @@ -0,0 +1,87 @@ +package scala.reflect +package api + +import java.io.{ PrintWriter, StringWriter } + +trait TreePrinters { self: Universe => + + trait TreePrinter { + def print(args: Any*) + protected var typesPrinted = false + protected var uniqueIds = false + def withTypesPrinted: this.type = { typesPrinted = true; this } + def withUniqueIds: this.type = { uniqueIds = true; this } + } + + def show(tree: Tree): String = show(tree, newTreePrinter) + + def show(tree: Tree, mkPrinter: PrintWriter => TreePrinter): String = { + val buffer = new StringWriter() + val writer = new PrintWriter(buffer) + val printer = mkPrinter(writer) + printer.print(tree) + writer.flush() + buffer.toString + } + + def showRaw(tree: Tree): String = show(tree, new RawTreePrinter(_)) + + /** Hook to define what `show(tree)` means. + */ + def newTreePrinter(out: PrintWriter): TreePrinter + + // emits more or less verbatim representation of the provided tree + // [Eugene] todo. needs to be refined + // http://groups.google.com/group/scala-user/browse_thread/thread/de5a5be2e083cf8e + class RawTreePrinter(out: PrintWriter) extends TreePrinter { + def print(args: Any*): Unit = args foreach { + case EmptyTree => + print("EmptyTree") + case tree @ TypeTree() => + print("TypeTree()") + if (tree.tpe != null) + print(".setType(", tree.tpe, ")") + else if (tree.original != null) + print(".setOriginal(", tree.original, ")") + case Literal(Constant(s: String)) => + print("Literal(Constant(\"" + s + "\"))") + case tree: Tree => + print(tree.productPrefix+"(") + val it = tree.productIterator + while (it.hasNext) { + it.next() match { + case name: Name if uniqueIds && tree.hasSymbol && tree.symbol != NoSymbol => + print(tree.symbol.name, "#", tree.symbol.id) + case arg => + print(arg) + } + print(if (it.hasNext) ", " else "") + } + print(")") + if (typesPrinted) + print(".setType(", tree.tpe, ")") + case list: List[_] => + print("List(") + val it = list.iterator + while (it.hasNext) { + print(it.next()) + print(if (it.hasNext) ", " else "") + } + print(")") + case mods: Modifiers => + val parts = collection.mutable.ListBuffer[String]() + parts += mods.flagString + if (mods.privateWithin.toString.nonEmpty) + parts += "newTypeName(\"" + mods.privateWithin.toString + "\")" + if (mods.annotations.nonEmpty) + parts += mods.annotations map showRaw mkString ("List(", ", ", ")") + print(parts mkString ("Modifiers(", ", ", ")")) + case name: Name => + if (name.isTermName) print("newTermName(\"") else print("newTypeName(\"") + print(name.toString) + print("\")") + case arg => + out.print(arg) + } + } +} diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala new file mode 100644 index 0000000000..2d130daa4e --- /dev/null +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -0,0 +1,689 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect +package api + +// Syncnote: Trees are currently not thread-safe. +trait Trees extends base.Trees { self: Universe => + + override type Tree >: Null <: TreeApi + + /** ... */ + trait TreeApi extends TreeBase { this: Tree => + + /** ... */ + def pos: Position + + /** ... */ + def tpe: Type + + /** Note that symbol is fixed as null at this level. In SymTrees, + * it is overridden and implemented with a var, initialized to NoSymbol. + * + * Trees which are not SymTrees but which carry symbols do so by + * overriding `def symbol` to forward it elsewhere. Examples: + * + * Super(qual, _) // has qual's symbol + * Apply(fun, args) // has fun's symbol + * TypeApply(fun, args) // has fun's symbol + * AppliedTypeTree(tpt, args) // has tpt's symbol + * TypeTree(tpe) // has tpe's typeSymbol, if tpe != null + * + * Attempting to set the symbol of a Tree which does not support + * it will induce an exception. + */ + def symbol: Symbol + + /** ... */ + def hasSymbol: Boolean + + /** Provides an alternate if tree is empty + * @param alt The alternate tree + * @return If this tree is non empty, this tree, otherwise `alt`. + */ + def orElse(alt: => Tree): Tree + + /** Apply `f` to each subtree */ + def foreach(f: Tree => Unit): Unit + + /** Find all subtrees matching predicate `p`. Same as `filter` */ + def withFilter(f: Tree => Boolean): List[Tree] + + /** Find all subtrees matching predicate `p`. Same as `withFilter` */ + def filter(f: Tree => Boolean): List[Tree] + + /** Apply `pf' to each subtree on which the function is defined and collect the results. + */ + def collect[T](pf: PartialFunction[Tree, T]): List[T] + + /** Returns optionally first tree (in a preorder traversal) which satisfies predicate `p`, + * or None if none exists. + */ + def find(p: Tree => Boolean): Option[Tree] + + /** Is there exists a part of this tree which satisfies predicate `p`? */ + def exists(p: Tree => Boolean): Boolean + + /** Do all parts of this tree satisfy predicate `p`? */ + def forAll(p: Tree => Boolean): Boolean + + /** Tests whether two trees are structurall equal. + * Note that `==` on trees is reference equality. + */ + def equalsStructure(that : Tree): Boolean + + /** The direct child trees of this tree. + * EmptyTrees are always omitted. Lists are flattened. + */ + def children: List[Tree] + + /** Extracts free term symbols from a tree that is reified or contains reified subtrees. + */ + def freeTerms: List[FreeTermSymbol] + + /** Extracts free type symbols from a tree that is reified or contains reified subtrees. + */ + def freeTypes: List[FreeTypeSymbol] + + /** Substitute symbols in `to` for corresponding occurrences of references to + * symbols `from` in this type. + */ + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree + + /** Substitute types in `to` for corresponding occurrences of references to + * symbols `from` in this tree. + */ + def substituteTypes(from: List[Symbol], to: List[Type]): Tree + + /** Substitute given tree `to` for occurrences of nodes that represent + * `C.this`, where `C` referes to the given class `clazz`. + */ + def substituteThis(clazz: Symbol, to: Tree): Tree + + /** 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). + */ + def duplicate: this.type + } + + override type TermTree >: Null <: Tree with TermTreeApi + + /** The API that all term trees support */ + trait TermTreeApi extends TreeApi { this: TermTree => + } + + override type TypTree >: Null <: Tree with TypTreeApi + + /** The API that all typ trees support */ + trait TypTreeApi extends TreeApi { this: TypTree => + } + + override type SymTree >: Null <: Tree with SymTreeApi + + /** The API that all sym trees support */ + trait SymTreeApi extends TreeApi { this: SymTree => + def symbol: Symbol + } + + override type NameTree >: Null <: Tree with NameTreeApi + + /** The API that all name trees support */ + trait NameTreeApi extends TreeApi { this: NameTree => + def name: Name + } + + override type RefTree >: Null <: SymTree with NameTree with RefTreeApi + + /** The API that all ref trees support */ + trait RefTreeApi extends SymTreeApi with NameTreeApi { this: RefTree => + def qualifier: Tree // empty for Idents + def name: Name + } + + override type DefTree >: Null <: SymTree with NameTree with DefTreeApi + + /** The API that all def trees support */ + trait DefTreeApi extends SymTreeApi with NameTreeApi { this: DefTree => + def name: Name + } + + override type MemberDef >: Null <: DefTree with MemberDefApi + + /** The API that all member defs support */ + trait MemberDefApi extends DefTreeApi { this: MemberDef => + def mods: Modifiers + } + + override type PackageDef >: Null <: MemberDef with PackageDefApi + + /** The API that all package defs support */ + trait PackageDefApi extends MemberDefApi { this: PackageDef => + val pid: RefTree + val stats: List[Tree] + } + + override type ImplDef >: Null <: MemberDef with ImplDefApi + + /** The API that all impl defs support */ + trait ImplDefApi extends MemberDefApi { this: ImplDef => + val impl: Template + } + + override type ClassDef >: Null <: ImplDef with ClassDefApi + + /** The API that all class defs support */ + trait ClassDefApi extends ImplDefApi { this: ClassDef => + val mods: Modifiers + val name: TypeName + val tparams: List[TypeDef] + val impl: Template + } + + override type ModuleDef >: Null <: ImplDef with ModuleDefApi + + /** The API that all module defs support */ + trait ModuleDefApi extends ImplDefApi { this: ModuleDef => + val mods: Modifiers + val name: TermName + val impl: Template + } + + override type ValOrDefDef >: Null <: MemberDef with ValOrDefDefApi + + /** The API that all val defs and def defs support */ + trait ValOrDefDefApi extends MemberDefApi { this: ValOrDefDef => + def name: Name // can't be a TermName because macros can be type names. + def tpt: Tree + def rhs: Tree + } + + override type ValDef >: Null <: ValOrDefDef with ValDefApi + + /** The API that all val defs support */ + trait ValDefApi extends ValOrDefDefApi { this: ValDef => + val mods: Modifiers + val name: TermName + val tpt: Tree + val rhs: Tree + } + + override type DefDef >: Null <: ValOrDefDef with DefDefApi + + /** The API that all def defs support */ + trait DefDefApi extends ValOrDefDefApi { this: DefDef => + val mods: Modifiers + val name: Name + val tparams: List[TypeDef] + val vparamss: List[List[ValDef]] + val tpt: Tree + val rhs: Tree + } + + override type TypeDef >: Null <: MemberDef with TypeDefApi + + /** The API that all type defs support */ + trait TypeDefApi extends MemberDefApi { this: TypeDef => + val mods: Modifiers + val name: TypeName + val tparams: List[TypeDef] + val rhs: Tree + } + + override type LabelDef >: Null <: DefTree with TermTree with LabelDefApi + + /** The API that all label defs support */ + trait LabelDefApi extends DefTreeApi with TermTreeApi { this: LabelDef => + val name: TermName + val params: List[Ident] + val rhs: Tree + } + + override type ImportSelector >: Null <: ImportSelectorApi + + /** The API that all import selectors support */ + trait ImportSelectorApi { this: ImportSelector => + val name: Name + val namePos: Int + val rename: Name + val renamePos: Int + } + + override type Import >: Null <: SymTree with ImportApi + + /** The API that all imports support */ + trait ImportApi extends SymTreeApi { this: Import => + val expr: Tree + val selectors: List[ImportSelector] + } + + override type Template >: Null <: SymTree with TemplateApi + + /** The API that all templates support */ + trait TemplateApi extends SymTreeApi { this: Template => + val parents: List[Tree] + val self: ValDef + val body: List[Tree] + } + + override type Block >: Null <: TermTree with BlockApi + + /** The API that all blocks support */ + trait BlockApi extends TermTreeApi { this: Block => + val stats: List[Tree] + val expr: Tree + } + + override type CaseDef >: Null <: Tree with CaseDefApi + + /** The API that all case defs support */ + trait CaseDefApi extends TreeApi { this: CaseDef => + val pat: Tree + val guard: Tree + val body: Tree + } + + override type Alternative >: Null <: TermTree with AlternativeApi + + /** The API that all alternatives support */ + trait AlternativeApi extends TermTreeApi { this: Alternative => + val trees: List[Tree] + } + + override type Star >: Null <: TermTree with StarApi + + /** The API that all stars support */ + trait StarApi extends TermTreeApi { this: Star => + val elem: Tree + } + + override type Bind >: Null <: DefTree with BindApi + + /** The API that all binds support */ + trait BindApi extends DefTreeApi { this: Bind => + val name: Name + val body: Tree + } + + override type UnApply >: Null <: TermTree with UnApplyApi + + /** The API that all unapplies support */ + trait UnApplyApi extends TermTreeApi { this: UnApply => + val fun: Tree + val args: List[Tree] + } + + override type ArrayValue >: Null <: TermTree with ArrayValueApi + + /** The API that all array values support */ + trait ArrayValueApi extends TermTreeApi { this: ArrayValue => + val elemtpt: Tree + val elems: List[Tree] + } + + override type Function >: Null <: TermTree with SymTree with FunctionApi + + /** The API that all functions support */ + trait FunctionApi extends TermTreeApi with SymTreeApi { this: Function => + val vparams: List[ValDef] + val body: Tree + } + + override type Assign >: Null <: TermTree with AssignApi + + /** The API that all assigns support */ + trait AssignApi extends TermTreeApi { this: Assign => + val lhs: Tree + val rhs: Tree + } + + override type AssignOrNamedArg >: Null <: TermTree with AssignOrNamedArgApi + + /** The API that all assigns support */ + trait AssignOrNamedArgApi extends TermTreeApi { this: AssignOrNamedArg => + val lhs: Tree + val rhs: Tree + } + + override type If >: Null <: TermTree with IfApi + + /** The API that all ifs support */ + trait IfApi extends TermTreeApi { this: If => + val cond: Tree + val thenp: Tree + val elsep: Tree + } + + override type Match >: Null <: TermTree with MatchApi + + /** The API that all matches support */ + trait MatchApi extends TermTreeApi { this: Match => + val selector: Tree + val cases: List[CaseDef] + } + + override type Return >: Null <: TermTree with SymTree with ReturnApi + + /** The API that all returns support */ + trait ReturnApi extends TermTreeApi { this: Return => + val expr: Tree + } + + override type Try >: Null <: TermTree with TryApi + + /** The API that all tries support */ + trait TryApi extends TermTreeApi { this: Try => + val block: Tree + val catches: List[CaseDef] + val finalizer: Tree + } + + override type Throw >: Null <: TermTree with ThrowApi + + /** The API that all tries support */ + trait ThrowApi extends TermTreeApi { this: Throw => + val expr: Tree + } + + override type New >: Null <: TermTree with NewApi + + /** The API that all news support */ + trait NewApi extends TermTreeApi { this: New => + val tpt: Tree + } + + override type Typed >: Null <: TermTree with TypedApi + + /** The API that all typeds support */ + trait TypedApi extends TermTreeApi { this: Typed => + val expr: Tree + val tpt: Tree + } + + override type GenericApply >: Null <: TermTree with GenericApplyApi + + /** The API that all applies support */ + trait GenericApplyApi extends TermTreeApi { this: GenericApply => + val fun: Tree + val args: List[Tree] + } + + override type TypeApply >: Null <: GenericApply with TypeApplyApi + + /** The API that all type applies support */ + trait TypeApplyApi extends GenericApplyApi { this: TypeApply => + } + + override type Apply >: Null <: GenericApply with ApplyApi + + /** The API that all applies support */ + trait ApplyApi extends GenericApplyApi { this: Apply => + } + + override type ApplyDynamic >: Null <: TermTree with SymTree with ApplyDynamicApi + + /** The API that all apply dynamics support */ + trait ApplyDynamicApi extends TermTreeApi with SymTreeApi { this: ApplyDynamic => + val qual: Tree + val args: List[Tree] + } + + override type Super >: Null <: TermTree with SuperApi + + /** The API that all supers support */ + trait SuperApi extends TermTreeApi { this: Super => + val qual: Tree + val mix: TypeName + } + + override type This >: Null <: TermTree with SymTree with ThisApi + + /** The API that all thises support */ + trait ThisApi extends TermTreeApi with SymTreeApi { this: This => + val qual: TypeName + } + + override type Select >: Null <: RefTree with SelectApi + + /** The API that all selects support */ + trait SelectApi extends RefTreeApi { this: Select => + val qualifier: Tree + val name: Name + } + + override type Ident >: Null <: RefTree with IdentApi + + /** The API that all idents support */ + trait IdentApi extends RefTreeApi { this: Ident => + val name: Name + } + + override type ReferenceToBoxed >: Null <: TermTree with ReferenceToBoxedApi + + /** The API that all references support */ + trait ReferenceToBoxedApi extends TermTreeApi { this: ReferenceToBoxed => + val ident: Tree + } + + override type Literal >: Null <: TermTree with LiteralApi + + /** The API that all literals support */ + trait LiteralApi extends TermTreeApi { this: Literal => + val value: Constant + } + + override type Annotated >: Null <: Tree with AnnotatedApi + + /** The API that all annotateds support */ + trait AnnotatedApi extends TreeApi { this: Annotated => + val annot: Tree + val arg: Tree + } + + override type SingletonTypeTree >: Null <: TypTree with SingletonTypeTreeApi + + /** The API that all singleton type trees support */ + trait SingletonTypeTreeApi extends TypTreeApi { this: SingletonTypeTree => + val ref: Tree + } + + override type SelectFromTypeTree >: Null <: TypTree with RefTree with SelectFromTypeTreeApi + + /** The API that all selects from type trees support */ + trait SelectFromTypeTreeApi extends TypTreeApi with RefTreeApi { this: SelectFromTypeTree => + val qualifier: Tree + val name: TypeName + } + + override type CompoundTypeTree >: Null <: TypTree with CompoundTypeTreeApi + + /** The API that all compound type trees support */ + trait CompoundTypeTreeApi extends TypTreeApi { this: CompoundTypeTree => + val templ: Template + } + + override type AppliedTypeTree >: Null <: TypTree with AppliedTypeTreeApi + + /** The API that all applied type trees support */ + trait AppliedTypeTreeApi extends TypTreeApi { this: AppliedTypeTree => + val tpt: Tree + val args: List[Tree] + } + + override type TypeBoundsTree >: Null <: TypTree with TypeBoundsTreeApi + + /** The API that all type bound trees support */ + trait TypeBoundsTreeApi extends TypTreeApi { this: TypeBoundsTree => + val lo: Tree + val hi: Tree + } + + override type ExistentialTypeTree >: Null <: TypTree with ExistentialTypeTreeApi + + /** The API that all existential type trees support */ + trait ExistentialTypeTreeApi extends TypTreeApi { this: ExistentialTypeTree => + val tpt: Tree + val whereClauses: List[Tree] + } + + override type TypeTree >: Null <: TypTree with TypeTreeApi + + /** The API that all type trees support */ + trait TypeTreeApi extends TypTreeApi { this: TypeTree => + def original: Tree + } + + /** An empty deferred value definition corresponding to: + * val _: _ + * This is used as a placeholder in the `self` parameter Template if there is + * no definition of a self value of self type. + */ + val emptyValDef: ValDef + +// ---------------------- copying ------------------------------------------------ + + /** The standard (lazy) tree copier + */ + type TreeCopier <: TreeCopierOps + val treeCopy: TreeCopier = newLazyTreeCopier + + def newStrictTreeCopier: TreeCopier + def newLazyTreeCopier: TreeCopier + + /** The API of a tree copier + * tree copiers are made available by an implicit conversion in reflect.ops + */ + abstract class TreeCopierOps { + 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 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 ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed + def Literal(tree: Tree, value: Constant): Literal + def TypeTree(tree: Tree): TypeTree + 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 + } + +// ---------------------- traversing and transforming ------------------------------ + + class Traverser { + protected[scala] var currentOwner: Symbol = rootMirror.RootClass + + def traverse(tree: Tree): Unit = itraverse(this, tree) + + 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 } + } + + protected def itraverse(traverser: Traverser, tree: Tree): Unit = throw new MatchError(tree) + + protected def xtraverse(traverser: Traverser, tree: Tree): Unit = throw new MatchError(tree) + + abstract class Transformer { + val treeCopy: TreeCopier = newLazyTreeCopier + protected[scala] var currentOwner: Symbol = rootMirror.RootClass + protected def currentMethod = currentOwner.enclosingMethod + protected def currentClass = currentOwner.enclosingClass +// protected def currentPackage = currentOwner.enclosingTopLevelClass.owner + def transform(tree: Tree): Tree = itransform(this, tree) + + 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 = + mods.mapAnnotations(transformTrees) + + def atOwner[A](owner: Symbol)(trans: => A): A = { + val prevOwner = currentOwner + currentOwner = owner + val result = trans + currentOwner = prevOwner + result + } + } + + protected def itransform(transformer: Transformer, tree: Tree): Tree = throw new MatchError(tree) + + protected def xtransform(transformer: Transformer, tree: Tree): Tree = throw new MatchError(tree) + + type Modifiers >: Null <: ModifiersApi + + abstract class ModifiersApi extends ModifiersBase with HasFlagsApi + +} + diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala new file mode 100644 index 0000000000..b62a92cbd7 --- /dev/null +++ b/src/reflect/scala/reflect/api/Types.scala @@ -0,0 +1,368 @@ +package scala.reflect +package api + +trait Types extends base.Types { self: Universe => + + override type Type >: Null <: TypeApi + + /** The extended API of types + */ + abstract class TypeApi extends TypeBase { + + /** 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 declaration(name: Name): Symbol + + /** The collection of declarations in this type + * [Eugene++] why not List? + */ + def declarations: Iterable[Symbol] + + /** The member with given name, either directly declared or inherited, + * an OverloadedSymbol if several exist, NoSymbol if none exist. + */ + def member(name: Name): Symbol + + /** The non-private member with given name, either directly declared or inherited, + * an OverloadedSymbol if several exist, NoSymbol if none exist. + */ + def nonPrivateMember(name: Name): Symbol + + /** An iterable containing all members of this type (directly declared or inherited) + * Members appear in the linearization order of their owners. + * Members with the same owner appear in reverse order of their declarations. + * [Eugene++] the order needs to be reversed back, at least in the public API + */ + def members: Iterable[Symbol] + + /** An iterable containing all non-private members of this type (directly declared or inherited) + * Members appear in the linearization order of their owners. + * Members with the same owner appear in reverse order of their declarations. + */ + def nonPrivateMembers: Iterable[Symbol] + + /** Substitute symbols in `to` for corresponding occurrences of references to + * symbols `from` in this type. + */ + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Type + + /** Substitute types in `to` for corresponding occurrences of references to + * symbols `from` in this type. + */ + def substituteTypes(from: List[Symbol], to: List[Type]): Type + + /** If this is a parameterized types, the type arguments. + * Otherwise the empty list + */ + def typeArguments: List[Type] + + /** For a (potentially wrapped) poly type, its type parameters, + * the empty list for all other types */ + def typeParams: List[Symbol] + + /** Is this type a type constructor that is missing its type arguments? + */ + def isHigherKinded: Boolean // !!! This should be called "isTypeConstructor", no? + + /** Returns the corresponding type constructor (e.g. List for List[T] or List[String]) + */ + def typeConstructor: Type + + /** Does this type refer to spliceable types or is a spliceable type? + */ + def isConcrete: Boolean + + /** Is this type an abstract type that needs to be resolved? + */ + def isSpliceable: Boolean + + /** + * 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: Type // !!! Alternative name? "normalize" is used to mean too many things. + + /** Does this type conform to given type argument `that`? */ + def <:< (that: Type): Boolean + + /** Is this type equivalent to given type argument `that`? */ + def =:= (that: Type): Boolean + + /** The list of all base classes 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] // !!! Alternative name, perhaps linearization? + + /** 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 + + /** 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 + + /** The erased type corresponding to this type after + * all transformations from Scala to Java have been performed. + */ + def erasure: Type // !!! "erasedType", compare with "widen" (so "erase") or "underlying" (so "erased") + // why not name it "erasure"? + + /** Apply `f` to each part of this type, returning + * a new type. children get mapped before their parents */ + def map(f: Type => Type): Type + + /** Apply `f` to each part of this type, for side effects only */ + def foreach(f: Type => Unit) + + /** Returns optionally first type (in a preorder traversal) which satisfies predicate `p`, + * or None if none exists. + */ + def find(p: Type => Boolean): Option[Type] + + /** Is there part of this type which satisfies predicate `p`? */ + def exists(p: Type => Boolean): Boolean + + /** Does this type contain a reference to given symbol? */ + def contains(sym: Symbol): Boolean + + /** If this is a compound type, the list of its parent types; + * otherwise the empty list + */ + def parents: List[Type] + + /** If this is a singleton type, returns the type underlying it; + * otherwise returns this type itself. + */ + def underlying: Type + + /** If this is a singleton type, widen it to its nearest underlying non-singleton + * base type by applying one or more `underlying` dereferences. + * If this is not a singleton type, returns this type itself. + * + * Example: + * + * class Outer { class C ; val x: C } + * val o: Outer + * <o.x.type>.widen = o.C + */ + def widen: Type + + /** The string discriminator of this type; useful for debugging */ + def kind: String + } + + /** .. */ + override type ThisType >: Null <: SingletonType with ThisTypeApi + + /** The API that all this types support */ + trait ThisTypeApi extends TypeApi { this: ThisType => + val sym: Symbol + } + + /** .. */ + override type SingleType >: Null <: SingletonType with SingleTypeApi + + /** The API that all single types support */ + trait SingleTypeApi extends TypeApi { this: SingleType => + val pre: Type + val sym: Symbol + } + + /** .. */ + override type SuperType >: Null <: SingletonType with SuperTypeApi + + /** The API that all super types support */ + trait SuperTypeApi extends TypeApi { this: SuperType => + val thistpe: Type + val supertpe: Type + } + + /** .. */ + override type ConstantType >: Null <: SingletonType with ConstantTypeApi + + /** The API that all constant types support */ + trait ConstantTypeApi extends TypeApi { this: ConstantType => + val value: Constant + } + + /** .. */ + override type TypeRef >: Null <: Type with TypeRefApi + + /** The API that all type refs support */ + trait TypeRefApi extends TypeApi { this: TypeRef => + val pre: Type + val sym: Symbol + val args: List[Type] + } + + /** .. */ + override type RefinedType >: Null <: CompoundType with RefinedTypeApi + + /** The API that all refined types support */ + trait RefinedTypeApi extends TypeApi { this: RefinedType => + val parents: List[Type] + val decls: Scope + } + + /** .. */ + override type ClassInfoType >: Null <: CompoundType with ClassInfoTypeApi + + /** The API that all class info types support */ + trait ClassInfoTypeApi extends TypeApi { this: ClassInfoType => + val parents: List[Type] + val decls: Scope + val typeSymbol: Symbol + } + + /** .. */ + override type MethodType >: Null <: Type with MethodTypeApi + + /** The API that all method types support */ + trait MethodTypeApi extends TypeApi { this: MethodType => + val params: List[Symbol] + val resultType: Type + } + + /** .. */ + override type NullaryMethodType >: Null <: Type with NullaryMethodTypeApi + + /** The API that all nullary method types support */ + trait NullaryMethodTypeApi extends TypeApi { this: NullaryMethodType => + val resultType: Type + } + + /** .. */ + override type PolyType >: Null <: Type with PolyTypeApi + + /** The API that all polymorphic types support */ + trait PolyTypeApi extends TypeApi { this: PolyType => + val typeParams: List[Symbol] + val resultType: Type + } + + /** .. */ + override type ExistentialType >: Null <: Type with ExistentialTypeApi + + /** The API that all existential types support */ + trait ExistentialTypeApi extends TypeApi { this: ExistentialType => + val quantified: List[Symbol] + val underlying: Type + } + + /** .. */ + override type AnnotatedType >: Null <: Type with AnnotatedTypeApi + + /** The API that all annotated types support */ + trait AnnotatedTypeApi extends TypeApi { this: AnnotatedType => + val annotations: List[AnnotationInfo] + val underlying: Type + val selfsym: Symbol + } + + /** .. */ + override type TypeBounds >: Null <: Type with TypeBoundsApi + + /** The API that all type bounds support */ + trait TypeBoundsApi extends TypeApi { this: TypeBounds => + val lo: Type + val hi: Type + } + + /** .. */ + override type BoundedWildcardType >: Null <: Type with BoundedWildcardTypeApi + + /** The API that all this types support */ + trait BoundedWildcardTypeApi extends TypeApi { this: BoundedWildcardType => + val bounds: TypeBounds + } + + /** The least upper bound of a list of types, as determined by `<:<`. */ + def lub(xs: List[Type]): Type + + /** The greatest lower bound of a list of types, as determined by `<:<`. */ + def glb(ts: List[Type]): Type + + // Creators --------------------------------------------------------------- + // too useful and too non-trivial to be left out of public API + // [Eugene to Paul] needs review! + + /** The canonical creator for single-types */ + def singleType(pre: Type, sym: Symbol): Type + + /** the canonical creator for a refined type with a given scope */ + def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): Type + + /** The canonical creator for a refined type with an initially empty scope. + */ + def refinedType(parents: List[Type], owner: Symbol): Type + + /** The canonical creator for typerefs + */ + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself. */ + def intersectionType(tps: List[Type]): Type + + /** A creator for intersection type where intersections of a single type are + * replaced by the type itself, and repeated parent classes are merged. + * + * !!! Repeated parent classes are not merged - is this a bug in the + * comment or in the code? + */ + def intersectionType(tps: List[Type], owner: Symbol): Type + + /** A creator for type applications */ + def appliedType(tycon: Type, args: List[Type]): Type + + /** 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 + + /** A creator for existential types. This generates: + * + * {{{ + * tpe1 where { tparams } + * }}} + * + * where `tpe1` is the result of extrapolating `tpe` with regard 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 with regard 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 +} + diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala new file mode 100644 index 0000000000..002cd2e673 --- /dev/null +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -0,0 +1,68 @@ +package scala.reflect +package api + +import language.experimental.macros + +abstract class Universe extends base.Universe + with Symbols + with Types + with FlagSets + with Names + with Trees + with TreePrinters + with Constants + with Positions + with Mirrors + with StandardDefinitions + with StandardNames + with Importers + with Exprs + with AnnotationInfos +{ + + /** Given an expression, generate a tree that when compiled and executed produces the original tree. + * The produced tree will be bound to the Universe it was called from. + * + * For instance, given the abstract syntax tree representation of the <[ x + 1 ]> expression: + * + * {{{ + * Apply(Select(Ident("x"), "+"), List(Literal(Constant(1)))) + * }}} + * + * The reifier transforms it to the following expression: + * + * {{{ + * <[ + * val $u: u.type = u // where u is a reference to the Universe that calls the reify + * $u.Expr[Int]($u.Apply($u.Select($u.Ident($u.newFreeVar("x", <Int>, x), "+"), List($u.Literal($u.Constant(1)))))) + * ]> + * }}} + * + * Reification performs expression splicing (when processing Expr.splice) + * and type splicing (for every type T that has a TypeTag[T] implicit in scope): + * + * {{{ + * val two = mirror.reify(2) // Literal(Constant(2)) + * val four = mirror.reify(two.splice + two.splice) // Apply(Select(two.tree, newTermName("$plus")), List(two.tree)) + * + * def macroImpl[T](c: Context) = { + * ... + * // T here is just a type parameter, so the tree produced by reify won't be of much use in a macro expansion + * // however, if T were annotated with c.TypeTag (which would declare an implicit parameter for macroImpl) + * // then reification would subtitute T with the TypeTree that was used in a TypeApply of this particular macro invocation + * val factory = c.reify{ new Queryable[T] } + * ... + * } + * }}} + * + * The transformation looks mostly straightforward, but it has its tricky parts: + * * Reifier retains symbols and types defined outside the reified tree, however + * locally defined entities get erased and replaced with their original trees + * * Free variables are detected and wrapped in symbols of the type FreeVar + * * Mutable variables that are accessed from a local function are wrapped in refs + * * Since reified trees can be compiled outside of the scope they've been created in, + * special measures are taken to ensure that all members accessed in the reifee remain visible + */ + // implementation is magically hardwired to `scala.reflect.reify.Taggers` + def reify[T](expr: T): Expr[T] = macro ??? +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/package.scala b/src/reflect/scala/reflect/api/package.scala new file mode 100644 index 0000000000..d2fce7cf1d --- /dev/null +++ b/src/reflect/scala/reflect/api/package.scala @@ -0,0 +1,12 @@ +package scala.reflect + +package object api { + + // type and value aliases for slices of the base Universe cake that are not + // repeated in api.Universe + type Scopes = base.Scopes + type BuildUtils = base.BuildUtils + type Attachments = base.Attachments + + type MirrorOf[U <: base.Universe with Singleton] = base.MirrorOf[U] +} diff --git a/src/reflect/scala/reflect/internal/AbstractFileApi.scala b/src/reflect/scala/reflect/internal/AbstractFileApi.scala new file mode 100644 index 0000000000..9f37f4536f --- /dev/null +++ b/src/reflect/scala/reflect/internal/AbstractFileApi.scala @@ -0,0 +1,7 @@ +package scala.reflect +package internal + +trait AbstractFileApi { + def path: String + def canonicalPath: String +} diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala new file mode 100644 index 0000000000..449b0ca0bc --- /dev/null +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -0,0 +1,121 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +/** 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 + } + + // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. + /** 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/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala new file mode 100644 index 0000000000..c283ae408e --- /dev/null +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -0,0 +1,293 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import util._ +import pickling.ByteCodecs + +/** AnnotationInfo and its helpers */ +trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => + import definitions.{ ThrowsClass, StaticAnnotationClass, isMetaAnnotation } + + // Common annotation code between Symbol and Type. + // For methods altering the annotation list, on Symbol it mutates + // the Symbol's field directly. For Type, a new AnnotatedType is + // created which wraps the original type. + trait Annotatable[Self] { + /** The annotations on this type. */ + def annotations: List[AnnotationInfo] // Annotations on this type. + def setAnnotations(annots: List[AnnotationInfo]): Self // Replace annotations with argument list. + def withAnnotations(annots: List[AnnotationInfo]): Self // Add annotations to this type. + def filterAnnotations(p: AnnotationInfo => Boolean): Self // Retain only annotations meeting the condition. + def withoutAnnotations: Self // Remove all annotations from this type. + + /** Symbols of any @throws annotations on this symbol. + */ + def throwsAnnotations(): List[Symbol] = annotations collect { + case AnnotationInfo(tp, Literal(Constant(tpe: Type)) :: Nil, _) if tp.typeSymbol == ThrowsClass => tpe.typeSymbol + } + + /** Test for, get, or remove an annotation */ + def hasAnnotation(cls: Symbol) = annotations exists (_ matches cls) + def getAnnotation(cls: Symbol) = annotations find (_ matches cls) + def removeAnnotation(cls: Symbol): Self = filterAnnotations(ann => !(ann matches cls)) + final def withAnnotation(annot: AnnotationInfo): Self = withAnnotations(List(annot)) + } + + /** Arguments to classfile annotations (which are written to + * bytecode as java annotations) are either: + * + * - constants + * - arrays of constants + * - or nested classfile annotations + */ + abstract class ClassfileAnnotArg extends Product + implicit val ClassfileAnnotArgTag = ClassTag[ClassfileAnnotArg](classOf[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 with LiteralAnnotArgApi { + override def toString = const.escapedStringValue + } + implicit val LiteralAnnotArgTag = ClassTag[LiteralAnnotArg](classOf[LiteralAnnotArg]) + + object LiteralAnnotArg extends LiteralAnnotArgExtractor + + /** Represents an array of classfile annotation arguments */ + case class ArrayAnnotArg(args: Array[ClassfileAnnotArg]) + extends ClassfileAnnotArg with ArrayAnnotArgApi { + override def toString = args.mkString("[", ", ", "]") + } + implicit val ArrayAnnotArgTag = ClassTag[ArrayAnnotArg](classOf[ArrayAnnotArg]) + + object ArrayAnnotArg extends ArrayAnnotArgExtractor + + /** 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 = ByteCodecs.encode(bytes) // TODO remove after migration to ASM-based GenJVM complete + def isLong: Boolean = (encodedBytes.length > 65535) // TODO remove after migration to ASM-based GenJVM complete + lazy val sevenBitsMayBeZero: Array[Byte] = { + mapToNextModSevenBits(scala.reflect.internal.pickling.ByteCodecs.encode8to7(bytes)) + } + def fitsInOneString: Boolean = { + val numZeros = (sevenBitsMayBeZero count { b => b == 0 }) + val res = (sevenBitsMayBeZero.length + numZeros) <= 65535 + assert(this.isLong == !res, "As things stand, can't just swap in `fitsInOneString()` for `isLong()`") + res + } + def sigAnnot: Type = + if (this.isLong) + definitions.ScalaLongSignatureAnnotation.tpe + else + definitions.ScalaSignatureAnnotation.tpe + + private def mapToNextModSevenBits(src: Array[Byte]): Array[Byte] = { + var i = 0 + val srclen = src.length + while (i < srclen) { + val in = src(i) + src(i) = (if (in == 0x7f) 0.toByte else (in + 1).toByte) + i += 1 + } + src + } + + } + + /** Represents a nested classfile annotation */ + case class NestedAnnotArg(annInfo: AnnotationInfo) extends ClassfileAnnotArg with NestedAnnotArgApi { + // The nested annotation should not have any Scala annotation arguments + assert(annInfo.args.isEmpty, annInfo.args) + override def toString = annInfo.toString + } + implicit val NestedAnnotArgTag = ClassTag[NestedAnnotArg](classOf[NestedAnnotArg]) + + object NestedAnnotArg extends NestedAnnotArgExtractor + + object AnnotationInfo extends AnnotationInfoExtractor { + def marker(atp: Type): AnnotationInfo = + apply(atp, Nil, Nil) + + def lazily(lazyInfo: => AnnotationInfo) = + new LazyAnnotationInfo(lazyInfo) + + def apply(atp: Type, args: List[Tree], assocs: List[(Name, ClassfileAnnotArg)]): AnnotationInfo = + new CompleteAnnotationInfo(atp, args, assocs) + + def unapply(info: AnnotationInfo): Option[(Type, List[Tree], List[(Name, ClassfileAnnotArg)])] = + Some((info.atp, info.args, info.assocs)) + } + + class CompleteAnnotationInfo( + val atp: Type, + val args: List[Tree], + val assocs: List[(Name, ClassfileAnnotArg)] + ) extends AnnotationInfo { + // Classfile annot: args empty. Scala annot: assocs empty. + assert(args.isEmpty || assocs.isEmpty, atp) + + // necessary for reification, see Reifiers.scala for more info + private var orig: Tree = EmptyTree + def original = orig + def setOriginal(t: Tree): this.type = { + orig = t + this setPos t.pos + this + } + + override def toString = ( + atp + + (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + + (if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "") + ) + } + + /** Symbol annotations parsed in `Namer` (typeCompleter of + * definitions) have to be lazy (#1782) + */ + final class LazyAnnotationInfo(lazyInfo: => AnnotationInfo) extends AnnotationInfo { + private var forced = false + private lazy val forcedInfo = + try { + val result = lazyInfo + if (result.pos == NoPosition) result setPos pos + result + } finally forced = true + + def atp: Type = forcedInfo.atp + def args: List[Tree] = forcedInfo.args + def assocs: List[(Name, ClassfileAnnotArg)] = forcedInfo.assocs + def original: Tree = forcedInfo.original + def setOriginal(t: Tree): this.type = { forcedInfo.setOriginal(t); this } + + // We should always be able to print things without forcing them. + override def toString = if (forced) forcedInfo.toString else "@<?>" + + override def pos: Position = if (forced) forcedInfo.pos else NoPosition + } + + /** Typed information about an annotation. It can be attached to either + * a symbol or an annotated type. + * + * Annotations are written to the classfile as Java annotations + * if `atp` conforms to `ClassfileAnnotation` (the classfile parser adds + * this interface to any Java annotation class). + * + * Annotations are pickled (written to scala symtab attribute in the + * classfile) if `atp` inherits form `StaticAnnotation`. + * + * `args` stores arguments to Scala annotations, represented as typed + * trees. Note that these trees are not transformed by any phases + * following the type-checker. + * + * `assocs` stores arguments to classfile annotations as name-value pairs. + */ + sealed abstract class AnnotationInfo extends AnnotationInfoApi { + def atp: Type + def args: List[Tree] + def assocs: List[(Name, ClassfileAnnotArg)] + + // necessary for reification, see Reifiers.scala for more info + def original: Tree + def setOriginal(t: Tree): this.type + + // see annotationArgRewriter + lazy val isTrivial = atp.isTrivial && !hasArgWhich(_.isInstanceOf[This]) + + private var rawpos: Position = NoPosition + def pos = rawpos + def setPos(pos: Position): this.type = { // Syncnote: Setpos inaccessible to reflection, so no sync in rawpos necessary. + rawpos = pos + this + } + + /** Annotations annotating annotations are confusing so I drew + * an example. Given the following code: + * + * class A { + * @(deprecated @setter) @(inline @getter) + * var x: Int = 0 + * } + * + * For the setter `x_=` in A, annotations contains one AnnotationInfo = + * List(deprecated @setter) + * The single AnnotationInfo in that list, i.e. `@(deprecated @setter)`, has metaAnnotations = + * List(setter) + * + * Similarly, the getter `x` in A has an @inline annotation, which has + * metaAnnotations = List(getter). + */ + def symbol = atp.typeSymbol + + /** These are meta-annotations attached at the use site; they + * only apply to this annotation usage. For instance, in + * `@(deprecated @setter @field) val ...` + * metaAnnotations = List(setter, field). + */ + def metaAnnotations: List[AnnotationInfo] = atp match { + case AnnotatedType(metas, _, _) => metas + case _ => Nil + } + + /** The default kind of members to which this annotation is attached. + * For instance, for scala.deprecated defaultTargets = + * List(getter, setter, beanGetter, beanSetter). + */ + def defaultTargets = symbol.annotations map (_.symbol) filter isMetaAnnotation + // Test whether the typeSymbol of atp conforms to the given class. + def matches(clazz: Symbol) = symbol isNonBottomSubClass clazz + // All subtrees of all args are considered. + def hasArgWhich(p: Tree => Boolean) = args exists (_ exists p) + + /** Check whether the type or any of the arguments are erroneous */ + def isErroneous = atp.isErroneous || args.exists(_.isErroneous) + + def isStatic = symbol isNonBottomSubClass StaticAnnotationClass + + /** Check whether any of the arguments mention a symbol */ + def refsSymbol(sym: Symbol) = hasArgWhich(_.symbol == sym) + + /** Change all ident's with Symbol "from" to instead use symbol "to" */ + def substIdentSyms(from: Symbol, to: Symbol) = + AnnotationInfo(atp, args map (_ substituteSymbols (List(from), List(to))), assocs) setPos pos + + def stringArg(index: Int) = constantAtIndex(index) map (_.stringValue) + def intArg(index: Int) = constantAtIndex(index) map (_.intValue) + def symbolArg(index: Int) = argAtIndex(index) collect { + case Apply(fun, Literal(str) :: Nil) if fun.symbol == definitions.Symbol_apply => + newTermName(str.stringValue) + } + + // !!! when annotation arguments are not literals, but any sort of + // expression, there is a fair chance they will turn up here not as + // Literal(const) but some arbitrary AST. + def constantAtIndex(index: Int): Option[Constant] = + argAtIndex(index) collect { case Literal(x) => x } + + def argAtIndex(index: Int): Option[Tree] = + if (index < args.size) Some(args(index)) else None + + override def hashCode = atp.## + args.## + assocs.## + override def equals(other: Any) = other match { + case x: AnnotationInfo => (atp == x.atp) && (args == x.args) && (assocs == x.assocs) + case _ => false + } + } + + implicit val AnnotationInfoTag = ClassTag[AnnotationInfo](classOf[AnnotationInfo]) + + object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil) +} diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala new file mode 100644 index 0000000000..e07f1bac49 --- /dev/null +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -0,0 +1,260 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect +package internal + +// todo implement in terms of BitSet +import scala.collection.{ mutable, immutable } +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._ + + protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + new BaseTypeSeq(parents, elems) + + /** Note: constructor is protected to force everyone to use the factory method newBaseTypeSeq instead. + * This is necessary because when run from reflection every base type sequence needs to have a + * SynchronizedBaseTypeSeq as mixin. + */ + class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val 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) + private val pending = new mutable.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 + + 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 + newBaseTypeSeq(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 + } + newBaseTypeSeq(parents, arr) + } + + def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) + + 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 = newBaseTypeSeq(List(), Array()) + + /** Create a base type sequence consisting of a single type */ + def baseTypeSingletonSeq(tp: Type): BaseTypeSeq = newBaseTypeSeq(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 mutable.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 + newBaseTypeSeq(parents, elems) + } + + class MappedBaseTypeSeq(orig: BaseTypeSeq, f: Type => Type) extends BaseTypeSeq(orig.parents map f, orig.elems) { + override def apply(i: Int) = f(orig.apply(i)) + override def rawElem(i: Int) = f(orig.rawElem(i)) + override def typeSymbol(i: Int) = orig.typeSymbol(i) + override def toList = orig.toList map f + override def copy(head: Type, offset: Int) = (orig map f).copy(head, offset) + override def map(g: Type => Type) = lateMap(g) + override def lateMap(g: Type => Type) = orig.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(", ",", ")") + } + + val CyclicInheritance = new Throwable +} diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala new file mode 100644 index 0000000000..3bde57ded8 --- /dev/null +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -0,0 +1,69 @@ +package scala.reflect +package internal + +import Flags._ + +trait BuildUtils extends base.BuildUtils { self: SymbolTable => + + class BuildImpl extends BuildBase { + + def selectType(owner: Symbol, name: String): TypeSymbol = { + val result = owner.info.decl(newTypeName(name)) + if (result ne NoSymbol) result.asTypeSymbol + else MissingRequirementError.notFound("type %s in %s".format(name, owner.fullName)) + } + + def selectTerm(owner: Symbol, name: String): TermSymbol = { + val sym = owner.info.decl(newTermName(name)) + val result = + if (sym.isOverloaded) sym.suchThat(!_.isMethod) + else sym + if (result ne NoSymbol) result.asTermSymbol + else MissingRequirementError.notFound("term %s in %s".format(name, owner.fullName)) + } + + def selectOverloadedMethod(owner: Symbol, name: String, index: Int): MethodSymbol = { + val result = owner.info.decl(newTermName(name)).alternatives(index) + if (result ne NoSymbol) result.asMethodSymbol + else MissingRequirementError.notFound("overloaded method %s #%d in %s".format(name, index, owner.fullName)) + } + + def newFreeTerm(name: String, info: Type, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = + newFreeTermSymbol(newTermName(name), info, value, flags, origin) + + def newFreeType(name: String, info: Type, value: => Any, flags: Long = 0L, origin: String = null): FreeTypeSymbol = + newFreeTypeSymbol(newTypeName(name), info, value, (if (flags == 0L) PARAM else flags) | DEFERRED, origin) + + def newFreeExistential(name: String, info: Type, value: => Any, flags: Long = 0L, origin: String = null): FreeTypeSymbol = + newFreeTypeSymbol(newTypeName(name), info, value, (if (flags == 0L) EXISTENTIAL else flags) | DEFERRED, origin) + + def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: Long, isClass: Boolean): Symbol = + owner.newNestedSymbol(name, pos, flags, isClass) + + def setAnnotations[S <: Symbol](sym: S, annots: List[AnnotationInfo]): S = + sym.setAnnotations(annots) + + def setTypeSignature[S <: Symbol](sym: S, tpe: Type): S = + sym.setTypeSignature(tpe) + + def flagsFromBits(bits: Long): FlagSet = bits + + def emptyValDef: ValDef = self.emptyValDef + + def This(sym: Symbol): Tree = self.This(sym) + + def Select(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym) + + def Ident(sym: Symbol): Ident = self.Ident(sym) + + def TypeTree(tp: Type): TypeTree = self.TypeTree(tp) + + def thisPrefix(sym: Symbol): Type = sym.thisPrefix + + def setType[T <: Tree](tree: T, tpe: Type): T = { tree.setType(tpe); tree } + + def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree } + } + + val build: BuildBase = new BuildImpl +} diff --git a/src/reflect/scala/reflect/internal/CapturedVariables.scala b/src/reflect/scala/reflect/internal/CapturedVariables.scala new file mode 100644 index 0000000000..77909d9157 --- /dev/null +++ b/src/reflect/scala/reflect/internal/CapturedVariables.scala @@ -0,0 +1,36 @@ +package scala.reflect +package internal + +import Flags._ + +trait CapturedVariables { self: SymbolTable => + + import definitions._ + + /** Mark a variable as captured; i.e. force boxing in a *Ref type. + */ + def captureVariable(vble: Symbol): Unit = vble setFlag CAPTURED + + /** Mark given identifier as a reference to a captured variable itself + * suppressing dereferencing with the `elem` field. + */ + def referenceCapturedVariable(vble: Symbol): Tree = ReferenceToBoxed(Ident(vble)) + + /** Convert type of a captured variable to *Ref type. + */ + def capturedVariableType(vble: Symbol): Type = + capturedVariableType(vble, NoType, false) + + /** Convert type of a captured variable to *Ref type. + */ + def capturedVariableType(vble: Symbol, tpe: Type = NoType, erasedTypes: Boolean = false): Type = { + val tpe1 = if (tpe == NoType) vble.tpe else tpe + val symClass = tpe1.typeSymbol + def refType(valueRef: Map[Symbol, Symbol], objectRefClass: Symbol) = + if (isPrimitiveValueClass(symClass) && symClass != UnitClass) valueRef(symClass).tpe + else if (erasedTypes) objectRefClass.tpe + else appliedType(objectRefClass, tpe) + if (vble.hasAnnotation(VolatileAttr)) refType(volatileRefClass, VolatileObjectRefClass) + else refType(refClass, ObjectRefClass) + } +} diff --git a/src/reflect/scala/reflect/internal/Chars.scala b/src/reflect/scala/reflect/internal/Chars.scala new file mode 100644 index 0000000000..50ec71094a --- /dev/null +++ b/src/reflect/scala/reflect/internal/Chars.scala @@ -0,0 +1,98 @@ +/* NSC -- new Scala compiler + * Copyright 2006-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect +package internal + +import annotation.{ tailrec, switch } +import java.lang.{ Character => JCharacter } +import language.postfixOps + +/** 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 = { + val num = ( + if (ch <= '9') ch - '0' + else if ('a' <= ch && ch <= 'z') ch - 'a' + 10 + else if ('A' <= ch && ch <= 'Z') ch - 'A' + 10 + else -1 + ) + if (0 <= num && num < base) num else -1 + } + /** Buffer for creating '\ u XXXX' strings. */ + private[this] val char2uescapeArray = Array[Char]('\\', 'u', 0, 0, 0, 0) + + /** Convert a character to a backslash-u escape */ + def char2uescape(c: Char): String = { + @inline def hexChar(ch: Int): Char = + ( if (ch < 10) '0' else 'A' - 10 ) + ch toChar + + char2uescapeArray(2) = hexChar((c >> 12) ) + char2uescapeArray(3) = hexChar((c >> 8) % 16) + char2uescapeArray(4) = hexChar((c >> 4) % 16) + char2uescapeArray(5) = hexChar((c ) % 16) + + new String(char2uescapeArray) + } + + /** 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 { } diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala new file mode 100644 index 0000000000..3346e9cccb --- /dev/null +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -0,0 +1,390 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import annotation.switch + +object ClassfileConstants { + + final val JAVA_MAGIC = 0xCAFEBABE + final val JAVA_MAJOR_VERSION = 45 + final val JAVA_MINOR_VERSION = 3 + + /** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html) + * + * If the `ACC_INTERFACE` flag is set, the `ACC_ABSTRACT` flag must also + * be set (ch. 2.13.1). + * + * A class file cannot have both its `ACC_FINAL` and `ACC_ABSTRACT` flags + * set (ch. 2.8.2). + * + * A field may have at most one of its `ACC_PRIVATE`, `ACC_PROTECTED`, + * `ACC_PUBLIC` flags set (ch. 2.7.4). + * + * A field may not have both its `ACC_FINAL` and `ACC_VOLATILE` flags set + * (ch. 2.9.1). + * + * If a method has its `ACC_ABSTRACT` flag set it must not have any of its + * `ACC_FINAL`, `ACC_NATIVE`, `ACC_PRIVATE`, `ACC_STATIC`, `ACC_STRICT`, + * or `ACC_SYNCHRONIZED` flags set (ch. 2.13.3.2). + * + * All interface methods must have their `ACC_ABSTRACT` and + * `ACC_PUBLIC` flags set. + * + * Note for future reference: see this thread on ACC_SUPER and + * how its enforcement differs on the android vm. + * https://groups.google.com/forum/?hl=en#!topic/jvm-languages/jVhzvq8-ZIk + * + */ // 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 OBJECT_TAG = 'L' + 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 + + abstract class FlagTranslation { + import Flags._ + + private var isAnnotation = false + private var isClass = false + private def initFields(flags: Int) = { + isAnnotation = (flags & JAVA_ACC_ANNOTATION) != 0 + isClass = false + } + private def translateFlag(jflag: Int): Long = (jflag: @switch) match { + case JAVA_ACC_PRIVATE => PRIVATE + case JAVA_ACC_PROTECTED => PROTECTED + case JAVA_ACC_FINAL => FINAL + case JAVA_ACC_SYNTHETIC => SYNTHETIC + case JAVA_ACC_STATIC => STATIC + case JAVA_ACC_ABSTRACT => if (isAnnotation) 0L else if (isClass) ABSTRACT else DEFERRED + case JAVA_ACC_INTERFACE => if (isAnnotation) 0L else TRAIT | INTERFACE | ABSTRACT + case _ => 0L + } + private def translateFlags(jflags: Int, baseFlags: Long): Long = { + var res: Long = JAVA | baseFlags + /** fast, elegant, maintainable, pick any two... */ + res |= translateFlag(jflags & JAVA_ACC_PRIVATE) + res |= translateFlag(jflags & JAVA_ACC_PROTECTED) + res |= translateFlag(jflags & JAVA_ACC_FINAL) + res |= translateFlag(jflags & JAVA_ACC_SYNTHETIC) + res |= translateFlag(jflags & JAVA_ACC_STATIC) + res |= translateFlag(jflags & JAVA_ACC_ABSTRACT) + res |= translateFlag(jflags & JAVA_ACC_INTERFACE) + res + } + + def classFlags(jflags: Int): Long = { + initFields(jflags) + isClass = true + translateFlags(jflags, 0) + } + def fieldFlags(jflags: Int): Long = { + initFields(jflags) + translateFlags(jflags, if ((jflags & JAVA_ACC_FINAL) == 0) MUTABLE else 0) + } + def methodFlags(jflags: Int): Long = { + initFields(jflags) + translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE else 0) + } + } + object FlagTranslation extends FlagTranslation { } + + def toScalaMethodFlags(flags: Int): Long = FlagTranslation methodFlags flags + def toScalaClassFlags(flags: Int): Long = FlagTranslation classFlags flags + def toScalaFieldFlags(flags: Int): Long = FlagTranslation fieldFlags flags + + @deprecated("Use another method in this object", "2.10.0") + def toScalaFlags(flags: Int, isClass: Boolean = false, isField: Boolean = false): Long = ( + if (isClass) toScalaClassFlags(flags) + else if (isField) toScalaFieldFlags(flags) + else toScalaMethodFlags(flags) + ) +} diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala new file mode 100644 index 0000000000..820dfe0868 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Constants.scala @@ -0,0 +1,240 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import java.lang.Integer.toOctalString +import annotation.switch + +trait Constants extends api.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 ClazzTag = 12 + // For supporting java enumerations inside java annotations (see ClassfileParser) + final val EnumTag = 13 + + case class Constant(value: Any) extends ConstantApi { + 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 => ClazzTag + case x: Symbol => EnumTag + case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass) + } + + 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 isNonUnitAnyVal = BooleanTag <= tag && tag <= DoubleTag + def isAnyVal = UnitTag <= 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 ClazzTag => 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 == ClazzTag) 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 _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) + } + + def escapedStringValue: String = { + def escape(text: String): String = text flatMap escapedChar + tag match { + case NullTag => "null" + case StringTag => "\"" + escape(stringValue) + "\"" + case ClazzTag => "classOf[" + signature(typeValue) + "]" + case CharTag => "'" + escapedChar(charValue) + "'" + case LongTag => longValue.toString() + "L" + case EnumTag => symbolValue.name.toString() + case _ => String.valueOf(value) + } + } + def typeValue: Type = value.asInstanceOf[Type] + def symbolValue: Symbol = value.asInstanceOf[Symbol] + + override def hashCode: Int = value.## * 41 + 17 + } + + object Constant extends ConstantExtractor + + implicit val ConstantTag = ClassTag[Constant](classOf[Constant]) +} diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala new file mode 100644 index 0000000000..d55b38224d --- /dev/null +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -0,0 +1,1241 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import annotation.{ switch, meta } +import scala.collection.{ mutable, immutable } +import Flags._ +import PartialFunction._ +import scala.reflect.base.{Universe => BaseUniverse} + +trait Definitions extends api.StandardDefinitions { + self: SymbolTable => + + import rootMirror.{getModule, getClassByName, getRequiredClass, getRequiredModule, getRequiredPackage, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageObjectIfDefined, requiredClass, requiredModule} + + object definitions extends DefinitionsClass + + // [Eugene] find a way to make these non-lazy + lazy val ByteTpe = definitions.ByteClass.asType + lazy val ShortTpe = definitions.ShortClass.asType + lazy val CharTpe = definitions.CharClass.asType + lazy val IntTpe = definitions.IntClass.asType + lazy val LongTpe = definitions.LongClass.asType + lazy val FloatTpe = definitions.FloatClass.asType + lazy val DoubleTpe = definitions.DoubleClass.asType + lazy val BooleanTpe = definitions.BooleanClass.asType + lazy val UnitTpe = definitions.UnitClass.asType + lazy val AnyTpe = definitions.AnyClass.asType + lazy val ObjectTpe = definitions.ObjectClass.asType + lazy val AnyValTpe = definitions.AnyValClass.asType + lazy val AnyRefTpe = definitions.AnyRefClass.asType + lazy val NothingTpe = definitions.NothingClass.asType + lazy val NullTpe = definitions.NullClass.asType + lazy val StringTpe = definitions.StringClass.asType + + /** Since both the value parameter types and the result type may + * require access to the type parameter symbols, we model polymorphic + * creation as a function from those symbols to (formal types, result type). + * The Option is to distinguish between nullary methods and empty-param-list + * methods. + */ + private type PolyMethodCreator = List[Symbol] => (Option[List[Type]], Type) + + private def enterNewClass(owner: Symbol, name: TypeName, parents: List[Type], flags: Long = 0L): ClassSymbol = { + val clazz = owner.newClassSymbol(name, NoPosition, flags) + clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz) + } + private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol = { + val msym = owner.newMethod(name.encode, NoPosition, flags) + val params = msym.newSyntheticValueParams(formals) + msym setInfo MethodType(params, restpe) + } + private def enterNewMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol = + owner.info.decls enter newMethod(owner, name, formals, restpe, flags) + + // the scala value classes + trait ValueClassDefinitions { + self: DefinitionsClass => + + 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 + ) + + private def catastrophicFailure() = + abort("Could not find value classes! This is a catastrophic failure. scala " + + scala.util.Properties.versionString) + + private def valueClassSymbol(name: TypeName): ClassSymbol = { + getMember(ScalaPackageClass, name) match { + case x: ClassSymbol => x + case _ => catastrophicFailure() + } + } + private def valueClassCompanion(name: TermName): ModuleSymbol = { + getMember(ScalaPackageClass, name) match { + case x: ModuleSymbol => x + case _ => catastrophicFailure() + } + } + private def valueCompanionMember(className: Name, methodName: TermName): TermSymbol = + getMemberMethod(valueClassCompanion(className.toTermName).moduleClass, methodName) + + private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) + private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = mapFrom(syms)(x => f(x.name)) + 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(ScalaValueClasses, nameToTag) withDefaultValue OBJECT_TAG + lazy val numericWeight = symbolsMapFilt(ScalaValueClasses, nameToWeight.keySet, nameToWeight) + lazy val boxedModule = classesMap(x => getModule(boxedName(x))) + lazy val boxedClass = classesMap(x => getClassByName(boxedName(x))) + lazy val refClass = classesMap(x => getRequiredClass("scala.runtime." + x + "Ref")) + lazy val volatileRefClass = classesMap(x => getRequiredClass("scala.runtime.Volatile" + x + "Ref")) + lazy val boxMethod = classesMap(x => valueCompanionMember(x, nme.box)) + lazy val unboxMethod = classesMap(x => valueCompanionMember(x, nme.unbox)) + + def isNumericSubClass(sub: Symbol, sup: Symbol) = ( + (numericWeight contains sub) + && (numericWeight contains sup) + && (numericWeight(sup) % numericWeight(sub) == 0) + ) + + /** Is symbol a numeric value class? */ + def isNumericValueClass(sym: Symbol) = ScalaNumericValueClasses contains sym + + def isGetClass(sym: Symbol) = + (sym.name == nme.getClass_) && flattensToEmpty(sym.paramss) + + lazy val UnitClass = valueClassSymbol(tpnme.Unit) + lazy val ByteClass = valueClassSymbol(tpnme.Byte) + lazy val ShortClass = valueClassSymbol(tpnme.Short) + lazy val CharClass = valueClassSymbol(tpnme.Char) + lazy val IntClass = valueClassSymbol(tpnme.Int) + lazy val LongClass = valueClassSymbol(tpnme.Long) + lazy val FloatClass = valueClassSymbol(tpnme.Float) + lazy val DoubleClass = valueClassSymbol(tpnme.Double) + lazy val BooleanClass = valueClassSymbol(tpnme.Boolean) + lazy val Boolean_and = getMemberMethod(BooleanClass, nme.ZAND) + lazy val Boolean_or = getMemberMethod(BooleanClass, nme.ZOR) + lazy val Boolean_not = getMemberMethod(BooleanClass, nme.UNARY_!) + + lazy val ScalaNumericValueClasses = ScalaValueClasses filterNot Set[Symbol](UnitClass, BooleanClass) + + def ScalaValueClassesNoUnit = ScalaValueClasses filterNot (_ eq UnitClass) + def ScalaValueClasses: List[ClassSymbol] = List( + UnitClass, + BooleanClass, + ByteClass, + ShortClass, + CharClass, + IntClass, + LongClass, + FloatClass, + DoubleClass + ) + def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) + def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + } + + abstract class DefinitionsClass extends DefinitionsApi with ValueClassDefinitions { + private var isInitialized = false + def isDefinitionsInitialized = isInitialized + + // symbols related to packages + var emptypackagescope: Scope = null //debug + + // It becomes tricky to create dedicated objects for other symbols because + // of initialization order issues. + lazy val JavaLangPackage = getRequiredPackage(sn.JavaLang) + lazy val JavaLangPackageClass = JavaLangPackage.moduleClass.asClassSymbol + lazy val ScalaPackage = getRequiredPackage(nme.scala_) + lazy val ScalaPackageClass = ScalaPackage.moduleClass.asClassSymbol + lazy val RuntimePackage = getRequiredPackage("scala.runtime") + lazy val RuntimePackageClass = RuntimePackage.moduleClass.asClassSymbol + + lazy val JavaLangEnumClass = requiredClass[java.lang.Enum[_]] + + // convenient one-argument parameter lists + lazy val anyparam = List(AnyClass.tpe) + lazy val anyvalparam = List(AnyValClass.typeConstructor) + lazy val anyrefparam = List(AnyRefClass.typeConstructor) + + // private parameter conveniences + private def booltype = BooleanClass.tpe + private def inttype = IntClass.tpe + private def stringtype = StringClass.tpe + + def javaTypeToValueClass(jtype: Class[_]): Symbol = jtype match { + case java.lang.Void.TYPE => UnitClass + case java.lang.Byte.TYPE => ByteClass + case java.lang.Character.TYPE => CharClass + case java.lang.Short.TYPE => ShortClass + case java.lang.Integer.TYPE => IntClass + case java.lang.Long.TYPE => LongClass + case java.lang.Float.TYPE => FloatClass + case java.lang.Double.TYPE => DoubleClass + case java.lang.Boolean.TYPE => BooleanClass + case _ => NoSymbol + } + def valueClassToJavaType(sym: Symbol): Class[_] = sym match { + case UnitClass => java.lang.Void.TYPE + case ByteClass => java.lang.Byte.TYPE + case CharClass => java.lang.Character.TYPE + case ShortClass => java.lang.Short.TYPE + case IntClass => java.lang.Integer.TYPE + case LongClass => java.lang.Long.TYPE + case FloatClass => java.lang.Float.TYPE + case DoubleClass => java.lang.Double.TYPE + case BooleanClass => java.lang.Boolean.TYPE + case _ => null + } + + private def fixupAsAnyTrait(tpe: Type): Type = tpe match { + case ClassInfoType(parents, decls, clazz) => + if (parents.head.typeSymbol == AnyClass) tpe + else { + assert(parents.head.typeSymbol == ObjectClass, parents) + ClassInfoType(AnyClass.tpe :: parents.tail, decls, clazz) + } + case PolyType(tparams, restpe) => + PolyType(tparams, fixupAsAnyTrait(restpe)) +// case _ => tpe + } + + // top types + lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) + lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectClass.tpe) + lazy val ObjectClass = getRequiredClass(sn.Object.toString) + + // Note: this is not the type alias AnyRef, it's a companion-like + // object used by the @specialize annotation. + lazy val AnyRefModule = getMemberModule(ScalaPackageClass, nme.AnyRef) + @deprecated("Use AnyRefModule", "2.10.0") + def Predef_AnyRef = AnyRefModule + + lazy val AnyValClass: ClassSymbol = (ScalaPackageClass.info member tpnme.AnyVal orElse { + val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, List(AnyClass.tpe, NotNullClass.tpe), ABSTRACT) + val av_constr = anyval.newClassConstructor(NoPosition) + anyval.info.decls enter av_constr + anyval + }).asInstanceOf[ClassSymbol] + + // bottom types + lazy val RuntimeNothingClass = getClassByName(fulltpnme.RuntimeNothing) + lazy val RuntimeNullClass = getClassByName(fulltpnme.RuntimeNull) + + sealed abstract class BottomClassSymbol(name: TypeName, parent: Symbol) extends ClassSymbol(ScalaPackageClass, NoPosition, name) { + locally { + this initFlags ABSTRACT | FINAL + this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this) + } + final override def isBottomClass = true + } + final object NothingClass extends BottomClassSymbol(tpnme.Nothing, AnyClass) { + override def isSubClass(that: Symbol) = true + } + final object NullClass extends BottomClassSymbol(tpnme.Null, AnyRefClass) { + override def isSubClass(that: Symbol) = ( + (that eq AnyClass) + || (that ne NothingClass) && (that isSubClass ObjectClass) + ) + } + + // exceptions and other throwables + lazy val ClassCastExceptionClass = requiredClass[ClassCastException] + lazy val IndexOutOfBoundsExceptionClass = getClassByName(sn.IOOBException) + lazy val InvocationTargetExceptionClass = getClassByName(sn.InvTargetException) + lazy val MatchErrorClass = requiredClass[MatchError] + lazy val NonLocalReturnControlClass = requiredClass[scala.runtime.NonLocalReturnControl[_]] + lazy val NullPointerExceptionClass = getClassByName(sn.NPException) + lazy val ThrowableClass = getClassByName(sn.Throwable) + lazy val UninitializedErrorClass = requiredClass[UninitializedFieldError] + + // fundamental reference classes + lazy val PartialFunctionClass = requiredClass[PartialFunction[_,_]] + lazy val AbstractPartialFunctionClass = requiredClass[scala.runtime.AbstractPartialFunction[_,_]] + lazy val SymbolClass = requiredClass[scala.Symbol] + lazy val StringClass = requiredClass[java.lang.String] + lazy val StringModule = StringClass.linkedClassOfClass + lazy val ClassClass = requiredClass[java.lang.Class[_]] + def Class_getMethod = getMemberMethod(ClassClass, nme.getMethod_) + lazy val DynamicClass = requiredClass[Dynamic] + + // fundamental modules + lazy val SysPackage = getPackageObject("scala.sys") + def Sys_error = getMemberMethod(SysPackage, nme.error) + + // Modules whose members are in the default namespace + // [Eugene++] ScalaPackage and JavaLangPackage are never ever shared between mirrors + // as a result, `Int` becomes `scala.Int` and `String` becomes `java.lang.String` + // I could just change `isOmittablePrefix`, but there's more to it, so I'm leaving this as a todo for now + lazy val UnqualifiedModules = List(PredefModule, ScalaPackage, JavaLangPackage) + // Those modules and their module classes + lazy val UnqualifiedOwners = UnqualifiedModules.toSet ++ UnqualifiedModules.map(_.moduleClass) + + lazy val PredefModule = requiredModule[scala.Predef.type] + lazy val PredefModuleClass = PredefModule.moduleClass + + def Predef_classOf = getMemberMethod(PredefModule, nme.classOf) + def Predef_identity = getMemberMethod(PredefModule, nme.identity) + def Predef_conforms = getMemberMethod(PredefModule, nme.conforms) + def Predef_wrapRefArray = getMemberMethod(PredefModule, nme.wrapRefArray) + def Predef_??? = getMemberMethod(PredefModule, nme.???) + def Predef_implicitly = getMemberMethod(PredefModule, nme.implicitly) + + /** Is `sym` a member of Predef with the given name? + * Note: DON't replace this by sym == Predef_conforms/etc, as Predef_conforms is a `def` + * which does a member lookup (it can't be a lazy val because we might reload Predef + * during resident compilations). + */ + def isPredefMemberNamed(sym: Symbol, name: Name) = ( + (sym.name == name) && (sym.owner == PredefModule.moduleClass) + ) + + /** Specialization. + */ + lazy val SpecializableModule = requiredModule[Specializable] + lazy val GroupOfSpecializable = getMemberClass(SpecializableModule, tpnme.Group) + + lazy val ConsoleModule = requiredModule[scala.Console.type] + lazy val ScalaRunTimeModule = requiredModule[scala.runtime.ScalaRunTime.type] + lazy val SymbolModule = requiredModule[scala.Symbol.type] + lazy val Symbol_apply = getMemberMethod(SymbolModule, nme.apply) + + def SeqFactory = getMember(ScalaRunTimeModule, nme.Seq) // [Eugene++] obsolete? + def arrayApplyMethod = getMemberMethod(ScalaRunTimeModule, nme.array_apply) + def arrayUpdateMethod = getMemberMethod(ScalaRunTimeModule, nme.array_update) + def arrayLengthMethod = getMemberMethod(ScalaRunTimeModule, nme.array_length) + def arrayCloneMethod = getMemberMethod(ScalaRunTimeModule, nme.array_clone) + def ensureAccessibleMethod = getMemberMethod(ScalaRunTimeModule, nme.ensureAccessible) + def scalaRuntimeSameElements = getMemberMethod(ScalaRunTimeModule, nme.sameElements) + def arrayClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayClass) + def arrayElementClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayElementClass) + + // classes with special meanings + lazy val StringAddClass = requiredClass[scala.runtime.StringAdd] + lazy val ArrowAssocClass = getRequiredClass("scala.Predef.ArrowAssoc") // SI-5731 + lazy val StringAdd_+ = getMemberMethod(StringAddClass, nme.PLUS) + lazy val NotNullClass = getRequiredClass("scala.NotNull") + lazy val ScalaNumberClass = requiredClass[scala.math.ScalaNumber] + lazy val TraitSetterAnnotationClass = requiredClass[scala.runtime.TraitSetter] + lazy val DelayedInitClass = requiredClass[scala.DelayedInit] + def delayedInitMethod = getMemberMethod(DelayedInitClass, nme.delayedInit) + // a dummy value that communicates that a delayedInit call is compiler-generated + // from phase UnCurry to phase Constructors + // !!! This is not used anywhere (it was checked in that way.) + // def delayedInitArgVal = EmptyPackageClass.newValue(NoPosition, nme.delayedInitArg) + // .setInfo(UnitClass.tpe) + + lazy val TypeConstraintClass = requiredClass[scala.annotation.TypeConstraint] + lazy val SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, anyparam, ABSTRACT | TRAIT | FINAL) + lazy val SerializableClass = requiredClass[scala.Serializable] + lazy val JavaSerializableClass = requiredClass[java.io.Serializable] modifyInfo fixupAsAnyTrait + lazy val ComparableClass = requiredClass[java.lang.Comparable[_]] modifyInfo fixupAsAnyTrait + lazy val JavaCloneableClass = requiredClass[java.lang.Cloneable] + lazy val JavaNumberClass = requiredClass[java.lang.Number] + lazy val RemoteInterfaceClass = requiredClass[java.rmi.Remote] + lazy val RemoteExceptionClass = requiredClass[java.rmi.RemoteException] + + lazy val ByNameParamClass = specialPolyClass(tpnme.BYNAME_PARAM_CLASS_NAME, COVARIANT)(_ => AnyClass.tpe) + lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN_NAME, 0L)(_ => AnyClass.tpe) + lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.tpe)) + lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.tpe)) + + 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 isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf + + def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) + def isJavaVarArgs(params: Seq[Symbol]) = params.nonEmpty && isJavaRepeatedParamType(params.last.tpe) + def isScalaVarArgs(params: Seq[Symbol]) = params.nonEmpty && isScalaRepeatedParamType(params.last.tpe) + def isVarArgsList(params: Seq[Symbol]) = params.nonEmpty && isRepeatedParamType(params.last.tpe) + def isVarArgTypes(formals: Seq[Type]) = formals.nonEmpty && isRepeatedParamType(formals.last) + + def hasRepeatedParam(tp: Type): Boolean = tp match { + case MethodType(formals, restpe) => isScalaVarArgs(formals) || hasRepeatedParam(restpe) + case PolyType(_, restpe) => hasRepeatedParam(restpe) + case _ => false + } + + def isPrimitiveArray(tp: Type) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => isPrimitiveValueClass(arg.typeSymbol) + case _ => false + } + def isReferenceArray(tp: Type) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => arg <:< AnyRefClass.tpe + case _ => false + } + def isArrayOfSymbol(tp: Type, elem: Symbol) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => arg.typeSymbol == elem + case _ => false + } + + lazy val MatchingStrategyClass = getRequiredClass("scala.MatchingStrategy") + + // collections classes + lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]] + lazy val IterableClass = requiredClass[scala.collection.Iterable[_]] + lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]] + lazy val ListClass = requiredClass[scala.collection.immutable.List[_]] + lazy val SeqClass = requiredClass[scala.collection.Seq[_]] + lazy val StringBuilderClass = requiredClass[scala.collection.mutable.StringBuilder] + lazy val TraversableClass = requiredClass[scala.collection.Traversable[_]] + + lazy val ListModule = requiredModule[scala.collection.immutable.List.type] + lazy val List_apply = getMemberMethod(ListModule, nme.apply) + lazy val NilModule = requiredModule[scala.collection.immutable.Nil.type] + lazy val SeqModule = requiredModule[scala.collection.Seq.type] + lazy val IteratorModule = requiredModule[scala.collection.Iterator.type] + lazy val Iterator_apply = getMemberMethod(IteratorModule, nme.apply) + + // arrays and their members + lazy val ArrayModule = requiredModule[scala.Array.type] + lazy val ArrayModule_overloadedApply = getMemberMethod(ArrayModule, nme.apply) + lazy val ArrayClass = getRequiredClass("scala.Array") // requiredClass[scala.Array[_]] + lazy val Array_apply = getMemberMethod(ArrayClass, nme.apply) + lazy val Array_update = getMemberMethod(ArrayClass, nme.update) + lazy val Array_length = getMemberMethod(ArrayClass, nme.length) + lazy val Array_clone = getMemberMethod(ArrayClass, nme.clone_) + + // reflection / structural types + lazy val SoftReferenceClass = requiredClass[java.lang.ref.SoftReference[_]] + lazy val WeakReferenceClass = requiredClass[java.lang.ref.WeakReference[_]] + lazy val MethodClass = getClassByName(sn.MethodAsObject) + def methodClass_setAccessible = getMemberMethod(MethodClass, nme.setAccessible) + lazy val EmptyMethodCacheClass = requiredClass[scala.runtime.EmptyMethodCache] + lazy val MethodCacheClass = requiredClass[scala.runtime.MethodCache] + def methodCache_find = getMemberMethod(MethodCacheClass, nme.find_) + def methodCache_add = getMemberMethod(MethodCacheClass, nme.add_) + + // scala.reflect + lazy val ReflectPackage = requiredModule[scala.reflect.`package`.type] + def ReflectBasis = getMemberValue(ReflectPackage, nme.basis) + lazy val ReflectRuntimePackage = getPackageObjectIfDefined("scala.reflect.runtime") // defined in scala-reflect.jar, so we need to be careful + def ReflectRuntimeUniverse = if (ReflectRuntimePackage != NoSymbol) getMemberValue(ReflectRuntimePackage, nme.universe) else NoSymbol + def ReflectRuntimeCurrentMirror = if (ReflectRuntimePackage != NoSymbol) getMemberMethod(ReflectRuntimePackage, nme.currentMirror) else NoSymbol + + lazy val PartialManifestClass = requiredClass[scala.reflect.ClassManifest[_]] + lazy val PartialManifestModule = requiredModule[scala.reflect.ClassManifest.type] + lazy val FullManifestClass = requiredClass[scala.reflect.Manifest[_]] + lazy val FullManifestModule = requiredModule[scala.reflect.Manifest.type] + lazy val OptManifestClass = requiredClass[scala.reflect.OptManifest[_]] + lazy val NoManifest = requiredModule[scala.reflect.NoManifest.type] + + lazy val ExprsClass = getClassIfDefined("scala.reflect.api.Exprs") // defined in scala-reflect.jar, so we need to be careful + lazy val ExprClass = if (ExprsClass != NoSymbol) getMemberClass(ExprsClass, tpnme.Expr) else NoSymbol + def ExprSplice = if (ExprsClass != NoSymbol) getMemberMethod(ExprClass, nme.splice) else NoSymbol + def ExprValue = if (ExprsClass != NoSymbol) getMemberMethod(ExprClass, nme.value) else NoSymbol + lazy val ExprModule = if (ExprsClass != NoSymbol) getMemberModule(ExprsClass, nme.Expr) else NoSymbol + + lazy val ArrayTagClass = requiredClass[scala.reflect.ArrayTag[_]] + lazy val ClassTagModule = requiredModule[scala.reflect.ClassTag[_]] + lazy val ClassTagClass = requiredClass[scala.reflect.ClassTag[_]] + lazy val TypeTagsClass = requiredClass[scala.reflect.base.TypeTags] + lazy val TypeTagClass = getMemberClass(TypeTagsClass, tpnme.TypeTag) + lazy val TypeTagModule = getMemberModule(TypeTagsClass, nme.TypeTag) + lazy val ConcreteTypeTagClass = getMemberClass(TypeTagsClass, tpnme.ConcreteTypeTag) + lazy val ConcreteTypeTagModule = getMemberModule(TypeTagsClass, nme.ConcreteTypeTag) + + lazy val BaseUniverseClass = requiredClass[scala.reflect.base.Universe] + lazy val ApiUniverseClass = getClassIfDefined("scala.reflect.api.Universe") // defined in scala-reflect.jar, so we need to be careful + def ApiUniverseReify = if (ApiUniverseClass != NoSymbol) getMemberMethod(ApiUniverseClass, nme.reify) else NoSymbol + lazy val JavaUniverseClass = getClassIfDefined("scala.reflect.api.JavaUniverse") // defined in scala-reflect.jar, so we need to be careful + + lazy val MirrorOfClass = requiredClass[scala.reflect.base.MirrorOf[_]] + + lazy val TypeCreatorClass = requiredClass[scala.reflect.base.TypeCreator] + lazy val TreeCreatorClass = requiredClass[scala.reflect.base.TreeCreator] + + lazy val MacroContextClass = getClassIfDefined("scala.reflect.makro.Context") // defined in scala-reflect.jar, so we need to be careful + def MacroContextPrefix = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.prefix) else NoSymbol + def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getMemberType(MacroContextClass, tpnme.PrefixType) else NoSymbol + def MacroContextUniverse = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.universe) else NoSymbol + def MacroContextMirror = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.mirror) else NoSymbol + def MacroContextReify = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.reify) else NoSymbol + lazy val MacroImplAnnotation = requiredClass[scala.reflect.makro.internal.macroImpl] + lazy val MacroInternalPackage = getPackageObject("scala.reflect.makro.internal") + def MacroInternal_materializeArrayTag = getMemberMethod(MacroInternalPackage, nme.materializeArrayTag) + def MacroInternal_materializeClassTag = getMemberMethod(MacroInternalPackage, nme.materializeClassTag) + def MacroInternal_materializeTypeTag = getMemberMethod(MacroInternalPackage, nme.materializeTypeTag) + def MacroInternal_materializeConcreteTypeTag = getMemberMethod(MacroInternalPackage, nme.materializeConcreteTypeTag) + + lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature] + lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature] + + // Option classes + lazy val OptionClass: ClassSymbol = requiredClass[Option[_]] + lazy val SomeClass: ClassSymbol = requiredClass[Some[_]] + lazy val NoneModule: ModuleSymbol = requiredModule[scala.None.type] + lazy val SomeModule: ModuleSymbol = requiredModule[scala.Some.type] + + def compilerTypeFromTag(tt: BaseUniverse # TypeTag[_]): Type = tt.in(rootMirror).tpe + def compilerSymbolFromTag(tt: BaseUniverse # TypeTag[_]): Symbol = tt.in(rootMirror).tpe.typeSymbol + + // The given symbol represents either String.+ or StringAdd.+ + def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+ + def isArrowAssoc(sym: Symbol) = ArrowAssocClass.tpe.decls.toList contains sym + + // The given symbol is a method with the right name and signature to be a runnable java program. + def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (sym.info match { + case MethodType(p :: Nil, restpe) => isArrayOfSymbol(p.tpe, StringClass) && restpe.typeSymbol == UnitClass + case _ => false + }) + // The given class has a main method. + def hasJavaMainMethod(sym: Symbol): Boolean = + (sym.tpe member nme.main).alternatives exists isJavaMainMethod + def hasJavaMainMethod(path: String): Boolean = + hasJavaMainMethod(getModuleIfDefined(path)) + + def isOptionType(tp: Type) = tp.typeSymbol isSubClass OptionClass + def isSomeType(tp: Type) = tp.typeSymbol eq SomeClass + def isNoneType(tp: Type) = tp.typeSymbol eq NoneModule + + // Product, Tuple, Function, AbstractFunction + private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[ClassSymbol] = { + val list = countFrom to arity map (i => getRequiredClass("scala." + name + i)) + list.toArray + } + def prepend[S >: ClassSymbol : ClassTag](elem0: S, elems: Array[ClassSymbol]): Array[S] = elem0 +: elems + + private def aritySpecificType[S <: Symbol](symbolArray: Array[S], args: List[Type], others: Type*): Type = { + val arity = args.length + if (arity >= symbolArray.length) NoType + else appliedType(symbolArray(arity), args ++ others: _*) + } + + val MaxTupleArity, MaxProductArity, MaxFunctionArity = 22 + lazy val ProductClass: Array[ClassSymbol] = prepend(UnitClass, mkArityArray("Product", MaxProductArity, 1)) + lazy val TupleClass: Array[Symbol] = prepend(NoSymbol, mkArityArray("Tuple", MaxTupleArity, 1)) + lazy val FunctionClass = mkArityArray("Function", MaxFunctionArity, 0) + lazy val AbstractFunctionClass = mkArityArray("runtime.AbstractFunction", MaxFunctionArity, 0) + + /** Creators for TupleN, ProductN, FunctionN. */ + def tupleType(elems: List[Type]) = aritySpecificType(TupleClass, elems) + def productType(elems: List[Type]) = aritySpecificType(ProductClass, elems) + def functionType(formals: List[Type], restpe: Type) = aritySpecificType(FunctionClass, formals, restpe) + def abstractFunctionType(formals: List[Type], restpe: Type) = aritySpecificType(AbstractFunctionClass, formals, restpe) + + def wrapArrayMethodName(elemtp: Type): TermName = elemtp.typeSymbol match { + case ByteClass => nme.wrapByteArray + case ShortClass => nme.wrapShortArray + case CharClass => nme.wrapCharArray + case IntClass => nme.wrapIntArray + case LongClass => nme.wrapLongArray + case FloatClass => nme.wrapFloatArray + case DoubleClass => nme.wrapDoubleArray + case BooleanClass => nme.wrapBooleanArray + case UnitClass => nme.wrapUnitArray + case _ => + if ((elemtp <:< AnyRefClass.tpe) && !isPhantomClass(elemtp.typeSymbol)) nme.wrapRefArray + else nme.genericWrapArray + } + + @deprecated("Use isTupleType", "2.10.0") + def isTupleTypeOrSubtype(tp: Type) = isTupleType(tp) + + def tupleField(n: Int, j: Int) = getMemberValue(TupleClass(n), nme.productAccessorName(j)) + // NOTE: returns true for NoSymbol since it's included in the TupleClass array -- is this intensional? + def isTupleSymbol(sym: Symbol) = TupleClass contains unspecializedSymbol(sym) + def isProductNClass(sym: Symbol) = ProductClass contains sym + + def unspecializedSymbol(sym: Symbol): Symbol = { + if (sym hasFlag SPECIALIZED) { + // add initialization from its generic class constructor + val genericName = nme.unspecializedName(sym.name) + val member = sym.owner.info.decl(genericName.toTypeName) + member + } + else sym + } + + // Checks whether the given type is true for the given condition, + // or if it is a specialized subtype of a type for which it is true. + // + // Origins notes: + // An issue was introduced with specialization in that the implementation + // of "isTupleType" in Definitions relied upon sym == TupleClass(elems.length). + // This test is untrue for specialized tuples, causing mysterious behavior + // because only some tuples are specialized. + def isPossiblySpecializedType(tp: Type)(cond: Type => Boolean) = { + cond(tp) || (tp match { + case TypeRef(pre, sym, args) if sym hasFlag SPECIALIZED => + cond(tp baseType unspecializedSymbol(sym)) + case _ => + false + }) + } + // No normalization. + def isTupleTypeDirect(tp: Type) = isPossiblySpecializedType(tp) { + case TypeRef(_, sym, args) if args.nonEmpty => + val len = args.length + len <= MaxTupleArity && sym == TupleClass(len) + case _ => false + } + def isTupleType(tp: Type) = isTupleTypeDirect(tp.normalize) + + lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product] + def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity) + def Product_productElement = getMemberMethod(ProductRootClass, nme.productElement) + def Product_iterator = getMemberMethod(ProductRootClass, nme.productIterator) + def Product_productPrefix = getMemberMethod(ProductRootClass, nme.productPrefix) + def Product_canEqual = getMemberMethod(ProductRootClass, nme.canEqual_) + // def Product_productElementName = getMemberMethod(ProductRootClass, nme.productElementName) + + def productProj(z:Symbol, j: Int): TermSymbol = getMemberValue(z, nme.productAccessorName(j)) + def productProj(n: Int, j: Int): TermSymbol = productProj(ProductClass(n), j) + + /** returns true if this type is exactly ProductN[T1,...,Tn], not some subclass */ + def isExactProductType(tp: Type): Boolean = isProductNClass(tp.typeSymbol) + + /** if tpe <: ProductN[T1,...,TN], returns List(T1,...,TN) else Nil */ + def getProductArgs(tpe: Type): List[Type] = tpe.baseClasses find isProductNClass match { + case Some(x) => tpe.baseType(x).typeArgs + case _ => Nil + } + + def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match { + case RefinedType(p :: _, _) => p.normalize + case tp => tp + } + + def functionApply(n: Int) = getMemberMethod(FunctionClass(n), nme.apply) + + def abstractFunctionForFunctionType(tp: Type) = + if (isFunctionType(tp)) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) + else NoType + + def isFunctionType(tp: Type): Boolean = tp.normalize match { + case TypeRef(_, sym, args) if args.nonEmpty => + val arity = args.length - 1 // -1 is the return type + arity <= MaxFunctionArity && sym == FunctionClass(arity) + case _ => + false + } + + def isPartialFunctionType(tp: Type): Boolean = { + val sym = tp.typeSymbol + (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) + } + + 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 arrayType(arg: Type) = appliedType(ArrayClass, arg) + def byNameType(arg: Type) = appliedType(ByNameParamClass, arg) + def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp) + def javaRepeatedType(arg: Type) = appliedType(JavaRepeatedParamClass, arg) + def optionType(tp: Type) = appliedType(OptionClass, tp) + def scalaRepeatedType(arg: Type) = appliedType(RepeatedParamClass, arg) + def seqType(arg: Type) = appliedType(SeqClass, arg) + def someType(tp: Type) = appliedType(SomeClass, tp) + + def StringArray = arrayType(StringClass.tpe) + lazy val ObjectArray = arrayType(ObjectClass.tpe) + + def ClassType(arg: Type) = + if (phase.erasedTypes || forMSIL) ClassClass.tpe + else appliedType(ClassClass, arg) + + def vmClassType(arg: Type): Type = ClassType(arg) + def vmSignature(sym: Symbol, info: Type): String = signature(info) // !!! + + /** Given a class symbol C with type parameters T1, T2, ... Tn + * which have upper/lower bounds LB1/UB1, LB1/UB2, ..., LBn/UBn, + * returns an existential type of the form + * + * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. + */ + def classExistentialType(clazz: Symbol): Type = + newExistentialType(clazz.typeParams, clazz.tpe) + + /** Given type U, creates a Type representing Class[_ <: U]. + */ + def boundedClassType(upperBound: Type) = + appliedTypeAsUpperBounds(ClassClass.typeConstructor, List(upperBound)) + + /** To avoid unchecked warnings on polymorphic classes, translate + * a Foo[T] into a Foo[_] for use in the pattern matcher. + */ + @deprecated("Use classExistentialType", "2.10.0") + def typeCaseType(clazz: Symbol): Type = classExistentialType(clazz) + + // + // .NET backend + // + + lazy val ComparatorClass = getRequiredClass("scala.runtime.Comparator") + // System.ValueType + lazy val ValueTypeClass: ClassSymbol = getClassByName(sn.ValueType) + // System.MulticastDelegate + lazy val DelegateClass: ClassSymbol = getClassByName(sn.Delegate) + var Delegate_scalaCallers: List[Symbol] = List() // Syncnote: No protection necessary yet as only for .NET where reflection is not supported. + // Symbol -> (Symbol, Type): scalaCaller -> (scalaMethodSym, DelegateType) + // var Delegate_scalaCallerInfos: HashMap[Symbol, (Symbol, Type)] = _ + lazy val Delegate_scalaCallerTargets: mutable.HashMap[Symbol, Symbol] = mutable.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 + lazy val Any_== = enterNewMethod(AnyClass, nme.EQ, anyparam, booltype, FINAL) + lazy val Any_!= = enterNewMethod(AnyClass, nme.NE, anyparam, booltype, FINAL) + lazy val Any_equals = enterNewMethod(AnyClass, nme.equals_, anyparam, booltype) + lazy val Any_hashCode = enterNewMethod(AnyClass, nme.hashCode_, Nil, inttype) + lazy val Any_toString = enterNewMethod(AnyClass, nme.toString_, Nil, stringtype) + lazy val Any_## = enterNewMethod(AnyClass, nme.HASHHASH, Nil, inttype, FINAL) + + // Any_getClass requires special handling. The return type is determined on + // a per-call-site basis as if the function being called were actually: + // + // // Assuming `target.getClass()` + // def getClass[T](target: T): Class[_ <: T] + // + // Since getClass is not actually a polymorphic method, this requires compiler + // participation. At the "Any" level, the return type is Class[_] as it is in + // java.lang.Object. Java also special cases the return type. + lazy val Any_getClass = enterNewMethod(AnyClass, nme.getClass_, Nil, getMemberMethod(ObjectClass, nme.getClass_).tpe.resultType, DEFERRED) + lazy val Any_isInstanceOf = newT1NullaryMethod(AnyClass, nme.isInstanceOf_, FINAL)(_ => booltype) + lazy val Any_asInstanceOf = newT1NullaryMethod(AnyClass, nme.asInstanceOf_, FINAL)(_.typeConstructor) + + // A type function from T => Class[U], used to determine the return + // type of getClass calls. The returned type is: + // + // 1. If T is a value type, Class[T]. + // 2. If T is a phantom type (Any or AnyVal), Class[_]. + // 3. If T is a local class, Class[_ <: |T|]. + // 4. Otherwise, Class[_ <: T]. + // + // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the + // receiver is AnyVal, it implies the receiver is boxed, so the correct + // class object is that of java.lang.Integer, not Int. + // + // TODO: If T is final, return type could be Class[T]. Should it? + def getClassReturnType(tp: Type): Type = { + val sym = tp.typeSymbol + + if (phase.erasedTypes) ClassClass.tpe + else if (isPrimitiveValueClass(sym)) ClassType(tp.widen) + else { + val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) + val upperBound = ( + if (isPhantomClass(sym)) AnyClass.tpe + else if (sym.isLocalClass) erasure.intersectionDominator(tp.parents) + else tp.widen + ) + + existentialAbstraction( + eparams, + ClassType((eparams.head setInfo TypeBounds.upper(upperBound)).tpe) + ) + } + } + + /** Remove references to class Object (other than the head) in a list of parents */ + def removeLaterObjects(tps: List[Type]): List[Type] = tps match { + case Nil => Nil + case x :: xs => x :: xs.filterNot(_.typeSymbol == ObjectClass) + } + /** Remove all but one reference to class Object from a list of parents. */ + def removeRedundantObjects(tps: List[Type]): List[Type] = tps match { + case Nil => Nil + case x :: xs => + if (x.typeSymbol == ObjectClass) + x :: xs.filterNot(_.typeSymbol == ObjectClass) + else + x :: removeRedundantObjects(xs) + } + /** Order a list of types with non-trait classes before others. */ + def classesFirst(tps: List[Type]): List[Type] = { + val (classes, others) = tps partition (t => t.typeSymbol.isClass && !t.typeSymbol.isTrait) + if (classes.isEmpty || others.isEmpty || (tps startsWith classes)) tps + else classes ::: others + } + /** The following transformations applied to a list of parents. + * If any parent is a class/trait, all parents which normalize to + * Object are discarded. Otherwise, all parents which normalize + * to Object except the first one found are discarded. + */ + def normalizedParents(parents: List[Type]): List[Type] = { + if (parents exists (t => (t.typeSymbol ne ObjectClass) && t.typeSymbol.isClass)) + parents filterNot (_.typeSymbol eq ObjectClass) + else + removeRedundantObjects(parents) + } + + def typeStringNoPackage(tp: Type) = + "" + tp stripPrefix tp.typeSymbol.enclosingPackage.fullName + "." + + def briefParentsString(parents: List[Type]) = + normalizedParents(parents) map typeStringNoPackage mkString " with " + + def parentsString(parents: List[Type]) = + normalizedParents(parents) mkString " with " + + def typeParamsString(tp: Type) = tp match { + case PolyType(tparams, _) => tparams map (_.defString) mkString ("[", ",", "]") + case _ => "" + } + def valueParamsString(tp: Type) = tp match { + case MethodType(params, _) => params map (_.defString) mkString ("(", ",", ")") + case _ => "" + } + + // members of class java.lang.{ Object, String } + lazy val Object_## = enterNewMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) + lazy val Object_== = enterNewMethod(ObjectClass, nme.EQ, anyrefparam, booltype, FINAL) + lazy val Object_!= = enterNewMethod(ObjectClass, nme.NE, anyrefparam, booltype, FINAL) + lazy val Object_eq = enterNewMethod(ObjectClass, nme.eq, anyrefparam, booltype, FINAL) + lazy val Object_ne = enterNewMethod(ObjectClass, nme.ne, anyrefparam, booltype, FINAL) + lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC)(_ => booltype) + lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC)(_.typeConstructor) + lazy val Object_synchronized = newPolyMethod(1, ObjectClass, nme.synchronized_, FINAL)(tps => + (Some(List(tps.head.typeConstructor)), tps.head.typeConstructor) + ) + lazy val String_+ = enterNewMethod(StringClass, nme.raw.PLUS, anyparam, stringtype, FINAL) + + def Object_getClass = getMemberMethod(ObjectClass, nme.getClass_) + def Object_clone = getMemberMethod(ObjectClass, nme.clone_) + def Object_finalize = getMemberMethod(ObjectClass, nme.finalize_) + def Object_notify = getMemberMethod(ObjectClass, nme.notify_) + def Object_notifyAll = getMemberMethod(ObjectClass, nme.notifyAll_) + def Object_equals = getMemberMethod(ObjectClass, nme.equals_) + def Object_hashCode = getMemberMethod(ObjectClass, nme.hashCode_) + def Object_toString = getMemberMethod(ObjectClass, nme.toString_) + + // boxed classes + lazy val ObjectRefClass = requiredClass[scala.runtime.ObjectRef[_]] + lazy val VolatileObjectRefClass = requiredClass[scala.runtime.VolatileObjectRef[_]] + lazy val RuntimeStaticsModule = getRequiredModule("scala.runtime.Statics") + lazy val BoxesRunTimeModule = getRequiredModule("scala.runtime.BoxesRunTime") + lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass + lazy val BoxedNumberClass = getClassByName(sn.BoxedNumber) + lazy val BoxedCharacterClass = getClassByName(sn.BoxedCharacter) + lazy val BoxedBooleanClass = getClassByName(sn.BoxedBoolean) + lazy val BoxedByteClass = requiredClass[java.lang.Byte] + lazy val BoxedShortClass = requiredClass[java.lang.Short] + lazy val BoxedIntClass = requiredClass[java.lang.Integer] + lazy val BoxedLongClass = requiredClass[java.lang.Long] + lazy val BoxedFloatClass = requiredClass[java.lang.Float] + lazy val BoxedDoubleClass = requiredClass[java.lang.Double] + + lazy val Boxes_isNumberOrBool = getDecl(BoxesRunTimeClass, nme.isBoxedNumberOrBoolean) + lazy val Boxes_isNumber = getDecl(BoxesRunTimeClass, nme.isBoxedNumber) + + lazy val BoxedUnitClass = requiredClass[scala.runtime.BoxedUnit] + lazy val BoxedUnitModule = getRequiredModule("scala.runtime.BoxedUnit") + def BoxedUnit_UNIT = getMemberValue(BoxedUnitModule, nme.UNIT) + def BoxedUnit_TYPE = getMemberValue(BoxedUnitModule, nme.TYPE_) + + // Annotation base classes + lazy val AnnotationClass = requiredClass[scala.annotation.Annotation] + lazy val ClassfileAnnotationClass = requiredClass[scala.annotation.ClassfileAnnotation] + lazy val StaticAnnotationClass = requiredClass[scala.annotation.StaticAnnotation] + + // Annotations + lazy val BridgeClass = requiredClass[scala.annotation.bridge] + lazy val ElidableMethodClass = requiredClass[scala.annotation.elidable] + lazy val ImplicitNotFoundClass = requiredClass[scala.annotation.implicitNotFound] + lazy val MigrationAnnotationClass = requiredClass[scala.annotation.migration] + lazy val ScalaStrictFPAttr = requiredClass[scala.annotation.strictfp] + lazy val SerializableAttr = requiredClass[scala.annotation.serializable] // @serializable is deprecated + lazy val SwitchClass = requiredClass[scala.annotation.switch] + lazy val TailrecClass = requiredClass[scala.annotation.tailrec] + lazy val VarargsClass = requiredClass[scala.annotation.varargs] + lazy val uncheckedStableClass = requiredClass[scala.annotation.unchecked.uncheckedStable] + lazy val uncheckedVarianceClass = requiredClass[scala.annotation.unchecked.uncheckedVariance] + + lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty] + lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty] + lazy val CloneableAttr = requiredClass[scala.cloneable] + lazy val DeprecatedAttr = requiredClass[scala.deprecated] + lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] + lazy val NativeAttr = requiredClass[scala.native] + lazy val RemoteAttr = requiredClass[scala.remote] + lazy val ScalaInlineClass = requiredClass[scala.inline] + lazy val ScalaNoInlineClass = requiredClass[scala.noinline] + lazy val SerialVersionUIDAttr = requiredClass[scala.SerialVersionUID] + lazy val SpecializedClass = requiredClass[scala.specialized] + lazy val ThrowsClass = requiredClass[scala.throws] + lazy val TransientAttr = requiredClass[scala.transient] + lazy val UncheckedClass = requiredClass[scala.unchecked] + lazy val UnspecializedClass = requiredClass[scala.annotation.unspecialized] + lazy val VolatileAttr = requiredClass[scala.volatile] + + // Meta-annotations + lazy val BeanGetterTargetClass = requiredClass[meta.beanGetter] + lazy val BeanSetterTargetClass = requiredClass[meta.beanSetter] + lazy val FieldTargetClass = requiredClass[meta.field] + lazy val GetterTargetClass = requiredClass[meta.getter] + lazy val ParamTargetClass = requiredClass[meta.param] + lazy val SetterTargetClass = requiredClass[meta.setter] + lazy val ClassTargetClass = requiredClass[meta.companionClass] + lazy val ObjectTargetClass = requiredClass[meta.companionObject] + lazy val MethodTargetClass = requiredClass[meta.companionMethod] // TODO: module, moduleClass? package, packageObject? + lazy val LanguageFeatureAnnot = requiredClass[meta.languageFeature] + + // Language features + lazy val languageFeatureModule = getRequiredModule("scala.languageFeature") + lazy val experimentalModule = getMemberModule(languageFeatureModule, nme.experimental) + lazy val MacrosFeature = getLanguageFeature("macros", experimentalModule) + lazy val DynamicsFeature = getLanguageFeature("dynamics") + lazy val PostfixOpsFeature = getLanguageFeature("postfixOps") + lazy val ReflectiveCallsFeature = getLanguageFeature("reflectiveCalls") + lazy val ImplicitConversionsFeature = getLanguageFeature("implicitConversions") + lazy val HigherKindsFeature = getLanguageFeature("higherKinds") + lazy val ExistentialsFeature = getLanguageFeature("existentials") + + def isMetaAnnotation(sym: Symbol): Boolean = metaAnnotations(sym) || ( + // Trying to allow for deprecated locations + sym.isAliasType && isMetaAnnotation(sym.info.typeSymbol) + ) + lazy val metaAnnotations = Set[Symbol]( + FieldTargetClass, ParamTargetClass, + GetterTargetClass, SetterTargetClass, + BeanGetterTargetClass, BeanSetterTargetClass + ) + + lazy val AnnotationDefaultAttr: ClassSymbol = { + val attr = enterNewClass(RuntimePackageClass, tpnme.AnnotationDefaultATTR, List(AnnotationClass.tpe)) + // This attribute needs a constructor so that modifiers in parsed Java code make sense + attr.info.decls enter attr.newClassConstructor(NoPosition) + attr + } + + private def fatalMissingSymbol(owner: Symbol, name: Name, what: String = "member") = { + throw new FatalError(owner + " does not have a " + what + " " + name) + } + + def getLanguageFeature(name: String, owner: Symbol = languageFeatureModule): Symbol = + // [Eugene++] `getMemberClass` leads to crashes in mixin: + // "object languageFeature does not have a member class implicitConversions" + // that's because by that time `implicitConversions` becomes a module + // getMemberClass(owner, newTypeName(name)) + getMember(owner, newTypeName(name)) + + def termMember(owner: Symbol, name: String): Symbol = owner.info.member(newTermName(name)) + def typeMember(owner: Symbol, name: String): Symbol = owner.info.member(newTypeName(name)) + + def findNamedMember(fullName: Name, root: Symbol): Symbol = { + val segs = nme.segments(fullName.toString, fullName.isTermName) + if (segs.isEmpty || segs.head != root.simpleName) NoSymbol + else findNamedMember(segs.tail, root) + } + def findNamedMember(segs: List[Name], root: Symbol): Symbol = + if (segs.isEmpty) root + else findNamedMember(segs.tail, root.info member segs.head) + + def getMember(owner: Symbol, name: Name): Symbol = { + getMemberIfDefined(owner, name) orElse { + if (phase.flatClasses && name.isTypeName && !owner.isPackageObjectOrClass) { + val pkg = owner.owner + val flatname = nme.flattenedName(owner.name, name) + getMember(pkg, flatname) + } + else fatalMissingSymbol(owner, name) + } + } + def getMemberValue(owner: Symbol, name: Name): TermSymbol = { + // [Eugene++] should be a ClassCastException instead? + getMember(owner, name.toTermName) match { + case x: TermSymbol => x + case _ => fatalMissingSymbol(owner, name, "member value") + } + } + def getMemberModule(owner: Symbol, name: Name): ModuleSymbol = { + // [Eugene++] should be a ClassCastException instead? + getMember(owner, name.toTermName) match { + case x: ModuleSymbol => x + case _ => fatalMissingSymbol(owner, name, "member object") + } + } + def getMemberType(owner: Symbol, name: Name): TypeSymbol = { + // [Eugene++] should be a ClassCastException instead? + getMember(owner, name.toTypeName) match { + case x: TypeSymbol => x + case _ => fatalMissingSymbol(owner, name, "member type") + } + } + def getMemberClass(owner: Symbol, name: Name): ClassSymbol = { + // [Eugene++] should be a ClassCastException instead? + val y = getMember(owner, name.toTypeName) + getMember(owner, name.toTypeName) match { + case x: ClassSymbol => x + case _ => fatalMissingSymbol(owner, name, "member class") + } + } + def getMemberMethod(owner: Symbol, name: Name): TermSymbol = { + // [Eugene++] is this a bug? + // + // System.err.println(result.getClass) + // System.err.println(result.flags) + // System.err.println("isMethod = " + result.isMethod) + // System.err.println("isTerm = " + result.isTerm) + // System.err.println("isValue = " + result.isValue) + // result.asMethodSymbol + // + // prints this: + // + // quick.lib: + // [javac] Compiling 1 source file to C:\Projects\KeplerUnderRefactoring\build\quick\classes\library + // [scalacfork] Compiling 769 files to C:\Projects\KeplerUnderRefactoring\build\quick\classes\library + // [scalacfork] class scala.reflect.internal.Symbols$TermSymbol + // [scalacfork] 8589934592 + // [scalacfork] isMethod = false + // [scalacfork] isTerm = true + // [scalacfork] isValue = true + // [scalacfork] + // [scalacfork] while compiling: C:\Projects\KeplerUnderRefactoring\src\library\scala\LowPriorityImplicits.scala + // [scalacfork] current phase: cleanup + // [scalacfork] library version: version 2.10.0-20120507-185519-665d1d9127 + // [scalacfork] compiler version: version 2.10.0-20120507-185519-665d1d9127 + // [scalacfork] reconstructed args: -Xmacros -classpath C:\\Projects\\KeplerUnderRefactoring\\build\\quick\\classes\\library;C:\\Projects\\KeplerUnderRefactoring\\lib\\forkjoin.jar -d C:\\Projects\\KeplerUnderRefactoring\\build\\quick\\classes\\library -sourcepath C:\\Projects\\KeplerUnderRefactoring\\src\\library + // [scalacfork] + // [scalacfork] unhandled exception while transforming LowPriorityImplicits.scala + // [scalacfork] error: + // [scalacfork] while compiling: C:\Projects\KeplerUnderRefactoring\src\library\scala\LowPriorityImplicits.scala + // [scalacfork] current phase: cleanup + // [scalacfork] library version: version 2.10.0-20120507-185519-665d1d9127 + // [scalacfork] compiler version: version 2.10.0-20120507-185519-665d1d9127 + // [scalacfork] reconstructed args: -Xmacros -classpath C:\\Projects\\KeplerUnderRefactoring\\build\\quick\\classes\\library;C:\\Projects\\KeplerUnderRefactoring\\lib\\forkjoin.jar -d C:\\Projects\\KeplerUnderRefactoring\\build\\quick\\classes\\library -sourcepath C:\\Projects\\KeplerUnderRefactoring\\src\\library + // [scalacfork] + // [scalacfork] uncaught exception during compilation: java.lang.ClassCastException + // [scalacfork] error: java.lang.ClassCastException: value apply + // [scalacfork] at scala.reflect.base.Symbols$SymbolBase$class.asMethodSymbol(Symbols.scala:118) + // [scalacfork] at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethodSymbol(Symbols.scala:63) + // [scalacfork] at scala.reflect.internal.Definitions$DefinitionsClass.Symbol_apply(Definitions.scala:381) + + // [Eugene++] should be a ClassCastException instead? + getMember(owner, name.toTermName) match { + // case x: MethodSymbol => x + case x: TermSymbol => x + case _ => fatalMissingSymbol(owner, name, "method") + } + } + + def getMemberIfDefined(owner: Symbol, name: Name): Symbol = + owner.info.nonPrivateMember(name) + + /** Using getDecl rather than getMember may avoid issues with + * OverloadedTypes turning up when you don't want them, if you + * know the method in question is uniquely declared in the given owner. + */ + def getDecl(owner: Symbol, name: Name): Symbol = { + getDeclIfDefined(owner, name) orElse fatalMissingSymbol(owner, name, "decl") + } + def getDeclIfDefined(owner: Symbol, name: Name): Symbol = + owner.info.nonPrivateDecl(name) + + def packageExists(packageName: String): Boolean = + getModuleIfDefined(packageName).isPackage + + private def newAlias(owner: Symbol, name: TypeName, alias: Type): AliasTypeSymbol = + owner.newAliasType(name) setInfoAndEnter alias + + private def specialPolyClass(name: TypeName, flags: Long)(parentFn: Symbol => Type): ClassSymbol = { + val clazz = enterNewClass(ScalaPackageClass, name, Nil) + val tparam = clazz.newSyntheticTypeParam("T0", flags) + val parents = List(AnyRefClass.tpe, parentFn(tparam)) + + clazz setInfo GenPolyType(List(tparam), ClassInfoType(parents, newScope, clazz)) + } + + def newPolyMethod(typeParamCount: Int, owner: Symbol, name: TermName, flags: Long)(createFn: PolyMethodCreator): MethodSymbol = { + val msym = owner.newMethod(name.encode, NoPosition, flags) + val tparams = msym.newSyntheticTypeParams(typeParamCount) + val mtpe = createFn(tparams) match { + case (Some(formals), restpe) => MethodType(msym.newSyntheticValueParams(formals), restpe) + case (_, restpe) => NullaryMethodType(restpe) + } + + msym setInfoAndEnter genPolyType(tparams, mtpe) + } + + /** T1 means one type parameter. + */ + def newT1NullaryMethod(owner: Symbol, name: TermName, flags: Long)(createFn: Symbol => Type): MethodSymbol = { + newPolyMethod(1, owner, name, flags)(tparams => (None, createFn(tparams.head))) + } + def newT1NoParamsMethod(owner: Symbol, name: TermName, flags: Long)(createFn: Symbol => Type): MethodSymbol = { + newPolyMethod(1, owner, name, flags)(tparams => (Some(Nil), createFn(tparams.head))) + } + + lazy val boxedClassValues = boxedClass.values.toSet[Symbol] + lazy val isUnbox = unboxMethod.values.toSet[Symbol] + lazy val isBox = boxMethod.values.toSet[Symbol] + + /** Is symbol a phantom class for which no runtime representation exists? */ + lazy val isPhantomClass = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) + + /** Is the symbol that of a parent which is added during parsing? */ + lazy val isPossibleSyntheticParent = ProductClass.toSet[Symbol] + ProductRootClass + SerializableClass + + private lazy val boxedValueClassesSet = boxedClass.values.toSet[Symbol] + BoxedUnitClass + + /** Is symbol a value class? */ + def isPrimitiveValueClass(sym: Symbol) = ScalaValueClasses contains sym + def isNonUnitValueClass(sym: Symbol) = isPrimitiveValueClass(sym) && (sym != UnitClass) + def isSpecializableClass(sym: Symbol) = isPrimitiveValueClass(sym) || (sym == AnyRefClass) + def isPrimitiveValueType(tp: Type) = isPrimitiveValueClass(tp.typeSymbol) + + /** Is symbol a boxed value class, e.g. java.lang.Integer? */ + def isBoxedValueClass(sym: Symbol) = boxedValueClassesSet(sym) + + /** If symbol is a value class (boxed or not), return the unboxed + * value class. Otherwise, NoSymbol. + */ + def unboxedValueClass(sym: Symbol): Symbol = + if (isPrimitiveValueClass(sym)) sym + else if (sym == BoxedUnitClass) UnitClass + else boxedClass.map(kvp => (kvp._2: Symbol, kvp._1)).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.javaClassName + else flatNameString(sym.owner, separator) + nme.NAME_JOIN_STRING + sym.simpleName + def signature1(etp: Type): String = { + if (etp.typeSymbol == ArrayClass) "[" + signature1(erasure(etp.normalize.typeArgs.head)) + else if (isPrimitiveValueClass(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, '.') + } + + /** 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 + + 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, + Any_==, + Any_!=, + Any_equals, + Any_hashCode, + Any_toString, + Any_getClass, + Any_isInstanceOf, + Any_asInstanceOf, + Any_##, + Object_eq, + Object_ne, + Object_==, + Object_!=, + Object_##, + Object_synchronized, + Object_isInstanceOf, + Object_asInstanceOf, + String_+, + ComparableClass, + JavaSerializableClass + ) + + isInitialized = true + } //init + + var nbScalaCallers: Int = 0 + def newScalaCaller(delegateType: Type): MethodSymbol = { + 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 = newTermName("$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 = enterNewMethod(DelegateClass, name, paramTypes, delegateType, 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/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala new file mode 100644 index 0000000000..f1fe4fc118 --- /dev/null +++ b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -0,0 +1,50 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } +import util._ + +/** The name of this trait defines the eventual intent better than + * it does the initial contents. + */ +trait ExistentialsAndSkolems { + self: SymbolTable => + + /** Map a list of type parameter symbols to skolemized symbols, which + * can be deskolemized to the original type parameter. (A skolem is a + * representation of a bound variable when viewed inside its scope.) + * !!!Adriaan: this does not work for hk types. + */ + def deriveFreshSkolems(tparams: List[Symbol]): List[Symbol] = { + class Deskolemizer extends LazyType { + override val typeParams = tparams + val typeSkolems = typeParams map (_.newTypeSkolem setInfo this) + override def complete(sym: Symbol) { + // The info of a skolem is the skolemized info of the + // actual type parameter of the skolem + sym setInfo sym.deSkolemize.info.substSym(typeParams, typeSkolems) + } + } + (new Deskolemizer).typeSkolems + } + + /** Convert to corresponding type parameters all skolems of method + * parameters which appear in `tparams`. + */ + def deskolemizeTypeParams(tparams: List[Symbol])(tp: Type): Type = { + class DeSkolemizeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if sym.isTypeSkolem && (tparams contains sym.deSkolemize) => + mapOver(typeRef(NoPrefix, sym.deSkolemize, args)) + case _ => + mapOver(tp) + } + } + new DeSkolemizeMap mapOver tp + } +} diff --git a/src/reflect/scala/reflect/internal/FatalError.scala b/src/reflect/scala/reflect/internal/FatalError.scala new file mode 100644 index 0000000000..c843308480 --- /dev/null +++ b/src/reflect/scala/reflect/internal/FatalError.scala @@ -0,0 +1,6 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect.internal +case class FatalError(msg: String) extends Exception(msg) diff --git a/src/reflect/scala/reflect/internal/FlagSets.scala b/src/reflect/scala/reflect/internal/FlagSets.scala new file mode 100644 index 0000000000..0354d2513c --- /dev/null +++ b/src/reflect/scala/reflect/internal/FlagSets.scala @@ -0,0 +1,66 @@ +package scala.reflect +package internal + +import language.implicitConversions + +trait FlagSets extends api.FlagSets { self: SymbolTable => + + type FlagSet = Long + implicit val FlagSetTag = ClassTag[FlagSet](classOf[FlagSet]) + + implicit def addFlagOps(left: FlagSet): FlagOps = + new FlagOpsImpl(left) + + private class FlagOpsImpl(left: Long) extends FlagOps { + def | (right: Long): Long = left | right + def & (right: Long): Long = left & right + def containsAll (right: Long): Boolean = (right & ~left) == 0 + } + + val NoFlags: FlagSet = 0L + + trait FlagValues extends FlagValuesApi + + object Flag extends FlagValues { + val TRAIT : FlagSet = Flags.TRAIT + val MODULE : FlagSet = Flags.MODULE + val MUTABLE : FlagSet = Flags.MUTABLE + val PACKAGE : FlagSet = Flags.PACKAGE + val METHOD : FlagSet = Flags.METHOD + val MACRO : FlagSet = Flags.MACRO + val DEFERRED : FlagSet = Flags.DEFERRED + val ABSTRACT : FlagSet = Flags.ABSTRACT + val FINAL : FlagSet = Flags.FINAL + val SEALED : FlagSet = Flags.SEALED + val IMPLICIT : FlagSet = Flags.IMPLICIT + val LAZY : FlagSet = Flags.LAZY + val OVERRIDE : FlagSet = Flags.OVERRIDE + val PRIVATE : FlagSet = Flags.PRIVATE + val PROTECTED : FlagSet = Flags.PROTECTED + val CASE : FlagSet = Flags.CASE + val ABSOVERRIDE : FlagSet = Flags.ABSOVERRIDE + val BYNAMEPARAM : FlagSet = Flags.BYNAMEPARAM + val PARAM : FlagSet = Flags.PARAM + val PARAMACCESSOR : FlagSet = Flags.PARAMACCESSOR + val CASEACCESSOR : FlagSet = Flags.CASEACCESSOR + val COVARIANT : FlagSet = Flags.COVARIANT + val CONTRAVARIANT : FlagSet = Flags.CONTRAVARIANT + val DEFAULTPARAM : FlagSet = Flags.DEFAULTPARAM + val INTERFACE : FlagSet = Flags.INTERFACE + + def union(flags: FlagSet*): FlagSet = { + var acc = 0L + for (flag <- flags) acc |= flag + acc + } + + def intersection(flags: FlagSet*): FlagSet = { + var acc = -1L + for (flag <- flags) acc &= flag + acc + } + + def containsAll(superset: FlagSet, subset: FlagSet): Boolean = + (subset & ~superset) == 0 + } +} diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala new file mode 100644 index 0000000000..37e5a23819 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -0,0 +1,483 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } + +// Flags at each index of a flags Long. Those marked with /M are used in +// Parsers/JavaParsers and therefore definitely appear on Modifiers; but the +// absence of /M on the other flags does not imply they aren't. +// +// Generated by mkFlagsTable() at Thu Feb 02 20:31:52 PST 2012 +// +// 0: PROTECTED/M +// 1: OVERRIDE/M +// 2: PRIVATE/M +// 3: ABSTRACT/M +// 4: DEFERRED/M +// 5: FINAL/M +// 6: METHOD +// 7: INTERFACE/M +// 8: MODULE +// 9: IMPLICIT/M +// 10: SEALED/M +// 11: CASE/M +// 12: MUTABLE/M +// 13: PARAM/M +// 14: PACKAGE +// 15: MACRO/M +// 16: BYNAMEPARAM/M CAPTURED COVARIANT/M +// 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL +// 18: ABSOVERRIDE/M +// 19: LOCAL/M +// 20: JAVA/M +// 21: SYNTHETIC +// 22: STABLE +// 23: STATIC/M +// 24: CASEACCESSOR/M +// 25: DEFAULTPARAM/M TRAIT/M +// 26: BRIDGE +// 27: ACCESSOR +// 28: SUPERACCESSOR +// 29: PARAMACCESSOR/M +// 30: MODULEVAR +// 31: LAZY/M +// 32: IS_ERROR +// 33: OVERLOADED +// 34: LIFTED +// 35: EXISTENTIAL MIXEDIN +// 36: EXPANDEDNAME +// 37: IMPLCLASS PRESUPER/M +// 38: TRANS_FLAG +// 39: LOCKED +// 40: SPECIALIZED +// 41: DEFAULTINIT/M +// 42: VBRIDGE +// 43: VARARGS +// 44: TRIEDCOOKING +// 45: +// 46: +// 47: +// 48: +// 49: +// 50: +// 51: lateDEFERRED +// 52: lateFINAL +// 53: lateMETHOD +// 54: lateINTERFACE +// 55: lateMODULE +// 56: notPROTECTED +// 57: notOVERRIDE +// 58: notPRIVATE +// 59: +// 60: +// 61: +// 62: +// 63: + +/** Flags set on Modifiers instances in the parsing stage. + */ +class ModifierFlags { + final val IMPLICIT = 1 << 9 + final val FINAL = 1 << 5 // May not be overridden. Note that java final implies much more than scala final. + final val PRIVATE = 1 << 2 + final val PROTECTED = 1 << 0 + + final val SEALED = 1 << 10 + final val OVERRIDE = 1 << 1 + final val CASE = 1 << 11 + final val ABSTRACT = 1 << 3 // abstract class, or used in conjunction with abstract override. + // Note difference to DEFERRED! + final val DEFERRED = 1 << 4 // was `abstract' for members | trait is virtual + final val INTERFACE = 1 << 7 // symbol is an interface (i.e. a trait which defines only abstract methods) + final val MUTABLE = 1 << 12 // symbol is a mutable variable. + final val PARAM = 1 << 13 // symbol is a (value or type) parameter to a method + final val MACRO = 1 << 15 // symbol is a macro definition + + final val COVARIANT = 1 << 16 // symbol is a covariant type variable + final val BYNAMEPARAM = 1 << 16 // parameter is by name + final val CONTRAVARIANT = 1 << 17 // symbol is a contravariant type variable + final val ABSOVERRIDE = 1 << 18 // combination of abstract & override + final val LOCAL = 1 << 19 // symbol is local to current class (i.e. private[this] or protected[this] + // pre: PRIVATE or PROTECTED are also set + final val JAVA = 1 << 20 // symbol was defined by a Java class + final val STATIC = 1 << 23 // static field, method or class + final val CASEACCESSOR = 1 << 24 // symbol is a case parameter (or its accessor, or a GADT skolem) + final val TRAIT = 1 << 25 // symbol is a trait + final val DEFAULTPARAM = 1 << 25 // the parameter has a default value + final val PARAMACCESSOR = 1 << 29 // for field definitions generated for primary constructor + // parameters (no matter if it's a 'val' parameter or not) + // for parameters of a primary constructor ('val' or not) + // for the accessor methods generated for 'val' or 'var' parameters + final val LAZY = 1L << 31 // symbol is a lazy val. can't have MUTABLE unless transformed by typer + final val PRESUPER = 1L << 37 // value is evaluated before super call + final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit + + // Overridden. + def flagToString(flag: Long): String = "" + + final val PrivateLocal = PRIVATE | LOCAL + final val ProtectedLocal = PROTECTED | LOCAL + final val AccessFlags = PRIVATE | PROTECTED | LOCAL +} +object ModifierFlags extends ModifierFlags + +/** All flags and associated operatins */ +class Flags extends ModifierFlags { + final val METHOD = 1 << 6 // a method + final val MODULE = 1 << 8 // symbol is module or class implementing a module + final val PACKAGE = 1 << 14 // symbol is a java package + + final val CAPTURED = 1 << 16 // variable is accessed from nested function. Set by LambdaLift. + final val LABEL = 1 << 17 // method symbol is a label. Set by TailCall + final val INCONSTRUCTOR = 1 << 17 // class symbol is defined in this/superclass constructor. + final val SYNTHETIC = 1 << 21 // symbol is compiler-generated + final val STABLE = 1 << 22 // functions that are assumed to be stable + // (typically, access methods for valdefs) + // or classes that do not contain abstract types. + final val BRIDGE = 1 << 26 // function is a bridge method. Set by Erasure + final val ACCESSOR = 1 << 27 // a value or variable accessor (getter or setter) + + final val SUPERACCESSOR = 1 << 28 // a super accessor + final val MODULEVAR = 1 << 30 // for variables: is the variable caching a module value + + final val IS_ERROR = 1L << 32 // symbol is an error symbol + final val OVERLOADED = 1L << 33 // symbol is overloaded + final val LIFTED = 1L << 34 // class has been lifted out to package level + // local value has been lifted out to class level + // todo: make LIFTED = latePRIVATE? + final val MIXEDIN = 1L << 35 // term member has been mixed in + final val EXISTENTIAL = 1L << 35 // type is an existential parameter or skolem + final val EXPANDEDNAME = 1L << 36 // name has been expanded with class suffix + final val IMPLCLASS = 1L << 37 // symbol is an implementation class + final val TRANS_FLAG = 1L << 38 // transient flag guaranteed to be reset after each phase. + + final val LOCKED = 1L << 39 // temporary flag to catch cyclic dependencies + final val SPECIALIZED = 1L << 40 // symbol is a generated specialized member + final val VBRIDGE = 1L << 42 // symbol is a varargs bridge + + final val VARARGS = 1L << 43 // symbol is a Java-style varargs method + final val TRIEDCOOKING = 1L << 44 // ``Cooking'' has been tried on this symbol + // A Java method's type is ``cooked'' by transforming raw types to existentials + + final val SYNCHRONIZED = 1L << 45 // symbol is a method which should be marked ACC_SYNCHRONIZED + // ------- shift definitions ------------------------------------------------------- + + final val InitialFlags = 0x0001FFFFFFFFFFFFL // flags that are enabled from phase 1. + final val LateFlags = 0x00FE000000000000L // flags that override flags in 0x1FC. + final val AntiFlags = 0x7F00000000000000L // flags that cancel flags in 0x07F + final val LateShift = 47L + final val AntiShift = 56L + + // Flags which sketchily share the same slot + val OverloadedFlagsMask = 0L | BYNAMEPARAM | CONTRAVARIANT | DEFAULTPARAM | EXISTENTIAL | IMPLCLASS + + // ------- late flags (set by a transformer phase) --------------------------------- + // + // Summary of when these are claimed to be first used. + // You can get this output with scalac -Xshow-phases -Ydebug. + // + // refchecks 7 [START] <latemethod> + // specialize 13 [START] <latefinal> <notprivate> + // explicitouter 14 [START] <notprotected> + // erasure 15 [START] <latedeferred> <lateinterface> + // mixin 20 [START] <latemodule> <notoverride> + // + // lateMETHOD set in RefChecks#transformInfo. + // lateFINAL set in Symbols#makeNotPrivate. + // notPRIVATE set in Symbols#makeNotPrivate, IExplicitOuter#transform, Inliners. + // notPROTECTED set in ExplicitOuter#transform. + // lateDEFERRED set in AddInterfaces, Mixin, etc. + // lateINTERFACE set in AddInterfaces#transformMixinInfo. + // lateMODULE set in Mixin#transformInfo. + // notOVERRIDE set in Mixin#preTransform. + + final val lateDEFERRED = (DEFERRED: Long) << LateShift + final val lateFINAL = (FINAL: Long) << LateShift + final val lateINTERFACE = (INTERFACE: Long) << LateShift + final val lateMETHOD = (METHOD: Long) << LateShift + final val lateMODULE = (MODULE: Long) << LateShift + + final val notOVERRIDE = (OVERRIDE: Long) << AntiShift + final val notPRIVATE = (PRIVATE: Long) << AntiShift + final val notPROTECTED = (PROTECTED: Long) << AntiShift + + // ------- masks ----------------------------------------------------------------------- + + /** To be a little clearer to people who aren't habitual bit twiddlers. + */ + final val AllFlags = -1L + + /** These flags can be set when class or module symbol is first created. + * They are the only flags to survive a call to resetFlags(). + */ + final val TopLevelCreationFlags = + MODULE | PACKAGE | FINAL | JAVA + + // TODO - there's no call to slap four flags onto every package. + final val PackageFlags = TopLevelCreationFlags + + // FINAL not included here due to possibility of object overriding. + // In fact, FINAL should not be attached regardless. We should be able + // to reconstruct whether an object was marked final in source. + final val ModuleFlags = MODULE + + /** These modifiers can be set explicitly in source programs. This is + * used only as the basis for the default flag mask (which ones to display + * when printing a normal message.) + */ + final val ExplicitFlags = + PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED | + OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY + + /** The two bridge flags */ + final val BridgeFlags = BRIDGE | VBRIDGE + final val BridgeAndPrivateFlags = BridgeFlags | PRIVATE + + /** These modifiers appear in TreePrinter output. */ + final val PrintableFlags = + ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED + + /** When a symbol for a field is created, only these flags survive + * from Modifiers. Others which may be applied at creation time are: + * PRIVATE, LOCAL. + */ + final val FieldFlags = + MUTABLE | CASEACCESSOR | PARAMACCESSOR | STATIC | FINAL | PRESUPER | LAZY + + /** Masks for getters and setters, where the flags are derived from those + * on the field's modifiers. Both getters and setters get the ACCESSOR flag. + * Getters of immutable values also get STABLE. + */ + final val GetterFlags = ~(PRESUPER | MUTABLE) + final val SetterFlags = ~(PRESUPER | MUTABLE | STABLE | CASEACCESSOR) + + /** When a symbol for a default getter is created, it inherits these + * flags from the method with the default. Other flags applied at creation + * time are SYNTHETIC, DEFAULTPARAM, and possibly OVERRIDE, and maybe PRESUPER. + */ + final val DefaultGetterFlags = PRIVATE | PROTECTED | FINAL + + /** When a symbol for a method parameter is created, only these flags survive + * from Modifiers. Others which may be applied at creation time are: + * SYNTHETIC. + */ + final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM + final val BeanPropertyFlags = DEFERRED | OVERRIDE | STATIC + final val VarianceFlags = COVARIANT | CONTRAVARIANT + + /** These appear to be flags which should be transferred from owner symbol + * to a newly created constructor symbol. + */ + final val ConstrFlags = JAVA + + /** Module flags inherited by their module-class */ + final val ModuleToClassFlags = AccessFlags | TopLevelCreationFlags | CASE | SYNTHETIC + + def getterFlags(fieldFlags: Long): Long = ACCESSOR + ( + if ((fieldFlags & MUTABLE) != 0) fieldFlags & ~MUTABLE & ~PRESUPER + else fieldFlags & ~PRESUPER | STABLE + ) + + def setterFlags(fieldFlags: Long): Long = + getterFlags(fieldFlags) & ~STABLE & ~CASEACCESSOR + + // ------- pickling and unpickling of flags ----------------------------------------------- + + // The flags from 0x001 to 0x800 are different in the raw flags + // and in the pickled format. + + private final val IMPLICIT_PKL = (1 << 0) + private final val FINAL_PKL = (1 << 1) + private final val PRIVATE_PKL = (1 << 2) + private final val PROTECTED_PKL = (1 << 3) + private final val SEALED_PKL = (1 << 4) + private final val OVERRIDE_PKL = (1 << 5) + private final val CASE_PKL = (1 << 6) + private final val ABSTRACT_PKL = (1 << 7) + private final val DEFERRED_PKL = (1 << 8) + private final val METHOD_PKL = (1 << 9) + private final val MODULE_PKL = (1 << 10) + private final val INTERFACE_PKL = (1 << 11) + + private final val PKL_MASK = 0x00000FFF + + final val PickledFlags = 0xFFFFFFFFL + + private def rawPickledCorrespondence = Array( + (IMPLICIT, IMPLICIT_PKL), + (FINAL, FINAL_PKL), + (PRIVATE, PRIVATE_PKL), + (PROTECTED, PROTECTED_PKL), + (SEALED, SEALED_PKL), + (OVERRIDE, OVERRIDE_PKL), + (CASE, CASE_PKL), + (ABSTRACT, ABSTRACT_PKL), + (DEFERRED, DEFERRED_PKL), + (METHOD, METHOD_PKL), + (MODULE, MODULE_PKL), + (INTERFACE, INTERFACE_PKL) + ) + private val rawFlags: Array[Int] = rawPickledCorrespondence map (_._1) + private val pickledFlags: Array[Int] = rawPickledCorrespondence map (_._2) + + private def r2p(flags: Int): Int = { + var result = 0 + var i = 0 + while (i < rawFlags.length) { + if ((flags & rawFlags(i)) != 0) + result |= pickledFlags(i) + + i += 1 + } + result + } + private def p2r(flags: Int): Int = { + var result = 0 + var i = 0 + while (i < rawFlags.length) { + if ((flags & pickledFlags(i)) != 0) + result |= rawFlags(i) + + i += 1 + } + result + } + + // ------ displaying flags -------------------------------------------------------- + + // Generated by mkFlagToStringMethod() at Thu Feb 02 20:31:52 PST 2012 + @annotation.switch override def flagToString(flag: Long): String = flag match { + case PROTECTED => "protected" // (1L << 0) + case OVERRIDE => "override" // (1L << 1) + case PRIVATE => "private" // (1L << 2) + case ABSTRACT => "abstract" // (1L << 3) + case DEFERRED => "<deferred>" // (1L << 4) + case FINAL => "final" // (1L << 5) + case METHOD => "<method>" // (1L << 6) + case INTERFACE => "<interface>" // (1L << 7) + case MODULE => "<module>" // (1L << 8) + case IMPLICIT => "implicit" // (1L << 9) + case SEALED => "sealed" // (1L << 10) + case CASE => "case" // (1L << 11) + case MUTABLE => "<mutable>" // (1L << 12) + case PARAM => "<param>" // (1L << 13) + case PACKAGE => "<package>" // (1L << 14) + case MACRO => "<macro>" // (1L << 15) + case BYNAMEPARAM => "<bynameparam/captured/covariant>" // (1L << 16) + case CONTRAVARIANT => "<contravariant/inconstructor/label>" // (1L << 17) + case ABSOVERRIDE => "absoverride" // (1L << 18) + case LOCAL => "<local>" // (1L << 19) + case JAVA => "<java>" // (1L << 20) + case SYNTHETIC => "<synthetic>" // (1L << 21) + case STABLE => "<stable>" // (1L << 22) + case STATIC => "<static>" // (1L << 23) + case CASEACCESSOR => "<caseaccessor>" // (1L << 24) + case DEFAULTPARAM => "<defaultparam/trait>" // (1L << 25) + case BRIDGE => "<bridge>" // (1L << 26) + case ACCESSOR => "<accessor>" // (1L << 27) + case SUPERACCESSOR => "<superaccessor>" // (1L << 28) + case PARAMACCESSOR => "<paramaccessor>" // (1L << 29) + case MODULEVAR => "<modulevar>" // (1L << 30) + case LAZY => "lazy" // (1L << 31) + case IS_ERROR => "<is_error>" // (1L << 32) + case OVERLOADED => "<overloaded>" // (1L << 33) + case LIFTED => "<lifted>" // (1L << 34) + case EXISTENTIAL => "<existential/mixedin>" // (1L << 35) + case EXPANDEDNAME => "<expandedname>" // (1L << 36) + case IMPLCLASS => "<implclass/presuper>" // (1L << 37) + case TRANS_FLAG => "<trans_flag>" // (1L << 38) + case LOCKED => "<locked>" // (1L << 39) + case SPECIALIZED => "<specialized>" // (1L << 40) + case DEFAULTINIT => "<defaultinit>" // (1L << 41) + case VBRIDGE => "<vbridge>" // (1L << 42) + case VARARGS => "<varargs>" // (1L << 43) + case TRIEDCOOKING => "<triedcooking>" // (1L << 44) + case SYNCHRONIZED => "<synchronized>" // (1L << 45) + case 0x400000000000L => "" // (1L << 46) + case 0x800000000000L => "" // (1L << 47) + case 0x1000000000000L => "" // (1L << 48) + case 0x2000000000000L => "" // (1L << 49) + case 0x4000000000000L => "" // (1L << 50) + case `lateDEFERRED` => "<latedeferred>" // (1L << 51) + case `lateFINAL` => "<latefinal>" // (1L << 52) + case `lateMETHOD` => "<latemethod>" // (1L << 53) + case `lateINTERFACE` => "<lateinterface>" // (1L << 54) + case `lateMODULE` => "<latemodule>" // (1L << 55) + case `notPROTECTED` => "<notprotected>" // (1L << 56) + case `notOVERRIDE` => "<notoverride>" // (1L << 57) + case `notPRIVATE` => "<notprivate>" // (1L << 58) + case 0x800000000000000L => "" // (1L << 59) + case 0x1000000000000000L => "" // (1L << 60) + case 0x2000000000000000L => "" // (1L << 61) + case 0x4000000000000000L => "" // (1L << 62) + case 0x8000000000000000L => "" // (1L << 63) + case _ => "" + } + + private def accessString(flags: Long, privateWithin: String)= ( + if (privateWithin == "") { + if ((flags & PrivateLocal) == PrivateLocal) "private[this]" + else if ((flags & ProtectedLocal) == ProtectedLocal) "protected[this]" + else if ((flags & PRIVATE) != 0) "private" + else if ((flags & PROTECTED) != 0) "protected" + else "" + } + else if ((flags & PROTECTED) != 0) "protected[" + privateWithin + "]" + else "private[" + privateWithin + "]" + ) + + @deprecated("Use flagString on the flag-carrying member", "2.10.0") + def flagsToString(flags: Long, privateWithin: String): String = { + val access = accessString(flags, privateWithin) + val nonAccess = flagsToString(flags & ~AccessFlags) + + List(nonAccess, access) filterNot (_ == "") mkString " " + } + + @deprecated("Use flagString on the flag-carrying member", "2.10.0") + def flagsToString(flags: Long): String = { + // Fast path for common case + if (flags == 0L) "" else { + var sb: StringBuilder = null + var i = 0 + while (i <= MaxBitPosition) { + val mask = rawFlagPickledOrder(i) + if ((flags & mask) != 0L) { + val s = flagToString(mask) + if (s.length > 0) { + if (sb eq null) sb = new StringBuilder append s + else if (sb.length == 0) sb append s + else sb append " " append s + } + } + i += 1 + } + if (sb eq null) "" else sb.toString + } + } + + def rawFlagsToPickled(flags: Long): Long = + (flags & ~PKL_MASK) | r2p(flags.toInt & PKL_MASK) + + def pickledToRawFlags(pflags: Long): Long = + (pflags & ~PKL_MASK) | p2r(pflags.toInt & PKL_MASK) + + // List of the raw flags, in pickled order + final val MaxBitPosition = 62 + + final val pickledListOrder: List[Long] = { + val all = 0 to MaxBitPosition map (1L << _) + val front = rawFlags map (_.toLong) + + front.toList ++ (all filterNot (front contains _)) + } + final val rawFlagPickledOrder: Array[Long] = pickledListOrder.toArray +} + +object Flags extends Flags { } diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala new file mode 100644 index 0000000000..c7c0882209 --- /dev/null +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -0,0 +1,169 @@ +package scala.reflect +package internal + +import Flags._ + +/** Common code utilized by Modifiers (which carry the flags associated + * with Trees) and Symbol. + */ +trait HasFlags { + 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: Long + + /** 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) + + /** The printable representation of this entity's flags and access boundary, + * restricted to flags in the given mask. + */ + def flagString: String = flagString(flagMask) + def flagString(mask: Long): String = calculateFlagString(flags & mask) + + /** The default mask determining which flags to display. + */ + def flagMask: Long = AllFlags + + /** The string representation of a single bit, seen from this + * flag carrying entity. + */ + def resolveOverloadedFlag(flag: Long): String = Flags.flagToString(flag) + + // Tests which come through cleanly: both Symbol and Modifiers use these + // identically, testing for a single flag. + def hasAbstractFlag = hasFlag(ABSTRACT) + def hasAccessorFlag = hasFlag(ACCESSOR) + def hasDefault = hasAllFlags(DEFAULTPARAM | PARAM) + def hasLocalFlag = hasFlag(LOCAL) + def hasModuleFlag = hasFlag(MODULE) + def hasPackageFlag = hasFlag(PACKAGE) + def hasStableFlag = hasFlag(STABLE) + def hasStaticFlag = hasFlag(STATIC) + def isAbstractOverride = hasFlag(ABSOVERRIDE) + def isAnyOverride = hasFlag(OVERRIDE | ABSOVERRIDE) + def isCase = hasFlag(CASE) + def isCaseAccessor = hasFlag(CASEACCESSOR) + def isDeferred = hasFlag(DEFERRED) + def isFinal = hasFlag(FINAL) + def isImplicit = hasFlag(IMPLICIT) + def isInterface = hasFlag(INTERFACE) + def isJavaDefined = hasFlag(JAVA) + def isLabel = hasAllFlags(LABEL | METHOD) && !hasAccessorFlag + def isLazy = hasFlag(LAZY) + def isLifted = hasFlag(LIFTED) + def isMutable = hasFlag(MUTABLE) + def isOverride = hasFlag(OVERRIDE) + def isParamAccessor = hasFlag(PARAMACCESSOR) + def isPrivate = hasFlag(PRIVATE) + def isPackage = hasFlag(PACKAGE) + def isPrivateLocal = hasAllFlags(PrivateLocal) + def isProtected = hasFlag(PROTECTED) + def isProtectedLocal = hasAllFlags(ProtectedLocal) + def isPublic = hasNoFlags(PRIVATE | PROTECTED) && !hasAccessBoundary + def isSealed = hasFlag(SEALED) + def isSuperAccessor = hasFlag(SUPERACCESSOR) + def isSynthetic = hasFlag(SYNTHETIC) + def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM) + + def flagBitsToString(bits: Long): String = { + // Fast path for common case + if (bits == 0L) "" else { + var sb: StringBuilder = null + var i = 0 + while (i <= MaxBitPosition) { + val flag = Flags.rawFlagPickledOrder(i) + if ((bits & flag) != 0L) { + val s = resolveOverloadedFlag(flag) + if (s.length > 0) { + if (sb eq null) sb = new StringBuilder append s + else if (sb.length == 0) sb append s + else sb append " " append s + } + } + i += 1 + } + if (sb eq null) "" else sb.toString + } + } + + def accessString: String = { + val pw = if (hasAccessBoundary) privateWithin.toString else "" + + if (pw == "") { + if (hasAllFlags(PrivateLocal)) "private[this]" + else if (hasAllFlags(ProtectedLocal)) "protected[this]" + else if (hasFlag(PRIVATE)) "private" + else if (hasFlag(PROTECTED)) "protected" + else "" + } + else if (hasFlag(PROTECTED)) "protected[" + pw + "]" + else "private[" + pw + "]" + } + protected def calculateFlagString(basis: Long): String = { + val access = accessString + val nonAccess = flagBitsToString(basis & ~AccessFlags) + + if (access == "") nonAccess + else if (nonAccess == "") access + else nonAccess + " " + access + } + + // Backward compat section + @deprecated( "Use isTrait", "2.10.0") + def hasTraitFlag = hasFlag(TRAIT) + @deprecated("Use hasDefault", "2.10.0") + def hasDefaultFlag = hasFlag(DEFAULTPARAM) + @deprecated("Use isValueParameter or isTypeParameter", "2.10.0") + def isParameter = hasFlag(PARAM) + @deprecated("Use flagString", "2.10.0") + def defaultFlagString = flagString + @deprecated("Use flagString(mask)", "2.10.0") + def hasFlagsToString(mask: Long): String = flagString(mask) +} diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala new file mode 100644 index 0000000000..431d9819a5 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -0,0 +1,451 @@ +package scala.reflect +package internal +import scala.collection.mutable.WeakHashMap + +// todo: move importers to a mirror +trait Importers { self: SymbolTable => + + // [Eugene] possible to make this less cast-heavy? + def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( + if (self eq from0) { + new Importer { + val from = from0 + val reverse = this.asInstanceOf[from.Importer{ val from: self.type }] + def importSymbol(sym: from.Symbol) = sym.asInstanceOf[self.Symbol] + def importType(tpe: from.Type) = tpe.asInstanceOf[self.Type] + def importTree(tree: from.Tree) = tree.asInstanceOf[self.Tree] + } + } else { + // todo. fix this loophole + assert(from0.isInstanceOf[SymbolTable], "`from` should be an instance of scala.reflect.internal.SymbolTable") + new StandardImporter { val from = from0.asInstanceOf[SymbolTable] } + } + ).asInstanceOf[Importer { val from: from0.type }] + + abstract class StandardImporter extends Importer { + + val from: SymbolTable + + lazy val symMap: WeakHashMap[from.Symbol, Symbol] = new WeakHashMap + lazy val tpeMap: WeakHashMap[from.Type, Type] = new WeakHashMap + + // fixups and maps prevent stackoverflows in importer + var pendingSyms = 0 + var pendingTpes = 0 + lazy val fixups = collection.mutable.MutableList[Function0[Unit]]() + def addFixup(fixup: => Unit): Unit = fixups += (() => fixup) + def tryFixup(): Unit = { + if (pendingSyms == 0 && pendingTpes == 0) { + val fixups = this.fixups.toList + this.fixups.clear() + fixups foreach { _() } + } + } + + object reverse extends from.StandardImporter { + val from: self.type = self + for ((fromsym, mysym) <- StandardImporter.this.symMap) symMap += ((mysym, fromsym)) + for ((fromtpe, mytpe) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, fromtpe)) + } + + // todo. careful import of positions + def importPosition(pos: from.Position): Position = + pos.asInstanceOf[Position] + + def importSymbol(sym0: from.Symbol): Symbol = { + def doImport(sym: from.Symbol): Symbol = { + if (symMap.contains(sym)) + return symMap(sym) + + val myowner = importSymbol(sym.owner) + val mypos = importPosition(sym.pos) + val myname = importName(sym.name).toTermName + val myflags = sym.flags + def linkReferenced(mysym: TermSymbol, x: from.TermSymbol, op: from.Symbol => Symbol): Symbol = { + symMap(x) = mysym + mysym.referenced = op(x.referenced) + mysym + } + val mysym = sym match { + case x: from.MethodSymbol => + linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) + case x: from.ModuleSymbol => + linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, importSymbol) + case x: from.FreeTermSymbol => + newFreeTermSymbol(importName(x.name).toTermName, importType(x.info), x.value, x.flags, x.origin) + case x: from.FreeTypeSymbol => + newFreeTypeSymbol(importName(x.name).toTypeName, importType(x.info), x.value, x.flags, x.origin) + case x: from.TermSymbol => + linkReferenced(myowner.newValue(myname, mypos, myflags), x, importSymbol) + case x: from.TypeSkolem => + val origin = x.unpackLocation match { + case null => null + case y: from.Tree => importTree(y) + case y: from.Symbol => importSymbol(y) + } + myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) + case x: from.ModuleClassSymbol => + val mysym = myowner.newModuleClass(myname.toTypeName, mypos, myflags) + symMap(x) = mysym + mysym.sourceModule = importSymbol(x.sourceModule) + mysym + case x: from.ClassSymbol => + val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) + symMap(x) = mysym + if (sym.thisSym != sym) { + mysym.typeOfThis = importType(sym.typeOfThis) + mysym.thisSym setName importName(sym.thisSym.name) + } + mysym + case x: from.TypeSymbol => + myowner.newTypeSymbol(myname.toTypeName, mypos, myflags) + } + symMap(sym) = mysym + mysym setFlag Flags.LOCKED + mysym setInfo { + val mytypeParams = sym.typeParams map importSymbol + new LazyPolyType(mytypeParams) { + override def complete(s: Symbol) { + val result = sym.info match { + case from.PolyType(_, res) => res + case result => result + } + s setInfo GenPolyType(mytypeParams, importType(result)) + s setAnnotations (sym.annotations map importAnnotationInfo) + } + } + } + mysym resetFlag Flags.LOCKED + } // end doImport + + def importOrRelink: Symbol = { + val sym = sym0 // makes sym visible in the debugger + if (sym == null) + null + else if (sym == from.NoSymbol) + NoSymbol + else if (sym.isRoot) + rootMirror.RootClass // !!! replace with actual mirror when we move importers to the mirror + else { + val name = sym.name + val owner = sym.owner + var scope = if (owner.isClass && !owner.isRefinementClass) owner.info else from.NoType + var existing = scope.decl(name) + if (sym.isModuleClass) + existing = existing.moduleClass + + if (!existing.exists) scope = from.NoType + + val myname = importName(name) + val myowner = importSymbol(owner) + val myscope = if (scope != from.NoType && !(myowner hasFlag Flags.LOCKED)) myowner.info else NoType + var myexisting = if (myscope != NoType) myowner.info.decl(myname) else NoSymbol // cannot load myexisting in general case, because it creates cycles for methods + if (sym.isModuleClass) + myexisting = importSymbol(sym.sourceModule).moduleClass + + if (!sym.isOverloaded && myexisting.isOverloaded) { + myexisting = + if (sym.isMethod) { + val localCopy = doImport(sym) + myexisting filter (_.tpe matches localCopy.tpe) + } else { + myexisting filter (!_.isMethod) + } + assert(!myexisting.isOverloaded, + "import failure: cannot determine unique overloaded method alternative from\n "+ + (myexisting.alternatives map (_.defString) mkString "\n")+"\n that matches "+sym+":"+sym.tpe) + } + + val mysym = { + if (sym.isOverloaded) { + myowner.newOverloaded(myowner.thisType, sym.alternatives map importSymbol) + } else if (sym.isTypeParameter && sym.paramPos >= 0 && !(myowner hasFlag Flags.LOCKED)) { + assert(myowner.typeParams.length > sym.paramPos, + "import failure: cannot determine parameter "+sym+" (#"+sym.paramPos+") in "+ + myowner+typeParamsString(myowner.rawInfo)+"\n original symbol was: "+ + sym.owner+from.typeParamsString(sym.owner.info)) + myowner.typeParams(sym.paramPos) + } else { + if (myexisting != NoSymbol) { + myexisting + } else { + val mysym = doImport(sym) + + if (myscope != NoType) { + assert(myowner.info.decls.lookup(myname) == NoSymbol, myname+" "+myowner.info.decl(myname)+" "+myexisting) + myowner.info.decls enter mysym + } + + mysym + } + } + } + + mysym + } + } // end importOrRelink + + val sym = sym0 + if (symMap contains sym) { + symMap(sym) + } else { + pendingSyms += 1 + + try { + symMap getOrElseUpdate (sym, importOrRelink) + } finally { + pendingSyms -= 1 + tryFixup() + } + } + } + + def importType(tpe: from.Type): Type = { + def doImport(tpe: from.Type): Type = tpe match { + case from.TypeRef(pre, sym, args) => + TypeRef(importType(pre), importSymbol(sym), args map importType) + case from.ThisType(clazz) => + ThisType(importSymbol(clazz)) + case from.SingleType(pre, sym) => + SingleType(importType(pre), importSymbol(sym)) + case from.MethodType(params, restpe) => + MethodType(params map importSymbol, importType(restpe)) + case from.PolyType(tparams, restpe) => + PolyType(tparams map importSymbol, importType(restpe)) + case from.NullaryMethodType(restpe) => + NullaryMethodType(importType(restpe)) + case from.ConstantType(constant @ from.Constant(_)) => + ConstantType(importConstant(constant)) + case from.SuperType(thistpe, supertpe) => + SuperType(importType(thistpe), importType(supertpe)) + case from.TypeBounds(lo, hi) => + TypeBounds(importType(lo), importType(hi)) + case from.BoundedWildcardType(bounds) => + BoundedWildcardType(importTypeBounds(bounds)) + case from.ClassInfoType(parents, decls, clazz) => + val myclazz = importSymbol(clazz) + val myscope = if (myclazz.isPackageClass) newPackageScope(myclazz) else newScope + val myclazzTpe = ClassInfoType(parents map importType, myscope, myclazz) + myclazz setInfo GenPolyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope + decls foreach importSymbol // will enter itself into myclazz + myclazzTpe + case from.RefinedType(parents, decls) => + RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol)) + case from.ExistentialType(tparams, restpe) => + newExistentialType(tparams map importSymbol, importType(restpe)) + case from.OverloadedType(pre, alts) => + OverloadedType(importType(pre), alts map importSymbol) + case from.AntiPolyType(pre, targs) => + AntiPolyType(importType(pre), targs map importType) + case x: from.TypeVar => + TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) + case from.NotNullType(tpe) => + NotNullType(importType(tpe)) + case from.AnnotatedType(annots, tpe, selfsym) => + AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym)) + case from.ErrorType => + ErrorType + case from.WildcardType => + WildcardType + case from.NoType => + NoType + case from.NoPrefix => + NoPrefix + case null => + null + } // end doImport + + def importOrRelink: Type = + doImport(tpe) + + if (tpeMap contains tpe) { + tpeMap(tpe) + } else { + pendingTpes += 1 + + try { + tpeMap getOrElseUpdate (tpe, importOrRelink) + } finally { + pendingTpes -= 1 + tryFixup() + } + } + } + + def importTypeBounds(bounds: from.TypeBounds) = importType(bounds).asInstanceOf[TypeBounds] + + def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = { + val atp1 = importType(ann.atp) + val args1 = ann.args map importTree + val assocs1 = ann.assocs map { case (name, arg) => (importName(name), importAnnotArg(arg)) } + val original1 = importTree(ann.original) + AnnotationInfo(atp1, args1, assocs1) setOriginal original1 + } + + def importAnnotArg(arg: from.ClassfileAnnotArg): ClassfileAnnotArg = arg match { + case from.LiteralAnnotArg(constant @ from.Constant(_)) => + LiteralAnnotArg(importConstant(constant)) + case from.ArrayAnnotArg(args) => + ArrayAnnotArg(args map importAnnotArg) + case from.ScalaSigBytes(bytes) => + ScalaSigBytes(bytes) + case from.NestedAnnotArg(annInfo) => + NestedAnnotArg(importAnnotationInfo(annInfo)) + } + + def importTypeConstraint(constr: from.TypeConstraint): TypeConstraint = { + val result = new TypeConstraint(constr.loBounds map importType, constr.hiBounds map importType) + result.inst = importType(constr.inst) + result + } + + // !!! todo: override to cater for PackageScopes + def importScope(decls: from.Scope): Scope = + newScopeWith(decls.toList map importSymbol: _*) + + def importName(name: from.Name): Name = + if (name.isTypeName) newTypeName(name.toString) else newTermName(name.toString) + def importTypeName(name: from.TypeName): TypeName = importName(name).toTypeName + def importTermName(name: from.TermName): TermName = importName(name).toTermName + + def importModifiers(mods: from.Modifiers): Modifiers = + new Modifiers(mods.flags, importName(mods.privateWithin), mods.annotations map importTree) + + def importImportSelector(sel: from.ImportSelector): ImportSelector = + new ImportSelector(importName(sel.name), sel.namePos, if (sel.rename != null) importName(sel.rename) else null, sel.renamePos) + + def importTree(tree: from.Tree): Tree = { + val mytree = tree match { + case from.ClassDef(mods, name, tparams, impl) => + new ClassDef(importModifiers(mods), importName(name).toTypeName, tparams map importTypeDef, importTemplate(impl)) + case from.PackageDef(pid, stats) => + new PackageDef(importRefTree(pid), stats map importTree) + case from.ModuleDef(mods, name, impl) => + new ModuleDef(importModifiers(mods), importName(name).toTermName, importTemplate(impl)) + case from.emptyValDef => + emptyValDef + case from.ValDef(mods, name, tpt, rhs) => + new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs)) + case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) => + new DefDef(importModifiers(mods), importName(name).toTermName, tparams map importTypeDef, mmap(vparamss)(importValDef), importTree(tpt), importTree(rhs)) + case from.TypeDef(mods, name, tparams, rhs) => + new TypeDef(importModifiers(mods), importName(name).toTypeName, tparams map importTypeDef, importTree(rhs)) + case from.LabelDef(name, params, rhs) => + new LabelDef(importName(name).toTermName, params map importIdent, importTree(rhs)) + case from.Import(expr, selectors) => + new Import(importTree(expr), selectors map importImportSelector) + case from.Template(parents, self, body) => + new Template(parents map importTree, importValDef(self), body map importTree) + case from.Block(stats, expr) => + new Block(stats map importTree, importTree(expr)) + case from.CaseDef(pat, guard, body) => + new CaseDef(importTree(pat), importTree(guard), importTree(body)) + case from.Alternative(trees) => + new Alternative(trees map importTree) + case from.Star(elem) => + new Star(importTree(elem)) + case from.Bind(name, body) => + new Bind(importName(name), importTree(body)) + case from.UnApply(fun, args) => + new UnApply(importTree(fun), args map importTree) + case from.ArrayValue(elemtpt ,elems) => + new ArrayValue(importTree(elemtpt), elems map importTree) + case from.Function(vparams, body) => + new Function(vparams map importValDef, importTree(body)) + case from.Assign(lhs, rhs) => + new Assign(importTree(lhs), importTree(rhs)) + case from.AssignOrNamedArg(lhs, rhs) => + new AssignOrNamedArg(importTree(lhs), importTree(rhs)) + case from.If(cond, thenp, elsep) => + new If(importTree(cond), importTree(thenp), importTree(elsep)) + case from.Match(selector, cases) => + new Match(importTree(selector), cases map importCaseDef) + case from.Return(expr) => + new Return(importTree(expr)) + case from.Try(block, catches, finalizer) => + new Try(importTree(block), catches map importCaseDef, importTree(finalizer)) + case from.Throw(expr) => + new Throw(importTree(expr)) + case from.New(tpt) => + new New(importTree(tpt)) + case from.Typed(expr, tpt) => + new Typed(importTree(expr), importTree(tpt)) + case from.TypeApply(fun, args) => + new TypeApply(importTree(fun), args map importTree) + case from.Apply(fun, args) => tree match { + case _: from.ApplyToImplicitArgs => + new ApplyToImplicitArgs(importTree(fun), args map importTree) + case _: from.ApplyImplicitView => + new ApplyImplicitView(importTree(fun), args map importTree) + case _ => + new Apply(importTree(fun), args map importTree) + } + case from.ApplyDynamic(qual, args) => + new ApplyDynamic(importTree(qual), args map importTree) + case from.Super(qual, mix) => + new Super(importTree(qual), importTypeName(mix)) + case from.This(qual) => + new This(importName(qual).toTypeName) + case from.Select(qual, name) => + new Select(importTree(qual), importName(name)) + case from.Ident(name) => + new Ident(importName(name)) + case from.ReferenceToBoxed(ident) => + new ReferenceToBoxed(importTree(ident) match { case ident: Ident => ident }) + case from.Literal(constant @ from.Constant(_)) => + new Literal(importConstant(constant)) + case from.TypeTree() => + new TypeTree() + case from.Annotated(annot, arg) => + new Annotated(importTree(annot), importTree(arg)) + case from.SingletonTypeTree(ref) => + new SingletonTypeTree(importTree(ref)) + case from.SelectFromTypeTree(qual, name) => + new SelectFromTypeTree(importTree(qual), importName(name).toTypeName) + case from.CompoundTypeTree(templ) => + new CompoundTypeTree(importTemplate(templ)) + case from.AppliedTypeTree(tpt, args) => + new AppliedTypeTree(importTree(tpt), args map importTree) + case from.TypeBoundsTree(lo, hi) => + new TypeBoundsTree(importTree(lo), importTree(hi)) + case from.ExistentialTypeTree(tpt, whereClauses) => + new ExistentialTypeTree(importTree(tpt), whereClauses map importTree) + case from.EmptyTree => + EmptyTree + case null => + null + } + addFixup({ + if (mytree != null) { + val mysym = if (tree.hasSymbol) importSymbol(tree.symbol) else NoSymbol + val mytpe = importType(tree.tpe) + + mytree match { + case mytt: TypeTree => + val tt = tree.asInstanceOf[from.TypeTree] + if (mytree.hasSymbol) mytt.symbol = mysym + if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe) + if (tt.original != null) mytt.setOriginal(importTree(tt.original)) + case _ => + if (mytree.hasSymbol) mytree.symbol = importSymbol(tree.symbol) + mytree.tpe = importType(tree.tpe) + } + } + }) + tryFixup() + mytree + } + + def importValDef(tree: from.ValDef): ValDef = importTree(tree).asInstanceOf[ValDef] + def importTypeDef(tree: from.TypeDef): TypeDef = importTree(tree).asInstanceOf[TypeDef] + def importTemplate(tree: from.Template): Template = importTree(tree).asInstanceOf[Template] + def importRefTree(tree: from.RefTree): RefTree = importTree(tree).asInstanceOf[RefTree] + def importIdent(tree: from.Ident): Ident = importTree(tree).asInstanceOf[Ident] + def importCaseDef(tree: from.CaseDef): CaseDef = importTree(tree).asInstanceOf[CaseDef] + def importConstant(constant: from.Constant): Constant = new Constant(constant.tag match { + case ClazzTag => importType(constant.value.asInstanceOf[from.Type]) + case EnumTag => importSymbol(constant.value.asInstanceOf[from.Symbol]) + case _ => constant.value + }) + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/InfoTransformers.scala b/src/reflect/scala/reflect/internal/InfoTransformers.scala new file mode 100644 index 0000000000..e53f714c0c --- /dev/null +++ b/src/reflect/scala/reflect/internal/InfoTransformers.scala @@ -0,0 +1,51 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +trait InfoTransformers { + self: SymbolTable => + + /* Syncnote: This should not need to be protected, as reflection does not run in multiple phases. + */ + 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, this.pid) + + if (that.pid < this.pid) { + prev insert that + } else if (next.pid <= that.pid && next.pid != NoPhase.id) { + next insert that + } else { + log("Inserting info transformer %s following %s".format(phaseOf(that.pid), phaseOf(this.pid))) + 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/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala new file mode 100644 index 0000000000..b736a9192f --- /dev/null +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -0,0 +1,232 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } +import scala.reflect.internal.util.StringOps.{ countAsString, countElementsAsString } + +trait Kinds { + self: SymbolTable => + + import definitions._ + + private type SymPair = ((Symbol, Symbol)) // ((Argument, Parameter)) + + case class KindErrors( + arity: List[SymPair] = Nil, + variance: List[SymPair] = Nil, + strictness: List[SymPair] = Nil + ) { + def isEmpty = arity.isEmpty && variance.isEmpty && strictness.isEmpty + + def arityError(syms: SymPair) = copy(arity = arity :+ syms) + def varianceError(syms: SymPair) = copy(variance = variance :+ syms) + def strictnessError(syms: SymPair) = copy(strictness = strictness :+ syms) + + def ++(errs: KindErrors) = KindErrors( + arity ++ errs.arity, + variance ++ errs.variance, + strictness ++ errs.strictness + ) + // @M TODO this method is duplicated all over the place (varianceString) + private def varStr(s: Symbol): String = + if (s.isCovariant) "covariant" + else if (s.isContravariant) "contravariant" + else "invariant"; + + private def qualify(a0: Symbol, b0: Symbol): String = if (a0.toString != b0.toString) "" else { + if((a0 eq b0) || (a0.owner eq b0.owner)) "" + else { + var a = a0; var b = b0 + while (a.owner.name == b.owner.name) { a = a.owner; b = b.owner} + if (a.locationString ne "") " (" + a.locationString.trim + ")" else "" + } + } + private def kindMessage(a: Symbol, p: Symbol)(f: (String, String) => String): String = + f(a+qualify(a,p), p+qualify(p,a)) + + // Normally it's nicer to print nothing rather than '>: Nothing <: Any' all over + // the place, but here we need it for the message to make sense. + private def strictnessMessage(a: Symbol, p: Symbol) = + kindMessage(a, p)("%s's bounds%s are stricter than %s's declared bounds%s".format( + _, a.info, _, p.info match { + case tb @ TypeBounds(_, _) if tb.isEmptyBounds => " >: Nothing <: Any" + case tb => "" + tb + }) + ) + + private def varianceMessage(a: Symbol, p: Symbol) = + kindMessage(a, p)("%s is %s, but %s is declared %s".format(_, varStr(a), _, varStr(p))) + + private def arityMessage(a: Symbol, p: Symbol) = + kindMessage(a, p)("%s has %s, but %s has %s".format( + _, countElementsAsString(a.typeParams.length, "type parameter"), + _, countAsString(p.typeParams.length)) + ) + + private def buildMessage(xs: List[SymPair], f: (Symbol, Symbol) => String) = ( + if (xs.isEmpty) "" + else xs map f.tupled mkString ("\n", ", ", "") + ) + + def errorMessage(targ: Type, tparam: Symbol): String = ( + (targ+"'s type parameters do not match "+tparam+"'s expected parameters:") + + buildMessage(arity, arityMessage) + + buildMessage(variance, varianceMessage) + + buildMessage(strictness, strictnessMessage) + ) + } + val NoKindErrors = KindErrors(Nil, Nil, Nil) + + // 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 whether `sym1`'s variance conforms to `sym2`'s variance. + * + * If `sym2` is invariant, `sym1`'s variance is irrelevant. Otherwise they must be equal. + */ + private def variancesMatch(sym1: Symbol, sym2: Symbol) = ( + sym2.variance==0 + || sym1.variance==sym2.variance + ) + + /** 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, KindErrors)] = { + + // instantiate type params that come from outside the abstract type we're currently checking + def transform(tp: Type, clazz: Symbol): Type = tp.asSeenFrom(pre, clazz) + + // check that the type parameters hkargs to a higher-kinded type conform to the + // expected params hkparams + def checkKindBoundsHK( + hkargs: List[Symbol], + arg: Symbol, + param: Symbol, + paramowner: Symbol, + underHKParams: List[Symbol], + withHKArgs: List[Symbol] + ): KindErrors = { + + var kindErrors: KindErrors = NoKindErrors + 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 + + def kindCheck(cond: Boolean, f: KindErrors => KindErrors) { + if (!cond) + kindErrors = f(kindErrors) + } + + 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)) { + // Any and Nothing are kind-overloaded + if (arg == AnyClass || arg == NothingClass) NoKindErrors + // shortcut: always set error, whether explainTypesOrNot + else return kindErrors.arityError(arg -> param) + } + else foreach2(hkargs, hkparams) { (hkarg, hkparam) => + if (hkparam.typeParams.isEmpty && hkarg.typeParams.isEmpty) { // base-case: kind * + kindCheck(variancesMatch(hkarg, hkparam), _ varianceError (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. + val declaredBounds = transform(hkparam.info.instantiateTypeParams(tparams, targs).bounds, paramowner) + val declaredBoundsInst = transform(bindHKParams(declaredBounds), owner) + val argumentBounds = transform(hkarg.info.bounds, owner) + + kindCheck(declaredBoundsInst <:< argumentBounds, _ strictnessError (hkarg -> hkparam)) + + debuglog( + "checkKindBoundsHK base case: " + hkparam + + " declared bounds: " + declaredBounds + + " after instantiating earlier hkparams: " + declaredBoundsInst + "\n" + + "checkKindBoundsHK base case: "+ hkarg + + " has bounds: " + argumentBounds + ) + } + else { + debuglog("checkKindBoundsHK recursing to compare params of "+ hkparam +" with "+ hkarg) + kindErrors ++= checkKindBoundsHK( + hkarg.typeParams, + hkarg, + hkparam, + paramowner, + underHKParams ++ hkparam.typeParams, + withHKArgs ++ hkarg.typeParams + ) + } + if (!explainErrors && !kindErrors.isEmpty) + return kindErrors + } + if (explainErrors) kindErrors + else NoKindErrors + } + + if (settings.debug.value && (tparams.nonEmpty || targs.nonEmpty)) log( + "checkKindBounds0(" + tparams + ", " + targs + ", " + pre + ", " + + owner + ", " + explainErrors + ")" + ) + + flatMap2(tparams, targs) { (tparam, targ) => + // Prevent WildcardType from causing kind errors, as typevars may be higher-order + if (targ == WildcardType) Nil else { + // force symbol load for #4205 + targ.typeSymbolDirect.info + // @M must use the typeParams of the *type* targ, not of the *symbol* of targ!! + val tparamsHO = targ.typeParams + if (targ.isHigherKinded || tparam.typeParams.nonEmpty) { + // NOTE: *not* targ.typeSymbol, which normalizes + val kindErrors = checkKindBoundsHK( + tparamsHO, targ.typeSymbolDirect, tparam, + tparam.owner, tparam.typeParams, tparamsHO + ) + if (kindErrors.isEmpty) Nil else { + if (explainErrors) List((targ, tparam, kindErrors)) + // Return as soon as an error is seen if there's nothing to explain. + else return List((NoType, NoSymbol, NoKindErrors)) + } + } + else Nil + } + } + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala new file mode 100644 index 0000000000..e3680b14d5 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -0,0 +1,243 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import Flags._ + +trait Mirrors extends api.Mirrors { + self: SymbolTable => + + override type Mirror >: Null <: RootsBase + + abstract class RootsBase(rootOwner: Symbol) extends MirrorOf[Mirrors.this.type] { thisMirror => + + protected[scala] def rootLoader: LazyType + + val RootClass: ClassSymbol + val RootPackage: ModuleSymbol + val EmptyPackageClass: ClassSymbol + val EmptyPackage: ModuleSymbol + + def findMemberFromRoot(fullName: Name): Symbol = { + val segs = nme.segments(fullName.toString, fullName.isTermName) + if (segs.isEmpty) NoSymbol + else definitions.findNamedMember(segs.tail, RootClass.info member segs.head) + } + + /** Todo: organize similar to mkStatic in reflect.Base */ + private def getModuleOrClass(path: Name, len: Int): Symbol = { + val point = path lastPos('.', len - 1) + val owner = + if (point > 0) getModuleOrClass(path.toTermName, point) + else RootClass + val name = path subName (point + 1, len) + val sym = owner.info member name + val result = if (path.isTermName) sym.suchThat(_ hasFlag MODULE) else sym + if (result != NoSymbol) result + else { + if (settings.debug.value) { log(sym.info); log(sym.info.members) }//debug + mirrorMissingHook(owner, name) orElse symbolTableMissingHook(owner, name) orElse { + MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path+" in "+thisMirror) + } + } + } + + protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol + + protected def symbolTableMissingHook(owner: Symbol, name: Name): Symbol = self.missingHook(owner, name) + + /** If you're looking for a class, pass a type name. + * If a module, a term name. + */ + private def getModuleOrClass(path: Name): Symbol = getModuleOrClass(path, path.length) + + override def staticClass(fullName: String): ClassSymbol = getRequiredClass(fullName) + + // todo: get rid of most creation methods and keep just staticClass/Module/Package + + def getClassByName(fullname: Name): ClassSymbol = { + var result = getModuleOrClass(fullname.toTypeName) + while (result.isAliasType) result = result.info.typeSymbol + result match { + case x: ClassSymbol => x + case _ => MissingRequirementError.notFound("class " + fullname) + } + } + + override def staticModule(fullName: String): ModuleSymbol = getRequiredModule(fullName) + + def getModule(fullname: Name): ModuleSymbol = + // [Eugene++] should be a ClassCastException instead? + getModuleOrClass(fullname.toTermName) match { + case x: ModuleSymbol => x + case _ => MissingRequirementError.notFound("object " + fullname) + } + + def getPackage(fullname: Name): ModuleSymbol = getModule(fullname) + + def getRequiredPackage(fullname: String): ModuleSymbol = + getPackage(newTermNameCached(fullname)) + + @deprecated("Use getClassByName", "2.10.0") + def getClass(fullname: Name): ClassSymbol = getClassByName(fullname) + + def getRequiredClass(fullname: String): ClassSymbol = + getClassByName(newTypeNameCached(fullname)) match { + case x: ClassSymbol => x + case _ => MissingRequirementError.notFound("class " + fullname) + } + + def getRequiredModule(fullname: String): ModuleSymbol = + getModule(newTermNameCached(fullname)) + + def erasureName[T: ClassTag] : String = { + /** We'd like the String representation to be a valid + * scala type, so we have to decode the jvm's secret language. + */ + def erasureString(clazz: Class[_]): String = { + if (clazz.isArray) "Array[" + erasureString(clazz.getComponentType) + "]" + else clazz.getName + } + erasureString(classTag[T].runtimeClass) + } + + def requiredClass[T: ClassTag] : ClassSymbol = + getRequiredClass(erasureName[T]) + + // TODO: What syntax do we think should work here? Say you have an object + // like scala.Predef. You can't say requiredModule[scala.Predef] since there's + // no accompanying Predef class, and if you say requiredModule[scala.Predef.type] + // the name found via the erasure is scala.Predef$. For now I am + // removing the trailing $, but I think that classTag should have + // a method which returns a usable name, one which doesn't expose this + // detail of the backend. + def requiredModule[T: ClassTag] : ModuleSymbol = + getRequiredModule(erasureName[T] stripSuffix "$") + + def getClassIfDefined(fullname: String): Symbol = + getClassIfDefined(newTypeName(fullname)) + + def getClassIfDefined(fullname: Name): Symbol = + wrapMissing(getClassByName(fullname.toTypeName)) + + def getModuleIfDefined(fullname: String): Symbol = + getModuleIfDefined(newTermName(fullname)) + + def getModuleIfDefined(fullname: Name): Symbol = + wrapMissing(getModule(fullname.toTermName)) + + def getPackageObject(fullname: String): ModuleSymbol = + (getModule(newTermName(fullname)).info member nme.PACKAGE) match { + case x: ModuleSymbol => x + case _ => MissingRequirementError.notFound("package object " + fullname) + } + + def getPackageObjectIfDefined(fullname: String): Symbol = { + val module = getModuleIfDefined(newTermName(fullname)) + if (module == NoSymbol) NoSymbol + else { + val packageObject = module.info member nme.PACKAGE + packageObject match { + case x: ModuleSymbol => x + case _ => NoSymbol + } + } + } + + @inline private def wrapMissing(body: => Symbol): Symbol = + try body + catch { case _: MissingRequirementError => NoSymbol } + + /** 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 } + // } + // } + + def init() { + // Still fiddling with whether it's cleaner to do some of this setup here + // or from constructors. The latter approach tends to invite init order issues. + + EmptyPackageClass setInfo ClassInfoType(Nil, newPackageScope(EmptyPackageClass), EmptyPackageClass) + EmptyPackage setInfo EmptyPackageClass.tpe + + connectModuleToClass(EmptyPackage, EmptyPackageClass) + connectModuleToClass(RootPackage, RootClass) + + RootClass.info.decls enter EmptyPackage + RootClass.info.decls enter RootPackage + } + } + + abstract class Roots(rootOwner: Symbol) extends RootsBase(rootOwner) { thisMirror => + + // TODO - having these as objects means they elude the attempt to + // add synchronization in SynchronizedSymbols. But we should either + // flip on object overrides or find some other accomodation, because + // lazy vals are unnecessarily expensive relative to objects and it + // is very beneficial for a handful of bootstrap symbols to have + // first class identities + sealed trait WellKnownSymbol extends Symbol { + this initFlags TopLevelCreationFlags + } + // Features common to RootClass and RootPackage, the roots of all + // type and term symbols respectively. + sealed trait RootSymbol extends WellKnownSymbol { + final override def isRootSymbol = true + override def owner = rootOwner + override def typeOfThis = thisSym.tpe + } + + // This is the package _root_. The actual root cannot be referenced at + // the source level, but _root_ is essentially a function => <root>. + final object RootPackage extends ModuleSymbol(rootOwner, NoPosition, nme.ROOTPKG) with RootSymbol { + this setInfo NullaryMethodType(RootClass.tpe) + RootClass.sourceModule = this + + override def isRootPackage = true + } + // This is <root>, the actual root of everything except the package _root_. + // <root> and _root_ (RootPackage and RootClass) should be the only "well known" + // symbols owned by NoSymbol. All owner chains should go through RootClass, + // although it is probable that some symbols are created as direct children + // of NoSymbol to ensure they will not be stumbled upon. (We should designate + // a better encapsulated place for that.) + final object RootClass extends PackageClassSymbol(rootOwner, NoPosition, tpnme.ROOT) with RootSymbol { + this setInfo rootLoader + + override def isRoot = true + override def isEffectiveRoot = true + override def isStatic = true + override def isNestedClass = false + override def ownerOfNewSymbols = EmptyPackageClass + } + // The empty package, which holds all top level types without given packages. + final object EmptyPackage extends ModuleSymbol(RootClass, NoPosition, nme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + override def isEmptyPackage = true + } + final object EmptyPackageClass extends PackageClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { + override def isEffectiveRoot = true + override def isEmptyPackageClass = true + } + } +} diff --git a/src/reflect/scala/reflect/internal/MissingRequirementError.scala b/src/reflect/scala/reflect/internal/MissingRequirementError.scala new file mode 100644 index 0000000000..fbbbcc1928 --- /dev/null +++ b/src/reflect/scala/reflect/internal/MissingRequirementError.scala @@ -0,0 +1,24 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +class MissingRequirementError private (msg: String) extends FatalError(msg) { + import MissingRequirementError.suffix + def req: String = if (msg endsWith suffix) msg dropRight suffix.length else msg +} + +object MissingRequirementError { + private val suffix = " not found." + def signal(msg: String): Nothing = throw new MissingRequirementError(msg) + def notFound(req: String): Nothing = signal(req + suffix) + def unapply(x: Throwable): Option[String] = x match { + case x: MissingRequirementError => Some(x.req) + case _ => None + } +} + + diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala new file mode 100644 index 0000000000..18671871ae --- /dev/null +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -0,0 +1,527 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.io.Codec +import java.security.MessageDigest +import language.implicitConversions + +/** The class Names ... + * + * @author Martin Odersky + * @version 1.0, 05/02/2005 + */ +trait Names extends api.Names { + implicit def promoteTermNamesAsNecessary(name: Name): TermName = name.toTermName + +// 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 = + newTermName(cs, offset, len, cachedString = null) + + def newTermName(cs: Array[Char]): TermName = newTermName(cs, 0, cs.length) + def newTypeName(cs: Array[Char]): TypeName = newTypeName(cs, 0, cs.length) + + /** Create a term name from the characters in cs[offset..offset+len-1]. + * TODO - have a mode where name validation is performed at creation time + * (e.g. if a name has the string "$class" in it, then fail if that + * string is not at the very end.) + */ + protected def newTermName(cs: Array[Char], offset: Int, len: Int, cachedString: String): 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 ne null) n + else { + // The logic order here is future-proofing against the possibility + // that name.toString will become an eager val, in which case the call + // to enterChars cannot follow the construction of the TermName. + val ncStart = nc + enterChars(cs, offset, len) + if (cachedString ne null) new TermName_S(ncStart, len, h, cachedString) + else new TermName_R(ncStart, len, h) + } + } + protected def newTypeName(cs: Array[Char], offset: Int, len: Int, cachedString: String): TypeName = + newTermName(cs, offset, len, cachedString).toTypeName + + /** Create a term name from string. */ + def newTermName(s: String): TermName = newTermName(s.toCharArray(), 0, s.length(), null) + + /** Create a type name from string. */ + def newTypeName(s: String): TypeName = newTermName(s).toTypeName + + /** 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 = { + val chars = Codec.fromUTF8(bs, offset, len) + newTermName(chars, 0, chars.length) + } + + def newTermNameCached(s: String): TermName = + newTermName(s.toCharArray(), 0, s.length(), cachedString = s) + + def newTypeNameCached(s: String): TypeName = + newTypeName(s.toCharArray(), 0, s.length(), cachedString = s) + + /** 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, cachedString = null).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 nameChars: Array[Char] = chrs + @deprecated("", "2.9.0") def view(s: String): TermName = newTermName(s) + +// Classes ---------------------------------------------------------------------- + + /** The name class. + * TODO - resolve schizophrenia regarding whether to treat Names as Strings + * or Strings as Names. Give names the key functions the absence of which + * make people want Strings all the time. + */ + sealed abstract class Name(protected val index: Int, protected val len: Int) extends NameApi with Function1[Int, Char] { + type ThisNameType >: Null <: Name + protected[this] def thisName: ThisNameType + + /** Index into name table */ + def start: Int = index + + /** The next name in the same hash bucket. */ + def next: ThisNameType + + /** The length of this name. */ + final def length: Int = len + final def isEmpty = length == 0 + final def nonEmpty = !isEmpty + + def nameKind: String + def isTermName: Boolean + def isTypeName: Boolean + def toTermName: TermName + def toTypeName: TypeName + def companionName: Name + def bothNames: List[Name] = List(toTermName, toTypeName) + + /** Return the subname with characters from from to to-1. */ + def subName(from: Int, to: Int): ThisNameType + + /** Return a new name of the same variety. */ + def newName(str: String): ThisNameType + + /** Return a new name based on string transformation. */ + def mapName(f: String => String): ThisNameType = newName(f(toString)) + + /** 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 + } + + /** 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, 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) + + /** Returns 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 + } + + /** Returns 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 + } + + /** Returns 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) + + /** Returns 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 + } + + /** Returns 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 + } + final def containsChar(ch: Char): Boolean = { + var i = index + val max = index + len + while (i < max) { + if (chrs(i) == ch) + return true + i += 1 + } + false + } + + /** 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)) + + def dropRight(n: Int): ThisNameType = subName(0, len - n) + def drop(n: Int): ThisNameType = subName(n, len) + def stripSuffix(suffix: Name): ThisNameType = + if (this endsWith suffix) dropRight(suffix.length) else thisName + + def indexOf(ch: Char) = { + val idx = pos(ch) + if (idx == length) -1 else idx + } + def indexOf(ch: Char, fromIndex: Int) = { + val idx = pos(ch, fromIndex) + if (idx == length) -1 else idx + } + def lastIndexOf(ch: Char) = lastPos(ch) + def lastIndexOf(ch: Char, fromIndex: Int) = lastPos(ch, fromIndex) + + /** 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) + } + + /** TODO - reconcile/fix that encode returns a Name but + * decode returns a String. + */ + + /** !!! Duplicative but consistently named. + */ + def decoded: String = decode + def encoded: String = "" + encode + // def decodedName: ThisNameType = newName(decoded) + def encodedName: ThisNameType = encode + + /** Replace operator symbols by corresponding \$op_name. */ + def encode: ThisNameType = { + val str = toString + val res = NameTransformer.encode(str) + if (res == str) thisName else newName(res) + } + + /** Replace \$op_name by corresponding operator symbol. */ + def decode: String = { + if (this containsChar '$') { + val str = toString + val res = NameTransformer.decode(str) + if (res == str) str + else res + } + else toString + } + + /** TODO - find some efficiency. */ + def append(ch: Char) = newName("" + this + ch) + def append(suffix: String) = newName("" + this + suffix) + def append(suffix: Name) = newName("" + this + suffix) + def prepend(ch: Char) = newName("" + ch + this) + def prepend(prefix: String) = newName("" + prefix + this) + def prepend(prefix: Name) = newName("" + prefix + this) + + def decodedName: ThisNameType = newName(decode) + def isOperatorName: Boolean = decode != toString + def longString: String = nameKind + " " + decode + def debugString = { val s = decode ; if (isTypeName) s + "!" else s } + } + + implicit val NameTag = ClassTag[Name](classOf[Name]) + + /** A name that contains no operator chars nor dollar signs. + * TODO - see if it's any faster to do something along these lines. + * Cute: now that exhaustivity kind of works, the mere presence of + * this trait causes TermName and TypeName to stop being exhaustive. + * Commented out. + */ + // trait AlphaNumName extends Name { + // final override def encode = thisName + // final override def decodedName = thisName + // final override def decode = toString + // final override def isOperatorName = false + // } + + /** TermName_S and TypeName_S have fields containing the string version of the name. + * TermName_R and TypeName_R recreate it each time toString is called. + */ + private class TermName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TermName(index0, len0, hash) { + protected def createCompanionName(h: Int): TypeName = new TypeName_S(index, len, h, toString) + override def newName(str: String): TermName = newTermNameCached(str) + } + private class TypeName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TypeName(index0, len0, hash) { + protected def createCompanionName(h: Int): TermName = new TermName_S(index, len, h, toString) + override def newName(str: String): TypeName = newTypeNameCached(str) + } + + private class TermName_R(index0: Int, len0: Int, hash: Int) extends TermName(index0, len0, hash) { + protected def createCompanionName(h: Int): TypeName = new TypeName_R(index, len, h) + override def toString = new String(chrs, index, len) + } + + private class TypeName_R(index0: Int, len0: Int, hash: Int) extends TypeName(index0, len0, hash) { + protected def createCompanionName(h: Int): TermName = new TermName_R(index, len, h) + override def toString = new String(chrs, index, len) + } + + sealed abstract class TermName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { + type ThisNameType = TermName + protected[this] def thisName: TermName = this + + 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 ne null) n + else createCompanionName(h) + } + def newName(str: String): TermName = newTermName(str) + def companionName: TypeName = toTypeName + def subName(from: Int, to: Int): TermName = + newTermName(chrs, start + from, to - from) + + def nameKind = "term" + protected def createCompanionName(h: Int): TypeName + } + + implicit val TermNameTag = ClassTag[TermName](classOf[TermName]) + + sealed abstract class TypeName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { + type ThisNameType = TypeName + protected[this] def thisName: TypeName = this + + 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 ne null) n + else createCompanionName(h) + } + def toTypeName: TypeName = this + def newName(str: String): TypeName = newTypeName(str) + def companionName: TermName = toTermName + def subName(from: Int, to: Int): TypeName = + newTypeName(chrs, start + from, to - from) + + def nameKind = "type" + override def decode = if (nameDebug) super.decode + "!" else super.decode + protected def createCompanionName(h: Int): TermName + } + + implicit val TypeNameTag = ClassTag[TypeName](classOf[TypeName]) +} diff --git a/src/reflect/scala/reflect/internal/Phase.scala b/src/reflect/scala/reflect/internal/Phase.scala new file mode 100644 index 0000000000..68dc5ce783 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Phase.scala @@ -0,0 +1,66 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +abstract class Phase(val prev: Phase) { + if ((prev ne null) && (prev ne NoPhase)) + prev.nx = this + + type Id = Int + val id: Id = if (prev eq null) 0 else prev.id + 1 + + /** New flags visible after this phase has completed */ + def nextFlags: Long = 0l + + /** New flags visible once this phase has started */ + def newFlags: Long = 0l + + val fmask = ( + if (prev eq null) Flags.InitialFlags + else prev.flagMask | prev.nextFlags | newFlags + ) + def flagMask: Long = fmask + + private var nx: Phase = this + + def next: Phase = nx + def hasNext = next != this + def iterator = Iterator.iterate(this)(_.next) takeWhile (p => p.next != p) + + def name: String + def description: String = name + // Will running with -Ycheck:name work? + def checkable: Boolean = true + def specialized: Boolean = false + def erasedTypes: Boolean = false + def flatClasses: Boolean = false + def refChecked: Boolean = false + + /** This is used only in unsafeTypeParams, and at this writing is + * overridden to false in parser, namer, typer, and erasure. (And NoPhase.) + */ + def keepsTypeParams = true + def run(): Unit + + override def toString() = name + override def hashCode = id.## + name.## + override def equals(other: Any) = other match { + case x: Phase => id == x.id && name == x.name + case _ => false + } +} + +object NoPhase extends Phase(null) { + def name = "<no phase>" + override def keepsTypeParams = false + def run() { throw new Error("NoPhase.run") } +} + +object SomePhase extends Phase(NoPhase) { + def name = "<some phase>" + def run() { throw new Error("SomePhase.run") } +} diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala new file mode 100644 index 0000000000..6ae9b40fcb --- /dev/null +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -0,0 +1,63 @@ +package scala.reflect +package internal + +trait Positions extends api.Positions { self: SymbolTable => + + type Position = scala.reflect.internal.util.Position + val NoPosition = scala.reflect.internal.util.NoPosition + implicit val PositionTag = ClassTag[Position](classOf[Position]) + + /** 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]) {} + + trait PosAssigner extends Traverser { + var pos: Position + } + protected[this] lazy val posAssigner: PosAssigner = new DefaultPosAssigner + + protected class DefaultPosAssigner extends PosAssigner { + var pos: Position = _ + override def traverse(t: Tree) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) { + t.setPos(pos) + super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? + // @PP: it's pruning whenever it encounters a node with a + // position, which I interpret to mean that (in the author's + // mind at least) either the children of a positioned node will + // already be positioned, or the children of a positioned node + // do not merit positioning. + // + // Whatever the author's rationale, it does seem like a bad idea + // to press on through a positioned node to find unpositioned + // children beneath it and then to assign whatever happens to + // be in `pos` to such nodes. There are supposed to be some + // position invariants which I can't imagine surviving that. + } + } + } + + def atPos[T <: Tree](pos: Position)(tree: T): T = { + posAssigner.pos = pos + posAssigner.traverse(tree) + tree + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/Required.scala b/src/reflect/scala/reflect/internal/Required.scala new file mode 100644 index 0000000000..abbe8fbfb7 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Required.scala @@ -0,0 +1,17 @@ +package scala.reflect +package internal + +import settings.MutableSettings + +trait Required { self: SymbolTable => + + type AbstractFileType >: Null <: AbstractFileApi + + def picklerPhase: Phase + + def settings: MutableSettings + + def forInteractive: Boolean + + def forScaladoc: Boolean +} diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala new file mode 100644 index 0000000000..ceacd2afb0 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -0,0 +1,359 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +trait Scopes extends api.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 + } + + object Scope { + def unapplySeq(decls: Scope): Some[Seq[Symbol]] = Some(decls.toList) + } + + /** Note: constructor is protected to force everyone to use the factory methods newScope or newNestedScope instead. + * This is necessary because when run from reflection every scope needs to have a + * SynchronizedScope as mixin. + */ + class Scope protected[Scopes] (initElems: ScopeEntry = null) extends Iterable[Symbol] { + + protected[Scopes] def this(base: Scope) = { + this(base.elems) + nestinglevel = base.nestinglevel + 1 + } + + private[scala] 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() + + /** Returns a new scope with the same content as this one. */ + def cloneScope: Scope = newScopeWith(this.toList: _*) + + /** 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 ... + */ + protected def enter(e: ScopeEntry) { + elemsCache = null + if (hashtable ne null) + enterInHash(e) + else if (size >= MIN_HASH) + createHash() + } + + private def enterInHash(e: ScopeEntry): Unit = { + val i = e.sym.name.start & HASHMASK + e.tail = hashtable(i) + hashtable(i) = e + } + + /** enter a symbol + * + * @param sym ... + */ + def enter[T <: Symbol](sym: T): T = { 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, (sym.fullLocationString, lookup(sym.name).fullLocationString)) + enter(sym) + } + + private def createHash() { + hashtable = new Array[ScopeEntry](HASHSIZE) + enterAllInHash(elems) + } + + private def enterAllInHash(e: ScopeEntry, n: Int = 0) { + if (e ne null) { + if (n < maxRecursions) { + enterAllInHash(e.next, n + 1) + enterInHash(e) + } else { + var entries: List[ScopeEntry] = List() + var ee = e + while (ee ne null) { + entries = ee :: entries + ee = ee.next + } + entries foreach enterInHash + } + } + } + + 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)) newScopeWith(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}") + + } + + implicit val ScopeTag = ClassTag[Scope](classOf[Scope]) + + /** Create a new scope */ + def newScope: Scope = new Scope() + + /** Create a new scope nested in another one with which it shares its elements */ + def newNestedScope(outer: Scope): Scope = new Scope(outer) + + /** Create a new scope with given initial elements */ + def newScopeWith(elems: Symbol*): Scope = { + val scope = newScope + elems foreach scope.enter + scope + } + + /** Create new scope for the members of package `pkg` */ + def newPackageScope(pkgClass: Symbol): Scope = newScope + + /** Transform scope of members of `owner` using operation `op` + * This is overridden by the reflective compiler to avoid creating new scopes for packages + */ + def scopeTransform(owner: Symbol)(op: => Scope): Scope = op + + + /** 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 + + private final val maxRecursions = 1000 + +} + diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala new file mode 100644 index 0000000000..4ea9b27da9 --- /dev/null +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -0,0 +1,12 @@ +package scala.reflect +package internal + +trait StdAttachments { + self: SymbolTable => + + case object BackquotedIdentifierAttachment + + case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) + + case class MacroExpansionAttachment(original: Tree) +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/StdCreators.scala b/src/reflect/scala/reflect/internal/StdCreators.scala new file mode 100644 index 0000000000..3e6b7c1ab4 --- /dev/null +++ b/src/reflect/scala/reflect/internal/StdCreators.scala @@ -0,0 +1,21 @@ +package scala.reflect +package internal + +import scala.reflect.base.{TreeCreator, TypeCreator} +import scala.reflect.base.{Universe => BaseUniverse} + +trait StdCreators { + self: SymbolTable => + + case class FixedMirrorTreeCreator(mirror: MirrorOf[StdCreators.this.type], tree: Tree) extends TreeCreator { + def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Tree = + if (m eq mirror) tree.asInstanceOf[U # Tree] + else throw new IllegalArgumentException(s"Expr defined in $mirror cannot be migrated to other mirrors.") + } + + case class FixedMirrorTypeCreator(mirror: MirrorOf[StdCreators.this.type], tpe: Type) extends TypeCreator { + def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Type = + if (m eq mirror) tpe.asInstanceOf[U # Type] + else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.") + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala new file mode 100644 index 0000000000..6f68b8f63a --- /dev/null +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -0,0 +1,1218 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import java.security.MessageDigest +import Chars.isOperatorPart +import annotation.switch +import language.implicitConversions +import scala.collection.immutable +import scala.io.Codec + +trait StdNames { + self: SymbolTable => + + def encode(str: String): TermName = newTermNameCached(NameTransformer.encode(str)) + + implicit def lowerTermNames(n: TermName): String = n.toString + + /** Tensions: would like the keywords to be the very first names entered into the names + * storage so their ids count from 0, which simplifies the parser. Switched to abstract + * classes to avoid all the indirection which is generated with implementation-containing + * traits. Since all these classes use eager vals, that means the constructor with the + * keywords must run first. If it's the top in the superclass chain, then CommonNames + * must inherit from it, which means TypeNames would inherit keywords as well. + * + * Solution: Keywords extends CommonNames and uses early defs to beat the + * CommonNames constructor out of the starting gate. This is its builder. + */ + private class KeywordSetBuilder { + private var kws: Set[TermName] = Set() + def apply(s: String): TermName = { + val result = newTermNameCached(s) + kws = kws + result + result + } + def result: Set[TermName] = { + val result = kws + kws = null + result + } + } + + private final object compactify extends (String => String) { + val md5 = MessageDigest.getInstance("MD5") + + /** + * 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: + */ + val marker = "$$$$" + val MaxNameLength = math.min( + settings.maxClassfileName.value - 6, + 2 * (settings.maxClassfileName.value - 6 - 2*marker.length - 32) + ) + def toMD5(s: String, edge: Int): String = { + 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 + } + def apply(s: String): String = ( + if (s.length <= MaxNameLength) s + else toMD5(s, MaxNameLength / 4) + ) + } + + abstract class CommonNames extends NamesApi { + type NameType >: Null <: Name + protected implicit def createNameType(name: String): NameType + + def flattenedName(segments: Name*): NameType = + compactify(segments mkString NAME_JOIN_STRING) + + val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING + val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val SINGLETON_SUFFIX: String = ".type" + + val ANON_CLASS_NAME: NameType = "$anon" + val ANON_FUN_NAME: NameType = "$anonfun" + val EMPTY: NameType = "" + val EMPTY_PACKAGE_NAME: NameType = "<empty>" + val IMPL_CLASS_SUFFIX = "$class" + val IMPORT: NameType = "<import>" + val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING + val MODULE_VAR_SUFFIX: NameType = "$module" + val NAME_JOIN_NAME: NameType = NAME_JOIN_STRING + val PACKAGE: NameType = "package" + val ROOT: NameType = "<root>" + val SPECIALIZED_SUFFIX: NameType = "$sp" + + // value types (and AnyRef) are all used as terms as well + // as (at least) arguments to the @specialize annotation. + 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) + + // some types whose companions we utilize + final val AnyRef: NameType = "AnyRef" + final val Array: NameType = "Array" + final val List: NameType = "List" + final val Seq: NameType = "Seq" + final val Symbol: NameType = "Symbol" + final val ClassTag: NameType = "ClassTag" + final val TypeTag : NameType = "TypeTag" + final val ConcreteTypeTag: NameType = "ConcreteTypeTag" + final val Expr: NameType = "Expr" + final val String: NameType = "String" + + // 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 = "_" + } + + /** This should be the first trait in the linearization. */ + // abstract class Keywords extends CommonNames { + abstract class Keywords extends { + private val kw = new KeywordSetBuilder + + 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 MACROkw: TermName = kw("macro") + 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 THENkw: TermName = kw("then") + 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 = kw.result + } with CommonNames { + final val javaKeywords = new JavaKeywords() + } + + abstract class TypeNames extends Keywords with TypeNamesApi { + type NameType = TypeName + protected implicit def createNameType(name: String): TypeName = newTypeNameCached(name) + + 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 REFINE_CLASS_NAME: NameType = "<refinement>" + final val REPEATED_PARAM_CLASS_NAME: NameType = "<repeated>" + final val WILDCARD_STAR: NameType = "_*" + final val REIFY_TREECREATOR_PREFIX: NameType = "$treecreator" + final val REIFY_TYPECREATOR_PREFIX: NameType = "$typecreator" + + final val Any: NameType = "Any" + final val AnyVal: NameType = "AnyVal" + final val ExprApi: NameType = "ExprApi" + final val Mirror: NameType = "Mirror" + final val Nothing: NameType = "Nothing" + final val Null: NameType = "Null" + final val Object: NameType = "Object" + final val PartialFunction: NameType = "PartialFunction" + final val PrefixType: NameType = "PrefixType" + final val Product: NameType = "Product" + final val Serializable: NameType = "Serializable" + final val Singleton: NameType = "Singleton" + final val Throwable: NameType = "Throwable" + + final val Annotation: NameType = "Annotation" + final val ClassfileAnnotation: NameType = "ClassfileAnnotation" + final val Enum: NameType = "Enum" + final val Group: NameType = "Group" + final val Tree: NameType = "Tree" + final val Type : NameType = "Type" + final val TypeTree: NameType = "TypeTree" + + // Annotation simple names, used in Namer + final val BeanPropertyAnnot: NameType = "BeanProperty" + final val BooleanBeanPropertyAnnot: NameType = "BooleanBeanProperty" + final val bridgeAnnot: NameType = "bridge" + + // Classfile Attributes + 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 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" + + def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).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 dropRight IMPL_CLASS_SUFFIX.length).toTypeName + } + + abstract class TermNames extends Keywords with TermNamesApi { + type NameType = TermName + protected implicit def createNameType(name: String): TermName = newTermNameCached(name) + + /** 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 DEFAULT_GETTER_INIT_STRING = "$lessinit$greater" // CONSTRUCTOR.encoded, less is more + val DO_WHILE_PREFIX = "doWhile$" + val EVIDENCE_PARAM_PREFIX = "evidence$" + val EXCEPTION_RESULT_PREFIX = "exceptionResult" + val EXPAND_SEPARATOR_STRING = "$$" + val INTERPRETER_IMPORT_WRAPPER = "$iw" + val INTERPRETER_LINE_PREFIX = "line" + val INTERPRETER_VAR_PREFIX = "res" + val INTERPRETER_WRAPPER_SUFFIX = "$object" + val LOCALDUMMY_PREFIX = "<local " // owner of local blocks + val PROTECTED_PREFIX = "protected$" + val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" + val SUPER_PREFIX_STRING = "super$" + val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" + val WHILE_PREFIX = "while$" + + // Compiler internal names + val ANYNAME: NameType = "<anyname>" + val CONSTRUCTOR: NameType = "<init>" + val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" + val FAKE_LOCAL_THIS: NameType = "this$" + val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? + val LAZY_LOCAL: NameType = "$lzy" + val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" + val LOCAL_SUFFIX_STRING = " " + val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." + val UNIVERSE_BUILD: NameType = "$u.build" + val UNIVERSE_PREFIX: NameType = "$u." + val UNIVERSE_SHORT: NameType = "$u" + val MIRROR_PREFIX: NameType = "$m." + val MIRROR_SHORT: NameType = "$m" + val MIRROR_UNTYPED: NameType = "$m$untyped" + val REIFY_FREE_PREFIX: NameType = "free$" + val REIFY_FREE_THIS_SUFFIX: NameType = "$this" + val REIFY_FREE_VALUE_SUFFIX: NameType = "$value" + val REIFY_SYMDEF_PREFIX: NameType = "symdef$" + val MIXIN_CONSTRUCTOR: NameType = "$init$" + val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" + val OUTER: NameType = "$outer" + val OUTER_LOCAL: NameType = OUTER + LOCAL_SUFFIX_STRING // "$outer ", note the space + val OUTER_SYNTH: NameType = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter + val ROOTPKG: NameType = "_root_" + val SELECTOR_DUMMY: NameType = "<unapply-selector>" + val SELF: NameType = "$this" + val SETTER_SUFFIX: NameType = encode("_=") + val SPECIALIZED_INSTANCE: NameType = "specInstance$" + val STAR: NameType = "*" + val THIS: NameType = "_$this" + + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName + + def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR + def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX + 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 isSuperAccessorName(name: Name) = name startsWith SUPER_PREFIX_STRING + 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 isModuleName(name: Name) = name endsWith MODULE_SUFFIX_NAME + + def isDeprecatedIdentifierName(name: Name) = name.toTermName match { + case nme.`then` | nme.`macro` => true + case _ => false + } + + 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 name of `name` relative to this class `base` with given `separator` + */ + def expandedName(name: TermName, base: Symbol, separator: String = EXPAND_SEPARATOR_STRING): TermName = + newTermNameCached(base.fullName('$') + separator + name) + + /** 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 + } + + def unspecializedName(name: Name): Name = ( + if (name endsWith SPECIALIZED_SUFFIX) + name.subName(0, name.lastIndexOf('m') - 1) + else name + ) + + /* + def anonNumberSuffix(name: Name): Name = { + ("" + name) lastIndexOf '$' match { + case -1 => nme.EMPTY + case idx => + val s = name drop idx + if (s.toString forall (_.isDigit)) s + else nme.EMPTY + } + } + */ + + /** 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 SPECIALIZED_SUFFIX) { + val name1 = name dropRight SPECIALIZED_SUFFIX.length + 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 append LOCAL_SUFFIX_STRING + def getterToSetter(name: TermName): TermName = name append SETTER_SUFFIX + def localToGetter(name: TermName): TermName = name dropRight LOCAL_SUFFIX_STRING.length + + def dropLocalSuffix(name: Name): Name = if (name endsWith ' ') name dropRight 1 else name + + def setterToGetter(name: TermName): TermName = { + val p = name.pos(TRAIT_SETTER_SEPARATOR_STRING) + if (p < name.length) + setterToGetter(name drop (p + TRAIT_SETTER_SEPARATOR_STRING.length)) + else + name.subName(0, name.length - SETTER_SUFFIX.length) + } + + // Nominally, name$default$N, encoded for <init> + def defaultGetterName(name: Name, pos: Int): TermName = { + val prefix = if (isConstructorName(name)) DEFAULT_GETTER_INIT_STRING else name + newTermName(prefix + DEFAULT_GETTER_STRING + pos) + } + // Nominally, name from name$default$N, CONSTRUCTOR for <init> + def defaultGetterToMethod(name: Name): TermName = { + val p = name.pos(DEFAULT_GETTER_STRING) + if (p < name.length) { + val q = name.toTermName.subName(0, p) + // i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q + if (q.toString == DEFAULT_GETTER_INIT_STRING) CONSTRUCTOR else q + } else name.toTermName + } + + // If the name ends with $nn where nn are + // all digits, strip the $ and the digits. + // Otherwise return the argument. + def stripAnonNumberSuffix(name: Name): Name = { + var pos = name.length + while (pos > 0 && name(pos - 1).isDigit) + pos -= 1 + + if (pos <= 0 || pos == name.length || name(pos - 1) != '$') name + else name.subName(0, pos - 1) + } + + def stripModuleSuffix(name: Name): Name = ( + if (isModuleName(name)) name dropRight MODULE_SUFFIX_STRING.length else name + ) + def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">") + 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) + + final val Nil: NameType = "Nil" + final val Predef: NameType = "Predef" + final val ScalaRunTime: NameType = "ScalaRunTime" + final val Some: NameType = "Some" + + val _1 : NameType = "_1" + val _2 : NameType = "_2" + val _3 : NameType = "_3" + val _4 : NameType = "_4" + val _5 : NameType = "_5" + val _6 : NameType = "_6" + val _7 : NameType = "_7" + val _8 : NameType = "_8" + val _9 : NameType = "_9" + val _10 : NameType = "_10" + val _11 : NameType = "_11" + val _12 : NameType = "_12" + val _13 : NameType = "_13" + val _14 : NameType = "_14" + val _15 : NameType = "_15" + val _16 : NameType = "_16" + val _17 : NameType = "_17" + val _18 : NameType = "_18" + val _19 : NameType = "_19" + val _20 : NameType = "_20" + val _21 : NameType = "_21" + val _22 : NameType = "_22" + + val x_0 : NameType = "x$0" + val x_1 : NameType = "x$1" + val x_2 : NameType = "x$2" + val x_3 : NameType = "x$3" + val x_4 : NameType = "x$4" + val x_5 : NameType = "x$5" + val x_6 : NameType = "x$6" + val x_7 : NameType = "x$7" + val x_8 : NameType = "x$8" + val x_9 : NameType = "x$9" + + @switch def syntheticParamName(i: Int): TermName = i match { + case 0 => nme.x_0 + case 1 => nme.x_1 + case 2 => nme.x_2 + case 3 => nme.x_3 + case 4 => nme.x_4 + case 5 => nme.x_5 + case 6 => nme.x_6 + case 7 => nme.x_7 + case 8 => nme.x_8 + case 9 => nme.x_9 + case _ => newTermName("x$" + i) + } + + @switch def productAccessorName(j: Int): TermName = j match { + case 1 => nme._1 + case 2 => nme._2 + case 3 => nme._3 + case 4 => nme._4 + case 5 => nme._5 + case 6 => nme._6 + case 7 => nme._7 + case 8 => nme._8 + case 9 => nme._9 + case 10 => nme._10 + case 11 => nme._11 + case 12 => nme._12 + case 13 => nme._13 + case 14 => nme._14 + case 15 => nme._15 + case 16 => nme._16 + case 17 => nme._17 + case 18 => nme._18 + case 19 => nme._19 + case 20 => nme._20 + case 21 => nme._21 + case 22 => nme._22 + case _ => newTermName("_" + j) + } + + val ??? = encode("???") + + val wrapRefArray: NameType = "wrapRefArray" + val wrapByteArray: NameType = "wrapByteArray" + val wrapShortArray: NameType = "wrapShortArray" + val wrapCharArray: NameType = "wrapCharArray" + val wrapIntArray: NameType = "wrapIntArray" + val wrapLongArray: NameType = "wrapLongArray" + val wrapFloatArray: NameType = "wrapFloatArray" + val wrapDoubleArray: NameType = "wrapDoubleArray" + val wrapBooleanArray: NameType = "wrapBooleanArray" + val wrapUnitArray: NameType = "wrapUnitArray" + val genericWrapArray: NameType = "genericWrapArray" + + // Compiler utilized names + + val AnnotatedType: NameType = "AnnotatedType" + val AnnotationInfo: NameType = "AnnotationInfo" + val Any: NameType = "Any" + val AnyVal: NameType = "AnyVal" + val AppliedTypeTree: NameType = "AppliedTypeTree" + val Apply: NameType = "Apply" + val ArrayAnnotArg: NameType = "ArrayAnnotArg" + val Constant: NameType = "Constant" + val ConstantType: NameType = "ConstantType" + val EmptyPackage: NameType = "EmptyPackage" + val EmptyPackageClass: NameType = "EmptyPackageClass" + val ExistentialTypeTree: NameType = "ExistentialTypeTree" + val Flag : NameType = "Flag" + val Ident: NameType = "Ident" + val Import: NameType = "Import" + val Literal: NameType = "Literal" + val LiteralAnnotArg: NameType = "LiteralAnnotArg" + val Modifiers: NameType = "Modifiers" + val NestedAnnotArg: NameType = "NestedAnnotArg" + val NoFlags: NameType = "NoFlags" + val NoPrefix: NameType = "NoPrefix" + val NoSymbol: NameType = "NoSymbol" + val Nothing: NameType = "Nothing" + val NoType: NameType = "NoType" + val Null: NameType = "Null" + val Object: NameType = "Object" + val RootPackage: NameType = "RootPackage" + val RootClass: NameType = "RootClass" + val Select: NameType = "Select" + val StringContext: NameType = "StringContext" + val This: NameType = "This" + val ThisType: NameType = "ThisType" + val Tree : NameType = "Tree" + val Tuple2: NameType = "Tuple2" + val TYPE_ : NameType = "TYPE" + val TypeApply: NameType = "TypeApply" + val TypeRef: NameType = "TypeRef" + val TypeTree: NameType = "TypeTree" + val UNIT : NameType = "UNIT" + val add_ : NameType = "add" + val annotation: NameType = "annotation" + val anyValClass: NameType = "anyValClass" + val append: NameType = "append" + val apply: NameType = "apply" + val applyDynamic: NameType = "applyDynamic" + val applyDynamicNamed: NameType = "applyDynamicNamed" + val applyImpl: NameType = "applyImpl" + val applyOrElse: NameType = "applyOrElse" + val args : NameType = "args" + val argv : NameType = "argv" + val arrayClass: NameType = "arrayClass" + val arrayElementClass: NameType = "arrayElementClass" + val arrayTagToClassManifest: NameType = "arrayTagToClassManifest" + val arrayValue: NameType = "arrayValue" + val array_apply : NameType = "array_apply" + val array_clone : NameType = "array_clone" + val array_length : NameType = "array_length" + val array_update : NameType = "array_update" + val arraycopy: NameType = "arraycopy" + val asTermSymbol: NameType = "asTermSymbol" + val asModuleSymbol: NameType = "asModuleSymbol" + val asMethodSymbol: NameType = "asMethodSymbol" + val asTypeSymbol: NameType = "asTypeSymbol" + val asClassSymbol: NameType = "asClassSymbol" + val asInstanceOf_ : NameType = "asInstanceOf" + val asInstanceOf_Ob : NameType = "$asInstanceOf" + val asTypeConstructor: NameType = "asTypeConstructor" + val assert_ : NameType = "assert" + val assume_ : NameType = "assume" + val basis : NameType = "basis" + val box: NameType = "box" + val build : NameType = "build" + 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 concreteTypeTagToManifest: NameType = "concreteTypeTagToManifest" + val conforms: NameType = "conforms" + val copy: NameType = "copy" + val currentMirror: NameType = "currentMirror" + val definitions: NameType = "definitions" + val delayedInit: NameType = "delayedInit" + val delayedInitArg: NameType = "delayedInit$body" + val drop: NameType = "drop" + val elem: NameType = "elem" + val emptyValDef: NameType = "emptyValDef" + val ensureAccessible : NameType = "ensureAccessible" + val eq: NameType = "eq" + val equalsNumChar : NameType = "equalsNumChar" + val equalsNumNum : NameType = "equalsNumNum" + val equalsNumObject : NameType = "equalsNumObject" + val equals_ : NameType = if (forMSIL) "Equals" else "equals" + val error: NameType = "error" + val eval: NameType = "eval" + val ex: NameType = "ex" + val experimental: NameType = "experimental" + val false_ : NameType = "false" + val filter: NameType = "filter" + val finalize_ : NameType = if (forMSIL) "Finalize" else "finalize" + val find_ : NameType = "find" + val flagsFromBits : NameType = "flagsFromBits" + val flatMap: NameType = "flatMap" + val foreach: NameType = "foreach" + val genericArrayOps: NameType = "genericArrayOps" + val get: NameType = "get" + val getOrElse: NameType = "getOrElse" + 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 implicitly: NameType = "implicitly" + val in: NameType = "in" + val info: NameType = "info" + val inlinedEquals: NameType = "inlinedEquals" + val isArray: NameType = "isArray" + val isDefinedAt: NameType = "isDefinedAt" + val isEmpty: NameType = "isEmpty" + val isInstanceOf_ : NameType = "isInstanceOf" + val isInstanceOf_Ob : NameType = "$isInstanceOf" + val java: NameType = "java" + val key: NameType = "key" + val lang: NameType = "lang" + val length: NameType = "length" + val lengthCompare: NameType = "lengthCompare" + val liftedTree: NameType = "liftedTree" + val `macro` : NameType = "macro" + val macroThis : NameType = "_this" + val macroContext : NameType = "c" + val main: NameType = "main" + val manifest: NameType = "manifest" + val manifestToConcreteTypeTag: NameType = "manifestToConcreteTypeTag" + val map: NameType = "map" + val materializeArrayTag: NameType = "materializeArrayTag" + val materializeClassTag: NameType = "materializeClassTag" + val materializeConcreteTypeTag: NameType = "materializeConcreteTypeTag" + val materializeTypeTag: NameType = "materializeTypeTag" + val mirror : NameType = "mirror" + val moduleClass : NameType = "moduleClass" + val name: NameType = "name" + val ne: NameType = "ne" + val newArray: NameType = "newArray" + val newFreeExistential: NameType = "newFreeExistential" + val newFreeTerm: NameType = "newFreeTerm" + val newFreeType: NameType = "newFreeType" + val newNestedSymbol: NameType = "newNestedSymbol" + val newScopeWith: NameType = "newScopeWith" + val next: NameType = "next" + val nmeNewTermName: NameType = "newTermName" + val nmeNewTypeName: NameType = "newTypeName" + val normalize: NameType = "normalize" + val notifyAll_ : NameType = "notifyAll" + val notify_ : NameType = "notify" + val null_ : NameType = "null" + val ofDim: NameType = "ofDim" + val origin: NameType = "origin" + val prefix : NameType = "prefix" + val productArity: NameType = "productArity" + val productElement: NameType = "productElement" + val productIterator: NameType = "productIterator" + val productPrefix: NameType = "productPrefix" + val readResolve: NameType = "readResolve" + val reflect : NameType = "reflect" + val reify : NameType = "reify" + val rootMirror : NameType = "rootMirror" + val runOrElse: NameType = "runOrElse" + val runtime: NameType = "runtime" + val runtimeClass: NameType = "runtimeClass" + val runtimeMirror: NameType = "runtimeMirror" + val sameElements: NameType = "sameElements" + val scala_ : NameType = "scala" + val selectDynamic: NameType = "selectDynamic" + val selectOverloadedMethod: NameType = "selectOverloadedMethod" + val selectTerm: NameType = "selectTerm" + val selectType: NameType = "selectType" + val self: NameType = "self" + val setAccessible: NameType = "setAccessible" + val setAnnotations: NameType = "setAnnotations" + val setSymbol: NameType = "setSymbol" + val setType: NameType = "setType" + val setTypeSignature: NameType = "setTypeSignature" + val splice: NameType = "splice" + val staticClass : NameType = "staticClass" + val staticModule : NameType = "staticModule" + val synchronized_ : NameType = "synchronized" + val tail: NameType = "tail" + val `then` : NameType = "then" + val this_ : NameType = "this" + val thisPrefix : NameType = "thisPrefix" + val throw_ : NameType = "throw" + val toArray: NameType = "toArray" + val toList: NameType = "toList" + val toObjectArray : NameType = "toObjectArray" + val toSeq: NameType = "toSeq" + val toString_ : NameType = if (forMSIL) "ToString" else "toString" + val tpe : NameType = "tpe" + val tree : NameType = "tree" + val true_ : NameType = "true" + val typedProductIterator: NameType = "typedProductIterator" + val unapply: NameType = "unapply" + val unapplySeq: NameType = "unapplySeq" + val unbox: NameType = "unbox" + val universe: NameType = "universe" + val update: NameType = "update" + val updateDynamic: NameType = "updateDynamic" + val value: NameType = "value" + val valueOf : NameType = "valueOf" + val values : NameType = "values" + val view_ : NameType = "view" + val wait_ : NameType = "wait" + val withFilter: NameType = "withFilter" + val wrap: NameType = "wrap" + val zip: NameType = "zip" + + val synthSwitch: NameType = "$synthSwitch" + + // 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" + + // primitive operation methods for structual types mostly + // overlap with the above, but not for these two. + val toCharacter: NameType = "toCharacter" + val toInteger: NameType = "toInteger" + + def newLazyValSlowComputeName(lzyValName: Name) = lzyValName append LAZY_SLOW_SUFFIX + + // ASCII names for operators + 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 = ADD // technically redundant, but ADD looks funny with MINUS + val SUB = MINUS // ... as does SUB with PLUS + 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_!") + + // Grouped here so Cleanup knows what tests to perform. + val CommonOpNames = Set[Name](OR, XOR, AND, EQ, NE) + val ConversionNames = Set[Name](toByte, toChar, toDouble, toFloat, toInt, toLong, toShort) + val BooleanOpNames = Set[Name](ZOR, ZAND, UNARY_!) ++ CommonOpNames + val NumberOpNames = ( + Set[Name](ADD, SUB, MUL, DIV, MOD, LSL, LSR, ASR, LT, LE, GE, GT) + ++ Set(UNARY_+, UNARY_-, UNARY_!) + ++ ConversionNames + ++ CommonOpNames + ) + + val add: NameType = "add" + val complement: NameType = "complement" + val divide: NameType = "divide" + val multiply: NameType = "multiply" + val negate: NameType = "negate" + val positive: NameType = "positive" + val shiftLogicalRight: NameType = "shiftLogicalRight" + val shiftSignedLeft: NameType = "shiftSignedLeft" + val shiftSignedRight: NameType = "shiftSignedRight" + val subtract: NameType = "subtract" + val takeAnd: NameType = "takeAnd" + val takeConditionalAnd: NameType = "takeConditionalAnd" + val takeConditionalOr: NameType = "takeConditionalOr" + val takeModulo: NameType = "takeModulo" + val takeNot: NameType = "takeNot" + val takeOr: NameType = "takeOr" + val takeXor: NameType = "takeXor" + val testEqual: NameType = "testEqual" + val testGreaterOrEqualThan: NameType = "testGreaterOrEqualThan" + val testGreaterThan: NameType = "testGreaterThan" + val testLessOrEqualThan: NameType = "testLessOrEqualThan" + val testLessThan: NameType = "testLessThan" + val testNotEqual: NameType = "testNotEqual" + + def toUnaryName(name: TermName): TermName = name match { + case raw.MINUS => UNARY_- + case raw.PLUS => UNARY_+ + case raw.TILDE => UNARY_~ + case raw.BANG => UNARY_! + case _ => name + } + /** The name of a method which stands in for a primitive operation + * during structural type dispatch. + */ + def primitiveInfixMethodName(name: Name): TermName = name match { + case OR => takeOr + case XOR => takeXor + case AND => takeAnd + case EQ => testEqual + case NE => testNotEqual + case ADD => add + case SUB => subtract + case MUL => multiply + case DIV => divide + case MOD => takeModulo + case LSL => shiftSignedLeft + case LSR => shiftLogicalRight + case ASR => shiftSignedRight + case LT => testLessThan + case LE => testLessOrEqualThan + case GE => testGreaterOrEqualThan + case GT => testGreaterThan + case ZOR => takeConditionalOr + case ZAND => takeConditionalAnd + case _ => NO_NAME + } + /** Postfix/prefix, really. + */ + def primitivePostfixMethodName(name: Name): TermName = name match { + case UNARY_! => takeNot + case UNARY_+ => positive + case UNARY_- => negate + case UNARY_~ => complement + case `toByte` => toByte + case `toShort` => toShort + case `toChar` => toCharacter + case `toInt` => toInteger + case `toLong` => toLong + case `toFloat` => toFloat + case `toDouble` => toDouble + case _ => NO_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, newTermName(name) drop (idx + 1)) + mkName(simple, div == '.') :: segments(rest, assumeTerm) + } + } + + def newBitmapName(bitmapPrefix: Name, n: Int) = bitmapPrefix append ("" + n) + + val BITMAP_NORMAL: NameType = BITMAP_PREFIX + "" // initialization bitmap for public/protected lazy vals + val BITMAP_TRANSIENT: NameType = BITMAP_PREFIX + "trans$" // initialization bitmap for transient lazy vals + val BITMAP_CHECKINIT: NameType = BITMAP_PREFIX + "init$" // initialization bitmap for checkinit values + val BITMAP_CHECKINIT_TRANSIENT: NameType = BITMAP_PREFIX + "inittrans$" // initialization bitmap for transient checkinit values + } + + object tpnme extends TypeNames { } + + /** For fully qualified type names. + */ + object fulltpnme extends TypeNames { + val RuntimeNothing: NameType = "scala.runtime.Nothing$" + val RuntimeNull: NameType = "scala.runtime.Null$" + val JavaLangEnum: NameType = "java.lang.Enum" + } + + /** Java binary names, like scala/runtime/Nothing$. + */ + object binarynme { + def toBinary(name: Name) = name mapName (_.replace('.', '/')) + + val RuntimeNothing = toBinary(fulltpnme.RuntimeNothing).toTypeName + val RuntimeNull = toBinary(fulltpnme.RuntimeNull).toTypeName + } + + val javanme = nme.javaKeywords + + // [Eugene++ to Martin] had to move a lot of stuff from here to TermNames to satisfy the contract + // why do we even have stuff in object nme? cf. object tpnme + object nme extends TermNames { + + def isModuleVarName(name: Name): Boolean = + stripAnonNumberSuffix(name) endsWith MODULE_VAR_SUFFIX + + def moduleVarName(name: TermName): TermName = + newTermNameCached("" + name + MODULE_VAR_SUFFIX) + + def getCause = sn.GetCause + def getClass_ = sn.GetClass + def getComponentType = sn.GetComponentType + def getMethod_ = sn.GetMethod + def invoke_ = sn.Invoke + + val isBoxedNumberOrBoolean: NameType = "isBoxedNumberOrBoolean" + val isBoxedNumber: NameType = "isBoxedNumber" + + val reflPolyCacheName: NameType = "reflPoly$Cache" + val reflClassCacheName: NameType = "reflClass$Cache" + val reflParamsCacheName: NameType = "reflParams$Cache" + val reflMethodCacheName: NameType = "reflMethod$Cache" + val reflMethodName: NameType = "reflMethod$Method" + + private val reflectionCacheNames = Set[NameType]( + reflPolyCacheName, + reflClassCacheName, + reflParamsCacheName, + reflMethodCacheName, + reflMethodName + ) + def isReflectionCacheName(name: Name) = reflectionCacheNames exists (name startsWith _) + + @deprecated("Use a method in tpnme", "2.10.0") def dropSingletonName(name: Name): TypeName = tpnme.dropSingletonName(name) + @deprecated("Use a method in tpnme", "2.10.0") def singletonName(name: Name): TypeName = tpnme.singletonName(name) + @deprecated("Use a method in tpnme", "2.10.0") def implClassName(name: Name): TypeName = tpnme.implClassName(name) + @deprecated("Use a method in tpnme", "2.10.0") def interfaceName(implname: Name): TypeName = tpnme.interfaceName(implname) + } + + abstract class SymbolNames { + protected implicit def createNameType(s: String): TypeName = newTypeNameCached(s) + + val BeanProperty : TypeName + val BooleanBeanProperty : TypeName + val BoxedBoolean : TypeName + val BoxedCharacter : TypeName + val BoxedNumber : TypeName + val Class : 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 GetClassLoader : TermName + val GetComponentType : TermName + val GetMethod : TermName + val Invoke : TermName + val JavaLang : TermName + + val Boxed: immutable.Map[TypeName, TypeName] + } + + class JavaKeywords { + private val kw = new KeywordSetBuilder + + 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 = kw.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 = newTermName("forName") + final val GetCause: TermName = newTermName("getCause") + final val GetClass: TermName = newTermName("getClass") + final val GetClassLoader: TermName = newTermName("getClassLoader") + final val GetComponentType: TermName = newTermName("getComponentType") + final val GetMethod: TermName = newTermName("getMethod") + final val Invoke: TermName = newTermName("invoke") + final val JavaLang: TermName = newTermName("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 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 = newTermName("GetType") + final val GetCause: TermName = newTermName("InnerException") /* System.Reflection.TargetInvocationException.InnerException */ + final val GetClass: TermName = newTermName("GetType") + final lazy val GetClassLoader: TermName = throw new UnsupportedOperationException("Scala reflection is not supported on this platform"); + final val GetComponentType: TermName = newTermName("GetElementType") + final val GetMethod: TermName = newTermName("GetMethod") + final val Invoke: TermName = newTermName("Invoke") + final val JavaLang: TermName = newTermName("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.beans.BeanProperty" + final val BooleanBeanProperty: TypeName = "scala.beans.BooleanBeanProperty" + final val JavaSerializable: TypeName = "java.io.Serializable" + } + + lazy val sn: SymbolNames = + if (forMSIL) new MSILNames + else new J2SENames +} diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala new file mode 100644 index 0000000000..cadd76b1ba --- /dev/null +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -0,0 +1,332 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } +import util._ + +abstract class SymbolTable extends makro.Universe + with Collections + with Names + with Symbols + with Types + with Kinds + with ExistentialsAndSkolems + with FlagSets + with Scopes + with Mirrors + with Definitions + with Constants + with BaseTypeSeqs + with InfoTransformers + with transform.Transforms + with StdNames + with AnnotationInfos + with AnnotationCheckers + with Trees + with TreePrinters + with Positions + with TypeDebugging + with Importers + with Required + with CapturedVariables + with StdAttachments + with StdCreators + with BuildUtils +{ + + val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } + val treeBuild = gen + + def log(msg: => AnyRef): Unit + def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) + + @deprecated("Give us a reason", "2.10.0") + def abort(): Nothing = abort("unknown error") + + /** Override with final implementation for inlining. */ + def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg) + def debugwarn(msg: => String): Unit = if (settings.debug.value) Console.err.println(msg) + def throwableAsString(t: Throwable): String = "" + t + + /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */ + def debugStack(t: Throwable): Unit = debugwarn(throwableAsString(t)) + + /** Overridden when we know more about what was happening during a failure. */ + def supplementErrorMessage(msg: String): String = msg + + private[scala] def printCaller[T](msg: String)(result: T) = { + Console.err.println("%s: %s\nCalled from: %s".format(msg, result, + (new Throwable).getStackTrace.drop(2).take(15).mkString("\n"))) + + result + } + + private[scala] def printResult[T](msg: String)(result: T) = { + Console.err.println(msg + ": " + result) + result + } + private[scala] def logResult[T](msg: String)(result: T): T = { + log(msg + ": " + result) + result + } + private[scala] def logResultIf[T](msg: String, cond: T => Boolean)(result: T): T = { + if (cond(result)) + log(msg + ": " + result) + + result + } + + // For too long have we suffered in order to sort NAMES. + // I'm pretty sure there's a reasonable default for that. + // Notice challenge created by Ordering's invariance. + implicit def lowPriorityNameOrdering[T <: Names#Name]: Ordering[T] = + SimpleNameOrdering.asInstanceOf[Ordering[T]] + + private object SimpleNameOrdering extends Ordering[Names#Name] { + def compare(n1: Names#Name, n2: Names#Name) = ( + if (n1 eq n2) 0 + else n1.toString compareTo n2.toString + ) + } + + /** Dump each symbol to stdout after shutdown. + */ + final val traceSymbolActivity = sys.props contains "scalac.debug.syms" + object traceSymbols extends { + val global: SymbolTable.this.type = SymbolTable.this + } with util.TraceSymbolActivity + + /** Are we compiling for Java SE? */ + // def forJVM: Boolean + + /** Are we compiling for .NET? */ + def forMSIL: Boolean = false + + /** A last effort if symbol in a select <owner>.<name> is not found. + * This is overridden by the reflection compiler to make up a package + * when it makes sense (i.e. <owner> is a package and <name> is a term name). + */ + def missingHook(owner: Symbol, name: Name): Symbol = NoSymbol + + /** Returns the mirror that loaded given symbol */ + def mirrorThatLoaded(sym: Symbol): Mirror + + /** 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 + + // sigh, this has to be public or atPhase doesn't inline. + var phStack: List[Phase] = Nil + private var ph: Phase = NoPhase + private var per = NoPeriod + + final def atPhaseStack: List[Phase] = phStack + final def phase: Phase = ph + + def atPhaseStackMessage = atPhaseStack match { + case Nil => "" + case ps => ps.reverseMap("->" + _).mkString("(", " ", ")") + } + + final def phase_=(p: Phase) { + //System.out.println("setting phase to " + p) + assert((p ne null) && p != NoPhase, p) + ph = p + per = period(currentRunId, p.id) + } + final def pushPhase(ph: Phase): Phase = { + val current = phase + phase = ph + phStack ::= ph + current + } + final def popPhase(ph: Phase) { + phStack = phStack.tail + phase = ph + } + + /** 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 = + (rid << 8) + pid + + /** Are we later than given phase in compilation? */ + final def isAtPhaseAfter(p: Phase) = + p != NoPhase && phase.id > p.id + + /** Perform given operation at given phase. */ + @inline final def atPhase[T](ph: Phase)(op: => T): T = { + val saved = pushPhase(ph) + try op + finally popPhase(saved) + } + + + /** Since when it is to be "at" a phase is inherently ambiguous, + * a couple unambiguously named methods. + */ + @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) + @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) + @inline final def afterCurrentPhase[T](op: => T): T = atPhase(phase.next)(op) + @inline final def beforePrevPhase[T](op: => T): T = atPhase(phase.prev)(op) + + @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isAtPhaseAfter(target)) atPhase(target)(op) else op + + 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) + } + } + + def openPackageModule(container: Symbol, dest: Symbol) { + // unlink existing symbols in the package + for (member <- container.info.decls.iterator) { + if (!member.isPrivate && !member.isConstructor) { + // todo: handle overlapping definitions in some way: mark as errors + // or treat as abstractions. For now the symbol in the package module takes precedence. + for (existing <- dest.info.decl(member.name).alternatives) + dest.info.decls.unlink(existing) + } + } + // enter non-private decls the class + for (member <- container.info.decls.iterator) { + if (!member.isPrivate && !member.isConstructor) { + dest.info.decls.enter(member) + } + } + // enter decls of parent classes + for (p <- container.parentSymbols) { + if (p != definitions.ObjectClass) { + openPackageModule(p, dest) + } + } + } + + /** Convert array parameters denoting a repeated parameter of a Java method + * to `JavaRepeatedParamClass` types. + */ + def arrayToRepeated(tp: Type): Type = tp match { + case MethodType(params, rtpe) => + val formals = tp.paramTypes + assert(formals.last.typeSymbol == definitions.ArrayClass, formals) + val method = params.last.owner + val elemtp = formals.last.typeArgs.head match { + case RefinedType(List(t1, t2), _) if (t1.typeSymbol.isAbstractType && t2.typeSymbol == definitions.ObjectClass) => + t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. + case t => + t + } + val newParams = method.newSyntheticValueParams(formals.init :+ definitions.javaRepeatedType(elemtp)) + MethodType(newParams, rtpe) + case PolyType(tparams, rtpe) => + PolyType(tparams, arrayToRepeated(rtpe)) + } + + abstract class SymLoader extends LazyType { + def fromSource = false + } + + /** if there's a `package` member object in `pkgClass`, enter its members into it. */ + def openPackageModule(pkgClass: Symbol) { + + val pkgModule = pkgClass.info.decl(nme.PACKAGEkw) + def fromSource = pkgModule.rawInfo match { + case ltp: SymLoader => ltp.fromSource + case _ => false + } + if (pkgModule.isModule && !fromSource) { + // println("open "+pkgModule)//DEBUG + openPackageModule(pkgModule, pkgClass) + } + } + + object perRunCaches { + import java.lang.ref.WeakReference + import scala.runtime.ScalaRunTime.stringOf + import scala.collection.generic.Clearable + + // Weak references so the garbage collector will take care of + // letting us know when a cache is really out of commission. + private val caches = mutable.HashSet[WeakReference[Clearable]]() + + def recordCache[T <: Clearable](cache: T): T = { + caches += new WeakReference(cache) + cache + } + + def clearAll() = { + debuglog("Clearing " + caches.size + " caches.") + caches foreach { ref => + val cache = ref.get() + if (cache == null) + caches -= ref + else + cache.clear() + } + } + + def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]()) + def newMap[K, V]() = recordCache(mutable.HashMap[K, V]()) + def newSet[K]() = recordCache(mutable.HashSet[K]()) + def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]()) + } + + /** 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] + + /** Is this symbol table a part of a compiler universe? + */ + def isCompilerUniverse = false +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala new file mode 100644 index 0000000000..86693cf880 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -0,0 +1,3190 @@ + /* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import util.Statistics._ +import Flags._ + +trait Symbols extends api.Symbols { self: SymbolTable => + import definitions._ + + protected var ids = 0 + + val emptySymbolArray = new Array[Symbol](0) + + def symbolCount = ids // statistics + + protected def nextId() = { ids += 1; ids } + + /** 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 + protected def freshExistentialName(suffix: String) = { + nextexid += 1 + newTypeName("_" + nextexid + suffix) + } + + // Set the fields which point companions at one another. Returns the module. + def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { + moduleClass.sourceModule = m + m setModuleClass moduleClass + m + } + + /** Create a new free term. Its owner is NoSymbol. + */ + def newFreeTermSymbol(name: TermName, info: Type, value: => Any, flags: Long = 0L, origin: String): FreeTermSymbol = + new FreeTermSymbol(name, value, origin) initFlags flags setInfo info + + /** Create a new free type. Its owner is NoSymbol. + */ + def newFreeTypeSymbol(name: TypeName, info: Type, value: => Any, flags: Long = 0L, origin: String): FreeTypeSymbol = + new FreeTypeSymbol(name, value, origin) initFlags flags setInfo info + + /** The original owner of a class. Used by the backend to generate + * EnclosingMethod attributes. + */ + val originalOwner = perRunCaches.newMap[Symbol, Symbol]() + + abstract class SymbolContextApiImpl extends SymbolContextApi { + this: Symbol => + + def kind: String = kindString + def isExistential: Boolean = this.isExistentiallyBound + + def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { + case n: TermName => newTermSymbol(n, pos, newFlags) + case n: TypeName => if (isClass) newClassSymbol(n, pos, newFlags) else newNonClassSymbol(n, pos, newFlags) + } + + def thisPrefix: Type = thisType + def selfType: Type = typeOfThis + def typeSignature: Type = info + def typeSignatureIn(site: Type): Type = site memberInfo this + + def asType: Type = tpe + def asTypeIn(site: Type): Type = site.memberType(this) + def asTypeConstructor: Type = typeConstructor + def setInternalFlags(flag: Long): this.type = { setFlag(flag); this } + def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this } + def getAnnotations: List[AnnotationInfo] = { initialize; annotations } + def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } + + private def lastElemType(ts: Seq[Type]): Type = ts.last.normalize.typeArgs.head + + private def formalTypes(formals: List[Type], nargs: Int): List[Type] = { + val formals1 = formals mapConserve { + case TypeRef(_, ByNameParamClass, List(arg)) => arg + case formal => formal + } + if (isVarArgTypes(formals1)) { + val ft = lastElemType(formals) + formals1.init ::: List.fill(nargs - (formals1.length - 1))(ft) + } else formals1 + } + + def resolveOverloaded(pre: Type, targs: Seq[Type], actuals: Seq[Type]): Symbol = { + def firstParams(tpe: Type): (List[Symbol], List[Type]) = tpe match { + case PolyType(tparams, restpe) => + val (Nil, formals) = firstParams(restpe) + (tparams, formals) + case MethodType(params, _) => + (Nil, params map (_.tpe)) + case _ => + (Nil, Nil) + } + def isApplicable(alt: Symbol, targs: List[Type], actuals: Seq[Type]) = { + def isApplicableType(tparams: List[Symbol], tpe: Type): Boolean = { + val (tparams, formals) = firstParams(pre memberType alt) + val formals1 = formalTypes(formals, actuals.length) + val actuals1 = + if (isVarArgTypes(actuals)) { + if (!isVarArgTypes(formals)) return false + actuals.init :+ lastElemType(actuals) + } else actuals + if (formals1.length != actuals1.length) return false + + if (tparams.isEmpty) return (actuals1 corresponds formals1)(_ <:< _) + + if (targs.length == tparams.length) + isApplicableType(List(), tpe.instantiateTypeParams(tparams, targs)) + else if (targs.nonEmpty) + false + else { + val tvars = tparams map (TypeVar(_)) + (actuals1 corresponds formals1) { (actual, formal) => + val tp1 = actual.deconst.instantiateTypeParams(tparams, tvars) + val pt1 = actual.instantiateTypeParams(tparams, tvars) + tp1 <:< pt1 + } && + solve(tvars, tparams, List.fill(tparams.length)(COVARIANT), upper = false) + } + } + isApplicableType(List(), pre.memberType(alt)) + } + def isAsGood(alt1: Symbol, alt2: Symbol): Boolean = { + alt1 == alt2 || + alt2 == NoSymbol || { + val (tparams, formals) = firstParams(pre memberType alt1) + isApplicable(alt2, tparams map (_.tpe), formals) + } + } + assert(isOverloaded) + val applicables = alternatives filter (isApplicable(_, targs.toList, actuals)) + def winner(alts: List[Symbol]) = + ((NoSymbol: Symbol) /: alts)((best, alt) => if (isAsGood(alt, best)) alt else best) + val best = winner(applicables) + if (best == winner(applicables.reverse)) best else NoSymbol + } + } + + /** The class for all symbols */ + abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name) + extends SymbolContextApiImpl + with HasFlags + with Annotatable[Symbol] { + + type AccessBoundaryType = Symbol + type AnnotationType = AnnotationInfo + + // TODO - don't allow names to be renamed in this unstructured a fashion. + // Rename as little as possible. Enforce invariants on all renames. + type TypeOfClonedSymbol >: Null <: Symbol { type NameType = Symbol.this.NameType } + + // Abstract here so TypeSymbol and TermSymbol can have a private[this] field + // with the proper specific type. + def rawname: NameType + def name: NameType + def name_=(n: Name): Unit + def asNameType(n: Name): NameType + + private[this] var _rawowner = initOwner // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api + private[this] var _rawflags: Long = _ + + def rawowner = _rawowner + def rawflags = _rawflags + + private var rawpos = initPos + + val id = nextId() // identity displayed when -uniqid + //assert(id != 3390, initName) + + private[this] var _validTo: Period = NoPeriod + + if (traceSymbolActivity) + traceSymbols.recordNewSymbol(this) + + def validTo = _validTo + def validTo_=(x: Period) { _validTo = x} + + def pos = rawpos + def setPos(pos: Position): this.type = { this.rawpos = pos; this } + def setName(name: Name): this.type = { this.name = asNameType(name) ; this } + + // Update the surrounding scopes + protected[this] def changeNameInOwners(name: Name) { + if (owner.isClass) { + var ifs = owner.infos + while (ifs != null) { + ifs.info.decls.rehash(this, name) + ifs = ifs.prev + } + } + } + + def rawFlagString(mask: Long): String = calculateFlagString(rawflags & mask) + def rawFlagString: String = rawFlagString(flagMask) + def debugFlagString: String = flagString(AllFlags) + + /** String representation of symbol's variance */ + def varianceString: String = + if (variance == 1) "+" + else if (variance == -1) "-" + else "" + + override def flagMask = + if (settings.debug.value && !isAbstractType) AllFlags + else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE + else ExplicitFlags + + // make the error message more googlable + def flagsExplanationString = + if (isGADTSkolem) " (this is a GADT skolem)" + else "" + + def shortSymbolClass = getClass.getName.split('.').last.stripPrefix("Symbols$") + def symbolCreationString: String = ( + "%s%25s | %-40s | %s".format( + if (settings.uniqid.value) "%06d | ".format(id) else "", + shortSymbolClass, + name.decode + " in " + owner, + rawFlagString + ) + ) + +// ------ creators ------------------------------------------------------------------- + + final def newValue(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = + newTermSymbol(name, pos, newFlags) + final def newVariable(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = + newTermSymbol(name, pos, MUTABLE | newFlags) + final def newValueParameter(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = + newTermSymbol(name, pos, PARAM | newFlags) + + /** Create local dummy for template (owner of local blocks) */ + final def newLocalDummy(pos: Position): TermSymbol = + newTermSymbol(nme.localDummyName(this), pos) setInfo NoType + final def newMethod(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): MethodSymbol = + createMethodSymbol(name, pos, METHOD | newFlags) + final def newMethodSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): MethodSymbol = + createMethodSymbol(name, pos, METHOD | newFlags) + final def newLabel(name: TermName, pos: Position = NoPosition): MethodSymbol = + newMethod(name, pos, LABEL) + + /** Propagates ConstrFlags (JAVA, specifically) from owner to constructor. */ + final def newConstructor(pos: Position, newFlags: Long = 0L): MethodSymbol = + newMethod(nme.CONSTRUCTOR, pos, getFlag(ConstrFlags) | newFlags) + + /** Static constructor with info set. */ + def newStaticConstructor(pos: Position): MethodSymbol = + newConstructor(pos, STATIC) setInfo UnitClass.tpe + + /** Instance constructor with info set. */ + def newClassConstructor(pos: Position): MethodSymbol = + newConstructor(pos) setInfo MethodType(Nil, this.tpe) + + def newLinkedModule(clazz: Symbol, newFlags: Long = 0L): ModuleSymbol = { + val m = newModuleSymbol(clazz.name.toTermName, clazz.pos, MODULE | newFlags) + connectModuleToClass(m, clazz.asInstanceOf[ClassSymbol]) + } + final def newModule(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = { + val m = newModuleSymbol(name, pos, newFlags | MODULE) + val clazz = newModuleClass(name.toTypeName, pos, m getFlag ModuleToClassFlags) + connectModuleToClass(m, clazz) + } + + final def newPackage(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = { + assert(name == nme.ROOT || isPackageClass, this) + newModule(name, pos, PackageFlags | newFlags) + } + + final def newThisSym(name: TermName = nme.this_, pos: Position = NoPosition): TermSymbol = + newTermSymbol(name, pos, SYNTHETIC) + + final def newImport(pos: Position): TermSymbol = + newTermSymbol(nme.IMPORT, pos) + + final def newModuleSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = + newTermSymbol(name, pos, newFlags).asInstanceOf[ModuleSymbol] + + final def newModuleAndClassSymbol(name: Name, pos: Position, flags: FlagSet): (ModuleSymbol, ClassSymbol) = { + val m = newModuleSymbol(name, pos, flags | MODULE) + val c = newModuleClass(name.toTypeName, pos, m getFlag ModuleToClassFlags) + connectModuleToClass(m, c) + (m, c) + } + + final def newPackageSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = + newTermSymbol(name, pos, newFlags).asInstanceOf[ModuleSymbol] + + final def newModuleClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = + newClassSymbol(name, pos, newFlags).asInstanceOf[ModuleClassSymbol] + + final def newTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position = NoPosition, newFlags: Long = 0L): TypeSkolem = + createTypeSkolemSymbol(name, origin, pos, newFlags) + + /** @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]): TermSymbol = ( + newTermSymbol(alternatives.head.name.toTermName, alternatives.head.pos, OVERLOADED) + setInfo OverloadedType(pre, alternatives) + ) + + final def newErrorValue(name: TermName): TermSymbol = + newTermSymbol(name, pos, SYNTHETIC | IS_ERROR) setInfo ErrorType + + /** Symbol of a type definition type T = ... + */ + final def newAliasType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AliasTypeSymbol = + createAliasTypeSymbol(name, pos, newFlags) + + /** Symbol of an abstract type type T >: ... <: ... + */ + final def newAbstractType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AbstractTypeSymbol = + createAbstractTypeSymbol(name, pos, DEFERRED | newFlags) + + /** Symbol of a type parameter + */ + final def newTypeParameter(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = + newAbstractType(name, pos, PARAM | newFlags) + +// is defined in SymbolCreations +// final def newTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = +// (if ((newFlags & DEFERRED) != 0) new AbstractTypeSymbol(this, pos, name) +// else new AbstractTypeSymbol(this, pos, name)) setFlag newFlags + + /** Symbol of an existential type T forSome { ... } + */ + final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = + newAbstractType(name, pos, EXISTENTIAL | newFlags) + + /** Synthetic value parameters when parameter symbols are not available + */ + final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[TermSymbol]] = { + var cnt = 0 + def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } + mmap(argtypess)(tp => newValueParameter(freshName(), owner.pos.focus, SYNTHETIC) setInfo tp) + } + + def newSyntheticTypeParam(): TypeSymbol = newSyntheticTypeParam("T0", 0L) + def newSyntheticTypeParam(name: String, newFlags: Long): TypeSymbol = newTypeParameter(newTypeName(name), NoPosition, newFlags) setInfo TypeBounds.empty + def newSyntheticTypeParams(num: Int): List[TypeSymbol] = (0 until num).toList map (n => newSyntheticTypeParam("T" + n, 0L)) + + /** Create a new existential type skolem with this symbol its owner, + * based on the given symbol and origin. + */ + def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { + val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (basis.info cloneInfo skolem) + } + + // flags set up to maintain TypeSkolem's invariant: origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) + // CASEACCESSOR | SYNTHETIC used to single this symbol out in deskolemizeGADT + def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem = + newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | CASEACCESSOR | SYNTHETIC) setInfo info + + final def freshExistential(suffix: String): TypeSymbol = + newExistential(freshExistentialName(suffix), pos) + + /** 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[TermSymbol] = + 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: TypeSkolem = + owner.newTypeSkolemSymbol(name.toTypeName, this, pos, flags) + + final def newClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = + newClassSymbol(name, pos, newFlags) + + /** A new class with its info set to a ClassInfoType with given scope and parents. */ + def newClassWithInfo(name: TypeName, parents: List[Type], scope: Scope, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = { + val clazz = newClass(name, pos, newFlags) + clazz setInfo ClassInfoType(parents, scope, clazz) + } + final def newErrorClass(name: TypeName): ClassSymbol = + newClassWithInfo(name, Nil, new ErrorScope(this), pos, SYNTHETIC | IS_ERROR) + + final def newModuleClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = + newModuleClassSymbol(name, pos, newFlags | MODULE) + + final def newAnonymousFunctionClass(pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = + newClassSymbol(tpnme.ANON_FUN_NAME, pos, FINAL | SYNTHETIC | newFlags) + + final def newAnonymousFunctionValue(pos: Position, newFlags: Long = 0L): TermSymbol = + newTermSymbol(nme.ANON_FUN_NAME, pos, SYNTHETIC | newFlags) setInfo NoType + + def newImplClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = { + newClassSymbol(name, pos, newFlags | IMPLCLASS) + } + + /** Refinement types P { val x: String; type T <: Number } + * also have symbols, they are refinementClasses + */ + final def newRefinementClass(pos: Position): RefinementClassSymbol = + createRefinementClassSymbol(pos, 0L) + + /** Create a new getter for current symbol (which must be a field) + */ + final def newGetter: MethodSymbol = ( + owner.newMethod(nme.getterName(name.toTermName), NoPosition, getterFlags(flags)) + setPrivateWithin privateWithin + setInfo MethodType(Nil, tpe) + ) + + final def newErrorSymbol(name: Name): Symbol = name match { + case x: TypeName => newErrorClass(x) + case x: TermName => newErrorValue(x) + } + + @deprecated("Use the other signature", "2.10.0") + def newClass(pos: Position, name: TypeName): Symbol = newClass(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newModuleClass(pos: Position, name: TypeName): Symbol = newModuleClass(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newLabel(pos: Position, name: TermName): MethodSymbol = newLabel(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newValue(pos: Position, name: TermName): TermSymbol = newTermSymbol(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newAliasType(pos: Position, name: TypeName): Symbol = newAliasType(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newAbstractType(pos: Position, name: TypeName): Symbol = newAbstractType(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newExistential(pos: Position, name: TypeName): Symbol = newExistential(name, pos) + @deprecated("Use the other signature", "2.10.0") + def newMethod(pos: Position, name: TermName): MethodSymbol = newMethod(name, pos) + +// ----- locking and unlocking ------------------------------------------------------ + + // True if the symbol is unlocked. + // True if the symbol is locked but still below the allowed recursion depth. + // False otherwise + private[scala] 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. + private[scala] def lock(handler: => Unit): Boolean = { + if ((_rawflags & LOCKED) != 0L) { + if (settings.Yrecursion.value != 0) { + recursionTable get this match { + case Some(n) => + if (n > settings.Yrecursion.value) { + handler + false + } else { + recursionTable += (this -> (n + 1)) + true + } + case None => + recursionTable += (this -> 1) + true + } + } else { handler; false } + } else { + _rawflags |= LOCKED + true +// activeLocks += 1 +// lockedSyms += this + } + } + + // Unlock a symbol + private[scala] def unlock() = { + if ((_rawflags & LOCKED) != 0L) { +// activeLocks -= 1 +// lockedSyms -= this + _rawflags &= ~LOCKED + if (settings.Yrecursion.value != 0) + recursionTable -= this + } + } + +// ----- tests ---------------------------------------------------------------------- + + def isAliasType = false + def isAbstractType = false + def isSkolem = false + + /** A Type, but not a Class. */ + def isNonClassType = false + + /** The bottom classes are Nothing and Null, found in Definitions. */ + def isBottomClass = false + def isSpecialized = this hasFlag SPECIALIZED + + /** These are all tests for varieties of ClassSymbol, which has these subclasses: + * - ModuleClassSymbol + * - RefinementClassSymbol + * - PackageClassSymbol (extends ModuleClassSymbol) + */ + def isAbstractClass = false + def isAnonOrRefinementClass = false + def isAnonymousClass = false + def isCaseClass = false + def isConcreteClass = false + def isImplClass = false // the implementation class of a trait + def isJavaInterface = false + def isModuleClass = false + def isNumericValueClass = false + def isPrimitiveValueClass = false + def isRefinementClass = false + override def isTrait = false + + /** Qualities of Types, always false for TermSymbols. + */ + def isContravariant = false + def isCovariant = false + def isExistentialQuantified = false + def isExistentialSkolem = false + def isExistentiallyBound = false + def isGADTSkolem = false + def isTypeParameter = false + def isTypeParameterOrSkolem = false + def isTypeSkolem = false + def isTypeMacro = false + + /** Qualities of Terms, always false for TypeSymbols. + */ + def isAccessor = false + def isBridge = false + def isCapturedVariable = false + def isClassConstructor = false + def isConstructor = false + def isEarlyInitialized = false + def isGetter = false + def isLocalDummy = false + def isMixinConstructor = false + def isOverloaded = false + def isSetter = false + def isSetterParameter = false + def isValue = false + def isValueParameter = false + def isVariable = false + override def hasDefault = false + def isTermMacro = false + + /** Qualities of MethodSymbols, always false for TypeSymbols + * and other TermSymbols. + */ + def isCaseAccessorMethod = false + def isLiftedMethod = false + def isSourceMethod = false + def isVarargsMethod = false + override def isLabel = false + + /** Package/package object tests */ + def isPackageClass = false + def isPackageObject = false + def isPackageObjectClass = false + def isPackageObjectOrClass = isPackageObject || isPackageObjectClass + def isModuleOrModuleClass = isModule || isModuleClass + + /** Overridden in custom objects in Definitions */ + def isRoot = false + def isRootPackage = false + def isRootSymbol = false // RootPackage and RootClass. TODO: also NoSymbol. + def isEmptyPackage = false + def isEmptyPackageClass = false + + /** Is this symbol an effective root for fullname string? + */ + def isEffectiveRoot = false + + /** For RootClass, this is EmptyPackageClass. For all other symbols, + * the symbol itself. + */ + def ownerOfNewSymbols = this + + final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol + final def isOverridableMember = !(isClass || isEffectivelyFinal) && (this ne NoSymbol) && owner.isClass + + /** Does this symbol denote a wrapper created by the repl? */ + final def isInterpreterWrapper = ( + (this hasFlag MODULE) + && owner.isPackageClass + && nme.isReplWrapperName(name) + ) + @inline final def getFlag(mask: Long): Long = flags & mask + /** Does symbol have ANY flag in `mask` set? */ + @inline final def hasFlag(mask: Long): Boolean = (flags & mask) != 0 + /** Does symbol have ALL the flags in `mask` set? */ + @inline final def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask + + def setFlag(mask: Long): this.type = { _rawflags |= mask ; this } + def resetFlag(mask: Long): this.type = { _rawflags &= ~mask ; this } + def resetFlags() { rawflags &= TopLevelCreationFlags } + + /** Default implementation calls the generic string function, which + * will print overloaded flags as <flag1/flag2/flag3>. Subclasses + * of Symbol refine. + */ + override def resolveOverloadedFlag(flag: Long): String = Flags.flagToString(flag) + + /** Set the symbol's flags to the given value, asserting + * that the previous value was 0. + */ + def initFlags(mask: Long): this.type = { + assert(rawflags == 0L, symbolCreationString) + _rawflags = mask + this + } + + final def flags: Long = { + val fs = _rawflags & phase.flagMask + (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) + } + def flags_=(fs: Long) = _rawflags = fs + def rawflags_=(x: Long) { _rawflags = x } + + final def hasGetter = isTerm && nme.isLocalName(name) + + final def isInitializedToDefault = !isType && hasAllFlags(DEFAULTINIT | ACCESSOR) + 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 + + def isHigherOrderTypeParameter = owner.isTypeParameterOrSkolem + + // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor + def isClassLocalToConstructor = false + + final def isDerivedValueClass = + isClass && info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass + + final def isMethodWithExtension = + isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) + + final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME) + final def isDefinedInPackage = effectiveOwner.isPackageClass + final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass + + /** change name by appending $$<fully-qualified-name-of-class `base`> + * Do the same for any accessed symbols or setters/getters. + * Implementation in TermSymbol. + */ + def expandName(base: Symbol) { } + + // In java.lang, Predef, or scala package/package object + def isInDefaultNamespace = UnqualifiedOwners(effectiveOwner) + + /** The owner, skipping package objects. + */ + def effectiveOwner = owner.skipPackageObject + + /** If this is a package object or its implementing class, its owner: otherwise this. + */ + def skipPackageObject: Symbol = 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 isOmittablePrefix = /*!settings.debug.value &&*/ ( + UnqualifiedOwners(skipPackageObject) + || isEmptyPrefix + ) + def isEmptyPrefix = ( + isEffectiveRoot // has no prefix for real, <empty> or <root> + || isAnonOrRefinementClass // has uninteresting <anon> or <refinement> prefix + || nme.isReplWrapperName(name) // has ugly $iw. prefix (doesn't call isInterpreterWrapper due to nesting) + ) + def isFBounded = info match { + case TypeBounds(_, _) => info.baseTypeSeq exists (_ contains this) + case _ => false + } + + /** 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 && { + val info = originalInfo + info.isComplete && !info.isHigherKinded + } + + 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 hasBridgeAnnotation = hasAnnotation(BridgeClass) + def isDeprecated = hasAnnotation(DeprecatedAttr) + def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0) + def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1) + def deprecatedParamName = getAnnotation(DeprecatedNameAttr) flatMap (_ symbolArg 0) + + // !!! 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 hasMigrationAnnotation = hasAnnotation(MigrationAnnotationClass) + def migrationMessage = getAnnotation(MigrationAnnotationClass) flatMap { _.stringArg(0) } + def migrationVersion = getAnnotation(MigrationAnnotationClass) flatMap { _.stringArg(1) } + 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? */ + def isStable = false + + /** 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 = ( + isTrait + && (!isInterface || hasFlag(lateINTERFACE)) + && !isImplClass + ) + + /** Is this a symbol which exists only in the implementation class, not in its trait? */ + final def isImplOnly = isPrivate || ( + (owner.isTrait || owner.isImplClass) && ( + hasAllFlags(LIFTED | MODULE | METHOD) + || isConstructor + || hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR | MODULE) + ) + ) + final def isModuleVar = hasFlag(MODULEVAR) + + /** Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + */ + def isStatic = (this hasFlag STATIC) || 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 + + def isTopLevelModule = hasFlag(MODULE) && owner.isPackageClass + + /** Is this symbol effectively final? I.e, it cannot be overridden */ + final def isEffectivelyFinal: Boolean = ( + (this hasFlag FINAL | PACKAGE) + || isModuleOrModuleClass && (owner.isPackageClass || !settings.overrideObjects.value) + || isTerm && ( + isPrivate + || isLocal + || owner.isClass && owner.isEffectivelyFinal + ) + ) + + /** 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 && isConstantType(tpe.resultType) + + /** Is this class nested in another class or module (not a package)? */ + def isNestedClass = false + + /** 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 + */ + def isLocalClass = false + + def isStableClass = false + +/* 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 a term symbol only defined in a refinement (so that it needs + * to be accessed by reflection)? + */ + def isOnlyRefinementMember: Boolean = + isTerm && // type members are not affected + owner.isRefinementClass && // owner must be a refinement class + (owner.info decl name) == this && // symbol must be explicitly declared in the refinement (not synthesized from glb) + allOverriddenSymbols.isEmpty && // symbol must not override a symbol in a base class + !isConstant // symbol must not be a constant. Question: Can we exclude @inline methods as well? + + final def isStructuralRefinementMember = owner.isStructuralRefinement && isPossibleInRefinement && isPublic + final def isPossibleInRefinement = !isConstructor && !isOverridingSymbol + + /** 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. + def exists = !owner.isPackageClass || { rawInfo.load(this); rawInfo != NoType } + + final def isInitialized: Boolean = + validTo != NoPeriod + + // [Eugene] todo. needs to be reviewed and [only then] rewritten without explicit returns + /** Determines whether this symbol can be loaded by subsequent reflective compilation */ + final def isLocatable: Boolean = { + if (this == NoSymbol) return false + if (isRoot || isRootPackage) return true + + if (!owner.isLocatable) return false + if (owner.isTerm) return false + if (isLocalDummy) return false + + if (isType && isNonClassType) return false + if (isRefinementClass) return false + return true + } + + // [Eugene] is it a good idea to add ``dealias'' to Symbol? + /** Expands type aliases */ + def dealias: Symbol = this + + /** The variance of this symbol as an integer */ + final def variance: Int = + if (isCovariant) 1 + else if (isContravariant) -1 + else 0 + + /** The sequence number of this parameter symbol among all type + * and value parameters of symbol's owner. -1 if symbol does not + * appear among the parameters of its owner. + */ + def paramPos: Int = { + def searchIn(tpe: Type, base: Int): Int = { + def searchList(params: List[Symbol], fallback: Type): Int = { + val idx = params indexOf this + if (idx >= 0) idx + base + else searchIn(fallback, base + params.length) + } + tpe match { + case PolyType(tparams, res) => searchList(tparams, res) + case MethodType(params, res) => searchList(params, res) + case _ => -1 + } + } + searchIn(owner.info, 0) + } + +// ------ owner attribute -------------------------------------------------------------- + + def owner: Symbol = rawowner + // TODO - don't allow the owner to be changed without checking invariants, at least + // when under some flag. Define per-phase invariants for owner/owned relationships, + // e.g. after flatten all classes are owned by package classes, there are lots and + // lots of these to be declared (or more realistically, discovered.) + def owner_=(owner: Symbol) { + // don't keep the original owner in presentation compiler runs + // (the map will grow indefinitely, and the only use case is the + // backend). + if (!forInteractive) { + if (originalOwner contains this) () + else originalOwner(this) = rawowner + } + assert(isCompilerUniverse, "owner_= is not thread-safe; cannot be run in reflexive code") + if (traceSymbolActivity) + traceSymbols.recordNewSymbolOwner(this, owner) + _rawowner = owner + } + + def ownerChain: List[Symbol] = this :: owner.ownerChain + def originalOwnerChain: List[Symbol] = this :: originalOwner.getOrElse(this, rawowner).originalOwnerChain + + // Non-classes skip self and return rest of owner chain; overridden in ClassSymbol. + def enclClassChain: List[Symbol] = 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 (see RefinementClassSymbol.) + */ + def hasTransOwner(sym: Symbol): Boolean = { + var o = this + while ((o ne sym) && (o ne NoSymbol)) o = o.owner + (o eq sym) + } + +// ------ name attribute -------------------------------------------------------------- + + /** If this symbol has an expanded name, its original name, otherwise its name itself. + * @see expandName + */ + def originalName: Name = 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 = nme.dropLocalSuffix(name).decode + + private def addModuleSuffix(n: Name): Name = + if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n + + def moduleSuffix: String = ( + if (needsModuleSuffix) nme.MODULE_SUFFIX_STRING + else "" + ) + /** Whether this symbol needs nme.MODULE_SUFFIX_STRING (aka $) appended on the java platform. + */ + def needsModuleSuffix = ( + hasModuleFlag + && !isMethod + && !isImplClass + && !isJavaDefined + ) + /** These should be moved somewhere like JavaPlatform. + */ + def javaSimpleName: Name = addModuleSuffix(nme.dropLocalSuffix(simpleName)) + def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) + def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString + + /** 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. + * Drops package objects. + */ + final def fullName(separator: Char): String = fullNameAsName(separator).toString + + /** Doesn't drop package objects, for those situations (e.g. classloading) + * where the true path is needed. + */ + private def fullNameInternal(separator: Char): Name = ( + if (isRoot || isRootPackage || this == NoSymbol) name + else if (owner.isEffectiveRoot) name + else effectiveOwner.enclClass.fullNameAsName(separator) append separator append name + ) + + def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) + + /** The encoded full path name of this symbol, where outer names and inner names + * are separated by periods. + */ + final def fullName: String = fullName('.') + + /** + * Symbol creation implementations. + */ + + protected def createAbstractTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AbstractTypeSymbol = + new AbstractTypeSymbol(this, pos, name) initFlags newFlags + + protected def createAliasTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AliasTypeSymbol = + new AliasTypeSymbol(this, pos, name) initFlags newFlags + + protected def createTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position, newFlags: Long): TypeSkolem = + new TypeSkolem(this, pos, name, origin) initFlags newFlags + + protected def createClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = + new ClassSymbol(this, pos, name) initFlags newFlags + + protected def createModuleClassSymbol(name: TypeName, pos: Position, newFlags: Long): ModuleClassSymbol = + new ModuleClassSymbol(this, pos, name) initFlags newFlags + + protected def createPackageClassSymbol(name: TypeName, pos: Position, newFlags: Long): PackageClassSymbol = + new PackageClassSymbol(this, pos, name) initFlags newFlags + + protected def createRefinementClassSymbol(pos: Position, newFlags: Long): RefinementClassSymbol = + new RefinementClassSymbol(this, pos) initFlags newFlags + + protected def createPackageObjectClassSymbol(pos: Position, newFlags: Long): PackageObjectClassSymbol = + new PackageObjectClassSymbol(this, pos) initFlags newFlags + + protected def createImplClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = + new ClassSymbol(this, pos, name) with ImplClassSymbol initFlags newFlags + + protected def createTermSymbol(name: TermName, pos: Position, newFlags: Long): TermSymbol = + new TermSymbol(this, pos, name) initFlags newFlags + + protected def createMethodSymbol(name: TermName, pos: Position, newFlags: Long): MethodSymbol = + new MethodSymbol(this, pos, name) initFlags newFlags + + protected def createModuleSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = + new ModuleSymbol(this, pos, name) initFlags newFlags + + protected def createPackageSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = + new ModuleSymbol(this, pos, name) initFlags newFlags + + protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long): TermSymbol = + new TermSymbol(this, pos, name) initFlags newFlags + + protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long): TermSymbol = + new TermSymbol(this, pos, name) initFlags newFlags + + final def newTermSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = { + if ((newFlags & METHOD) != 0) + createMethodSymbol(name, pos, newFlags) + else if ((newFlags & PACKAGE) != 0) + createPackageSymbol(name, pos, newFlags | PackageFlags) + else if ((newFlags & MODULE) != 0) + createModuleSymbol(name, pos, newFlags) + else if ((newFlags & PARAM) != 0) + createValueParameterSymbol(name, pos, newFlags) + else + createValueMemberSymbol(name, pos, newFlags) + } + + final def newClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = { + if (name == tpnme.REFINE_CLASS_NAME) + createRefinementClassSymbol(pos, newFlags) + else if ((newFlags & PACKAGE) != 0) + createPackageClassSymbol(name, pos, newFlags | PackageFlags) + else if (name == tpnme.PACKAGE) + createPackageObjectClassSymbol(pos, newFlags) + else if ((newFlags & MODULE) != 0) + createModuleClassSymbol(name, pos, newFlags) + else if ((newFlags & IMPLCLASS) != 0) + createImplClassSymbol(name, pos, newFlags) + else + createClassSymbol(name, pos, newFlags) + } + + final def newNonClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = { + if ((newFlags & DEFERRED) != 0) + createAbstractTypeSymbol(name, pos, newFlags) + else + createAliasTypeSymbol(name, pos, newFlags) + } + + def newTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = + newNonClassSymbol(name, pos, newFlags) + + /** The class or term up to which this symbol is accessible, + * or RootClass if it is public. As java protected statics are + * otherwise completely inaccessible in scala, they are treated + * as public. + */ + def accessBoundary(base: Symbol): Symbol = { + if (hasFlag(PRIVATE) || isLocal) owner + else if (hasAllFlags(PROTECTED | STATIC | JAVA)) enclosingRootClass + else if (hasAccessBoundary && !phase.erasedTypes) privateWithin + else if (hasFlag(PROTECTED)) base + else enclosingRootClass + } + + 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 } + def setPrivateWithin(sym: Symbol): this.type = { privateWithin_=(sym) ; this } + + /** 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 + def originalInfo = { + if (infos eq null) null + else { + var is = infos + while (is.prev ne null) { is = is.prev } + is.info + } + } + + /** 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. + * See the tpe/tpeHK overrides in TypeSymbol for more. + */ + def tpe: Type = info + def tpeHK: Type = tpe + + /** 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) + } + rawInfo + } + catch { + case ex: CyclicReference => + debugwarn("... hit cycle trying to complete " + this.fullLocationString) + 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 } + /** Modifies this symbol's info in place. */ + def modifyInfo(f: Type => Type): this.type = setInfo(f(info)) + /** Substitute second list of symbols for first in current info. */ + def substInfo(syms0: List[Symbol], syms1: List[Symbol]): this.type = + if (syms0.isEmpty) this + else modifyInfo(_.substSym(syms0, syms1)) + + def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this) + + /** Set the info and enter this symbol into the owner's scope. */ + def setInfoAndEnter(info: Type): this.type = { + setInfo(info) + owner.info.decls enter this + this + } + + /** Set new info valid from start of this phase. */ + def updateInfo(info: Type): Symbol = { + val pid = phaseId(infos.validFrom) + assert(pid <= phase.id, (pid, phase.id)) + if (pid == phase.id) infos = infos.prev + infos = TypeHistory(currentPeriod, info, infos) + _validTo = if (info.isComplete) currentPeriod else NoPeriod + this + } + + def hasRawInfo: Boolean = infos ne null + def hasCompleteInfo = hasRawInfo && rawInfo.isComplete + + /** 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 = { + assert(isCompilerUniverse) + if (infos == null || runId(infos.validFrom) == currentRunId) { + infos + } else { + val prev1 = adaptInfos(infos.prev) + if (prev1 ne infos.prev) prev1 + else { + val pid = phaseId(infos.validFrom) + + _validTo = period(currentRunId, pid) + phase = phaseWithId(pid) + + val info1 = ( + if (isPackageClass) infos.info + else adaptToNewRunMap(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 = { + assert(isCompilerUniverse) + 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 = { + assert(isCompilerUniverse) + 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) + + /** The logic approximately boils down to finding the most recent phase + * which immediately follows any of parser, namer, typer, or erasure. + * In effect that means this will return one of: + * + * - packageobjects (follows namer) + * - superaccessors (follows typer) + * - lazyvals (follows erasure) + * - null + */ + private def unsafeTypeParamPhase = { + var ph = phase + while (ph.prev.keepsTypeParams) + ph = ph.prev + + ph + } + /** 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) Nil + else atPhase(unsafeTypeParamPhase)(rawInfo.typeParams) + + /** 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) Nil + else { + // analogously to the "info" getter, here we allow for two completions: + // one: sourceCompleter to LazyType, two: LazyType to completed type + if (validTo == NoPeriod) + atPhase(phaseOf(infos.validFrom))(rawInfo load this) + if (validTo == NoPeriod) + atPhase(phaseOf(infos.validFrom))(rawInfo load this) + + rawInfo.typeParams + } + + /** The value parameter sections of this symbol. + */ + def paramss: List[List[Symbol]] = info.paramss + def hasParamWhich(cond: Symbol => Boolean) = mexists(paramss)(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) { + // todo: what about public references to private symbols? + if (sym.isPublic && !sym.isConstructor) { + oldsymbuf += sym + newsymbuf += ( + if (sym.isClass) + tp.typeSymbol.newAbstractType(sym.name.toTypeName, sym.pos).setInfo(sym.existentialBound) + else + sym.cloneSymbol(tp.typeSymbol)) + } + } + val oldsyms = oldsymbuf.toList + val newsyms = newsymbuf.toList + for (sym <- newsyms) { + addMember(thistp, tp, sym modifyInfo (_ substThisAndSym(this, thistp, 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 + + /** Reset symbol to initial state + */ + def reset(completer: Type): this.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 :+ 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 ------------------------------------------------------------ + + // null is a marker that they still need to be obtained. + private[this] var _annotations: List[AnnotationInfo] = Nil + + def annotationsString = if (annotations.isEmpty) "" else annotations.mkString("(", ", ", ")") + + /** 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] = + _annotations + + def setAnnotations(annots: List[AnnotationInfo]): this.type = { + _annotations = annots + this + } + + def withAnnotations(annots: List[AnnotationInfo]): this.type = + setAnnotations(annots ::: annotations) + + def withoutAnnotations: this.type = + setAnnotations(Nil) + + def filterAnnotations(p: AnnotationInfo => Boolean): this.type = + setAnnotations(annotations filter p) + + def addAnnotation(annot: AnnotationInfo): this.type = + setAnnotations(annot :: annotations) + + // Convenience for the overwhelmingly common case + def addAnnotation(sym: Symbol, args: Tree*): this.type = + addAnnotation(AnnotationInfo(sym.tpe, args.toList, Nil)) + +// ------ 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, + * and is this class symbol also different from Null or Nothing? */ + def isNonBottomSubClass(that: Symbol): Boolean = false + + /** Overridden in NullClass and NothingClass for custom behavior. + */ + def isSubClass(that: Symbol) = isNonBottomSubClass(that) + + final def isNumericSubClass(that: Symbol): Boolean = + definitions.isNumericSubClass(this, that) + + final def isWeakSubClass(that: Symbol) = + isSubClass(that) || isNumericSubClass(that) + +// ------ overloaded alternatives ------------------------------------------------------ + + def alternatives: List[Symbol] = + if (isOverloaded) info.asInstanceOf[OverloadedType].alternatives + else List(this) + + def filter(cond: Symbol => Boolean): Symbol = + if (isOverloaded) { + 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 (cond(this)) this + else NoSymbol + + def suchThat(cond: Symbol => Boolean): Symbol = { + val result = filter(cond) + assert(!result.isOverloaded, result.alternatives) + result + } + + @inline final def map(f: Symbol => Symbol): Symbol = if (this eq NoSymbol) this else f(this) + +// ------ cloneing ------------------------------------------------------------------- + + /** A clone of this symbol. */ + final def cloneSymbol: TypeOfClonedSymbol = + cloneSymbol(owner) + + /** A clone of this symbol, but with given owner. */ + final def cloneSymbol(newOwner: Symbol): TypeOfClonedSymbol = + cloneSymbol(newOwner, _rawflags) + final def cloneSymbol(newOwner: Symbol, newFlags: Long): TypeOfClonedSymbol = + cloneSymbol(newOwner, newFlags, null) + final def cloneSymbol(newOwner: Symbol, newFlags: Long, newName: Name): TypeOfClonedSymbol = { + val clone = cloneSymbolImpl(newOwner, newFlags) + ( clone + setPrivateWithin privateWithin + setInfo (this.info cloneInfo clone) + setAnnotations this.annotations + ) + if (clone.thisSym != clone) + clone.typeOfThis = (clone.typeOfThis cloneInfo clone) + + if (newName ne null) + clone setName asNameType(newName) + + clone + } + + /** Internal method to clone a symbol's implementation with the given flags and no info. */ + def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeOfClonedSymbol + +// ------ 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 = NoSymbol + + /** The self symbol (a TermSymbol) of a class with explicit self type, or else the + * symbol itself (a TypeSymbol). + * + * WARNING: you're probably better off using typeOfThis, as it's more uniform across classes with and without self variables. + * + * Example by Paul: + * scala> trait Foo1 { } + * scala> trait Foo2 { self => } + * scala> intp("Foo1").thisSym + * res0: $r.intp.global.Symbol = trait Foo1 + * + * scala> intp("Foo2").thisSym + * res1: $r.intp.global.Symbol = value self + * + * Martin says: The reason `thisSym' is `this' is so that thisType can be this.thisSym.tpe. + * It's a trick to shave some cycles off. + * + * Morale: DO: if (clazz.typeOfThis.typeConstructor ne clazz.typeConstructor) ... + * DON'T: if (clazz.thisSym ne clazz) ... + * + */ + 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 + + /** For a case class, the symbols of the accessor methods, one for each + * argument in the first parameter list of the primary constructor. + * The empty list for all other classes. + */ + final def caseFieldAccessors: List[Symbol] = + (info.decls filter (_.isCaseAccessorMethod)).toList + + 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, this) + ownerTp decl nme.getterToLocal(getterName.toTermName) + } + + /** 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. If available it will be the + * symbol with the same owner, and the name of this symbol with $class + * appended to it. + */ + final def implClass: Symbol = owner.info.decl(tpnme.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 + + /** If this is an accessor, the accessed symbol. Otherwise, this symbol. */ + def accessedOrSelf: Symbol = if (hasAccessorFlag) accessed 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 + def parentSymbols: List[Symbol] = info.parents map (_.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 + + @inline final def enclosingSuchThat(p: Symbol => Boolean): Symbol = { + var sym = this + while (sym != NoSymbol && !p(sym)) + sym = sym.owner + sym + } + + /** The package class containing this symbol, or NoSymbol if there + * is not one. + * TODO: formulate as enclosingSuchThat, after making sure + * we can start with current symbol rather than onwner. + * TODO: Also harmonize with enclClass, enclMethod etc. + */ + def enclosingPackageClass: Symbol = { + var sym = this.owner + while (sym != NoSymbol && !sym.isPackageClass) + sym = sym.owner + sym + } + + /** The package class containing this symbol, or NoSymbol if there + * is not one. */ + def enclosingRootClass: Symbol = enclosingSuchThat(_.isRoot) + + /** The package containing this symbol, or NoSymbol if there + * is not one. */ + def enclosingPackage: Symbol = enclosingPackageClass.companionModule + + /** 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. + * + * @note This method is NOT available in the presentation compiler run. The + * originalOwner map is not populated for memory considerations (the symbol + * may hang on to lazy types and in turn to whole (outdated) compilation units. + */ + def originalEnclosingMethod: Symbol = { + assert(!forInteractive, "originalOwner is not kept in presentation compiler runs.") + 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 + + /** Kept for source compatibility with 2.9. Scala IDE for Eclipse relies on this. */ + @deprecated("Use enclosingTopLevelClass", "2.10.0") + def toplevelClass: Symbol = enclosingTopLevelClass + + /** The top-level class containing this symbol. */ + def enclosingTopLevelClass: Symbol = + if (owner.isPackageClass) { + if (isClass) this else moduleClass + } else owner.enclosingTopLevelClass + + /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ + def isCoDefinedWith(that: Symbol) = { + import language.reflectiveCalls + (this.rawInfo ne NoType) && + (this.effectiveOwner == that.effectiveOwner) && { + !this.effectiveOwner.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) { + // The cheaper check can be wrong: do the expensive normalization + // before failing. + if (this.sourceFile.canonicalPath != that.sourceFile.canonicalPath) + 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: 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 + * + * !!! linkedClassOfClass depends on companionClass on the module class getting + * to the class. As presently implemented this potentially returns class for + * any symbol except NoSymbol. + */ + def companionClass: Symbol = flatOwnerInfo.decl(name.toTypeName).suchThat(_ isCoDefinedWith this) + + /** 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 + */ + def companionModule: Symbol = 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 + */ + def companionSymbol: Symbol = NoSymbol + + /** For a module class: its linked class + * For a plain class: the module class of its linked module. + * + * class Foo <-- linkedClassOfClass --> class Foo$ + */ + def linkedClassOfClass: Symbol = NoSymbol + + /** + * 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. + */ + protected 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. + * !!! Why? + */ + def toInterface: Symbol = 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, in class `ofclazz`, that is overridden by this symbol. + * + * @param 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`. + * + * @param 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) + + /** Equivalent to allOverriddenSymbols.nonEmpty, but more efficient. */ + // !!! When if ever will this answer differ from .isOverride? + // How/where is the OVERRIDE flag managed, as compared to how checks + // based on type membership will evaluate? + def isOverridingSymbol = owner.isClass && ( + owner.ancestors exists (cls => matchingSymbol(cls, owner.thisType) != NoSymbol) + ) + /** Equivalent to allOverriddenSymbols.head (or NoSymbol if no overrides) but more efficient. */ + def nextOverriddenSymbol: Symbol = { + if (owner.isClass) owner.ancestors foreach { base => + val sym = overriddenSymbol(base) + if (sym != NoSymbol) + return sym + } + 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 = base.info.decl(getterName) filter (_.hasAccessorFlag) + + def getterName: TermName = ( + if (isSetter) nme.setterToGetter(name.toTermName) + else if (nme.isLocalName(name)) nme.localToGetter(name.toTermName) + else name.toTermName + ) + + /** 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.toTermName)) + if (hasExpandedName) sname = nme.expandedSetterName(sname, base) + base.info.decl(sname) filter (_.hasAccessorFlag) + } + + /** Return the accessor method of the first parameter of this class. + * or NoSymbol if it does not exist. + */ + def firstParamAccessor: Symbol = NoSymbol + + /** 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 + * is not a constructor nor a static module rename it by expanding its name to avoid name clashes + * @param base the fully qualified name of this class will be appended if name expansion is needed + */ + final def makeNotPrivate(base: Symbol) { + if (this.isPrivate) { + setFlag(notPRIVATE) + // Marking these methods final causes problems for proxies which use subclassing. If people + // write their code with no usage of final, we probably shouldn't introduce it ourselves + // unless we know it is safe. ... Unfortunately if they aren't marked final the inliner + // thinks it can't inline them. So once again marking lateFINAL, and in genjvm we no longer + // generate ACC_FINAL on "final" methods which are actually lateFINAL. + if (isMethod && !isDeferred) + setFlag(lateFINAL) + if (!isStaticModule && !isClassConstructor) { + expandName(base) + if (isModule) moduleClass.makeNotPrivate(base) + } + } + } + + /** Remove any access boundary and clear flags PROTECTED | PRIVATE. + */ + def makePublic = this setPrivateWithin NoSymbol resetFlag AccessFlags + + /** The first parameter to the first argument list of this method, + * or NoSymbol if inapplicable. + */ + def firstParam = info.params match { + case p :: _ => p + case _ => NoSymbol + } +/* code for fixing nested objects + def expandModuleClassName() { + name = newTypeName(name.toString + "$") + } + + def isExpandedModuleClass: Boolean = name(name.length - 1) == '$' +*/ + + /** Desire to re-use the field in ClassSymbol which stores the source + * file to also store the classfile, but without changing the behavior + * of sourceFile (which is expected at least in the IDE only to + * return actual source code.) So sourceFile has classfiles filtered out. + */ + private def sourceFileOnly(file: AbstractFileType): AbstractFileType = + if ((file eq null) || (file.path endsWith ".class")) null else file + + private def binaryFileOnly(file: AbstractFileType): AbstractFileType = + if ((file eq null) || !(file.path endsWith ".class")) null else file + + final def binaryFile: AbstractFileType = binaryFileOnly(associatedFile) + final def sourceFile: AbstractFileType = sourceFileOnly(associatedFile) + + /** Overridden in ModuleSymbols to delegate to the module class. */ + def associatedFile: AbstractFileType = enclosingTopLevelClass.associatedFile + def associatedFile_=(f: AbstractFileType) { abort("associatedFile_= inapplicable for " + this) } + + @deprecated("Use associatedFile_= instead", "2.10.0") + def sourceFile_=(f: AbstractFileType): Unit = associatedFile_=(f) + + /** 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 + + @inline final def orElse(alt: => Symbol): Symbol = if (this ne NoSymbol) this else alt + @inline final def andAlso(f: Symbol => Unit): Symbol = { if (this ne NoSymbol) f(this) ; this } + +// ------ toString ------------------------------------------------------------------- + + /** A tag which (in the ideal case) uniquely identifies class symbols */ + final def tag: Int = 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: String = initName + "#" + id + + /** String representation of symbol's definition key word */ + final def keyString: String = + if (isJavaInterface) "interface" + else if (isTrait && !isImplClass) "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 "" + + private case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) + private def symbolKind: SymbolKind = { + var kind = + if (isTermMacro) ("macro method", "macro method", "MAC") + else if (isInstanceOf[FreeTermSymbol]) ("free term", "free term", "FTE") + else if (isInstanceOf[FreeTypeSymbol]) ("free type", "free type", "FTY") + else if (isPackage) ("package", "package", "PK") + else if (isPackageClass) ("package class", "package", "PKC") + else if (isPackageObject) ("package object", "package", "PKO") + else if (isPackageObjectClass) ("package object class", "package", "PKOC") + else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") + else if (isRefinementClass) ("refinement class", "", "RC") + else if (isModule) ("module", "object", "MOD") + else if (isModuleClass) ("module class", "object", "MODC") + else if (isGetter) ("getter", if (isSourceMethod) "method" else "value", "GET") + else if (isSetter) ("setter", if (isSourceMethod) "method" else "value", "SET") + else if (isTerm && isLazy) ("lazy value", "lazy value", "LAZ") + else if (isVariable) ("field", "variable", "VAR") + else if (isImplClass) ("implementation class", "class", "IMPL") + else if (isTrait) ("trait", "trait", "TRT") + else if (isClass) ("class", "class", "CLS") + else if (isType) ("type", "type", "TPE") + else if (isClassConstructor && isPrimaryConstructor) ("primary constructor", "constructor", "PCTOR") + else if (isClassConstructor) ("constructor", "constructor", "CTOR") + else if (isSourceMethod) ("method", "method", "METH") + else if (isTerm) ("value", "value", "VAL") + else ("", "", "???") + if (isSkolem) kind = (kind._1, kind._2, kind._3 + "#SKO") + SymbolKind(kind._1, kind._2, kind._3) + } + + /** Accurate string representation of symbols' kind, suitable for developers. */ + final def accurateKindString: String = + symbolKind.accurate + + /** String representation of symbol's kind, suitable for the masses. */ + private def sanitizedKindString: String = + symbolKind.sanitized + + /** String representation of symbol's kind, suitable for the masses. */ + protected[scala] def abbreviatedKindString: String = + symbolKind.abbreviation + + 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 + || isRefinementClass // <refinement> + || (name == nme.PACKAGE) // package + ) + + /** 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. + * If settings.Yshowsymkinds, adds abbreviated symbol kind. + */ + def nameString: String = ( + if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + decodedName + else if (settings.uniqid.value && !settings.Yshowsymkinds.value) decodedName + "#" + id + else if (!settings.uniqid.value && settings.Yshowsymkinds.value) decodedName + "#" + abbreviatedKindString + else decodedName + "#" + id + "#" + abbreviatedKindString + ) + + def fullNameString: String = { + def recur(sym: Symbol): String = { + if (sym.isRootSymbol || sym == NoSymbol) sym.nameString + else if (sym.owner.isEffectiveRoot) sym.nameString + else recur(sym.effectiveOwner.enclClass) + "." + sym.nameString + } + + recur(this) + } + + /** 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: String = compose( + kindString, + if (hasMeaninglessName) owner.decodedName + idString else nameString + ) + + /** String representation of location. + */ + def ownsString: String = { + val owns = effectiveOwner + if (owns.isClass && !owns.isEmptyPrefix) "" + owns else "" + } + + /** String representation of location, plus a preposition. Doesn't do much, + * for backward compatibility reasons. + */ + def locationString: String = ownsString match { + case "" => "" + case s => " in " + s + } + def fullLocationString: String = toString + locationString + def signatureString: String = if (hasRawInfo) infoString(rawInfo) else "<_>" + + /** String representation of symbol's definition following its name */ + final def infoString(tp: Type): String = { + def parents = ( + if (settings.debug.value) parentsString(tp.parents) + else briefParentsString(tp.parents) + ) + if (isType) typeParamsString(tp) + ( + if (isClass) " extends " + parents + else if (isAliasType) " = " + tp.resultType + else tp.resultType match { + case rt @ TypeBounds(_, _) => "" + rt + case rt => " <: " + rt + } + ) + else if (isModule) "" // avoid "object X of type X.type" + else tp match { + case PolyType(tparams, res) => typeParamsString(tp) + infoString(res) + case NullaryMethodType(res) => infoString(res) + case MethodType(params, res) => valueParamsString(tp) + infoString(res) + case _ => ": " + tp + } + } + + def infosString = infos.toString + def debugLocationString = fullLocationString + " (flags: " + debugFlagString + ")" + + private def defStringCompose(infoString: String) = compose( + flagString, + keyString, + varianceString + nameString + infoString + flagsExplanationString + ) + /** String representation of symbol's definition. It uses the + * symbol's raw info to avoid forcing types. + */ + def defString = defStringCompose(signatureString) + + /** String representation of symbol's definition, using the supplied + * info rather than the symbol's. + */ + def defStringSeenAs(info: Type) = defStringCompose(infoString(info)) + + /** 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 " + tpnme.dropSingletonName(name) + ": " + dropSingletonType(info.bounds.hi) + else defString + } + implicit val SymbolTag = ClassTag[Symbol](classOf[Symbol]) + + /** A class for term symbols */ + class TermSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) + extends Symbol(initOwner, initPos, initName) with TermSymbolApi { + private[this] var _referenced: Symbol = NoSymbol + privateWithin = NoSymbol + + type TypeOfClonedSymbol = TermSymbol + + private[this] var _rawname: TermName = initName + def rawname = _rawname + def name = _rawname + def name_=(name: Name) { + if (name != rawname) { + log("Renaming %s %s %s to %s".format(shortSymbolClass, debugFlagString, rawname, name)) + changeNameInOwners(name) + _rawname = name.toTermName + } + } + final def asNameType(n: Name) = n.toTermName + + /** Term symbols with the exception of static parts of Java classes and packages. + */ + override def isValue = !(isModule && hasFlag(PACKAGE | JAVA)) + override def isVariable = isMutable && !isMethod + override def isTermMacro = hasFlag(MACRO) + + // interesting only for lambda lift. Captured variables are accessed from inner lambdas. + override def isCapturedVariable = hasAllFlags(MUTABLE | CAPTURED) && !hasFlag(METHOD) + + override def companionSymbol: Symbol = companionClass + override def moduleClass = if (isModule) referenced else NoSymbol + + override def hasDefault = this hasFlag DEFAULTPARAM // overloaded with TRAIT + override def isBridge = this hasFlag BRIDGE + override def isEarlyInitialized = this hasFlag PRESUPER + override def isMethod = this hasFlag METHOD + override def isModule = this hasFlag MODULE + override def isOverloaded = this hasFlag OVERLOADED + override def isPackage = this hasFlag PACKAGE + override def isValueParameter = this hasFlag PARAM + + override def isSetterParameter = isValueParameter && owner.isSetter + override def isAccessor = this hasFlag ACCESSOR + override def isGetter = isAccessor && !isSetter + override def isSetter = isAccessor && nme.isSetterName(name) // todo: make independent of name, as this can be forged. + override def isLocalDummy = nme.isLocalDummyName(name) + override def isClassConstructor = name == nme.CONSTRUCTOR + override def isMixinConstructor = name == nme.MIXIN_CONSTRUCTOR + override def isConstructor = nme.isConstructorName(name) + + override def isPackageObject = isModule && (name == nme.PACKAGE) + override def isStable = !isUnstable + private def isUnstable = ( + isMutable + || (hasFlag(METHOD | BYNAMEPARAM) && !hasFlag(STABLE)) + || (tpe.isVolatile && !hasAnnotation(uncheckedStableClass)) + ) + + // The name in comments is what it is being disambiguated from. + // TODO - rescue CAPTURED from BYNAMEPARAM so we can see all the names. + override def resolveOverloadedFlag(flag: Long) = flag match { + case DEFAULTPARAM => "<defaultparam>" // TRAIT + case MIXEDIN => "<mixedin>" // EXISTENTIAL + case LABEL => "<label>" // CONTRAVARIANT / INCONSTRUCTOR + case PRESUPER => "<presuper>" // IMPLCLASS + case BYNAMEPARAM => if (this.isValueParameter) "<bynameparam>" else "<captured>" // COVARIANT + case _ => super.resolveOverloadedFlag(flag) + } + + def referenced: Symbol = _referenced + def referenced_=(x: Symbol) { _referenced = x } + + def existentialBound = singletonBounds(this.tpe) + + def cloneSymbolImpl(owner: Symbol, newFlags: Long): TermSymbol = + owner.newTermSymbol(name, pos, newFlags).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 (originalName == nme.OUTER) initialize.referenced + else NoSymbol + + def setModuleClass(clazz: Symbol): TermSymbol = { + assert(isModule, this) + referenced = clazz + this + } + + def setLazyAccessor(sym: Symbol): TermSymbol = { + assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, debugFlagString, referenced, sym)) + referenced = sym + this + } + + override def lazyAccessor: Symbol = { + assert(isLazy, this) + referenced + } + + /** change name by appending $$<fully-qualified-name-of-class `base`> + * Do the same for any accessed symbols or setters/getters + */ + override def expandName(base: Symbol) { + if (!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.toTermName, base) + } + } + + protected def doCookJavaRawInfo() { + def cook(sym: Symbol) { + require(sym.isJavaDefined, sym) + // @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 (isOverloaded) + for (sym2 <- alternatives) + if (sym2.isJavaDefined) + cook(sym2) + } + } + implicit val TermSymbolTag = ClassTag[TermSymbol](classOf[TermSymbol]) + + /** A class for module symbols */ + class ModuleSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) + extends TermSymbol(initOwner, initPos, initName) with ModuleSymbolApi { + private var flatname: TermName = null + + override def associatedFile = moduleClass.associatedFile + override def associatedFile_=(f: AbstractFileType) { moduleClass.associatedFile = f } + + override def moduleClass = referenced + override def companionClass = + flatOwnerInfo.decl(name.toTypeName).suchThat(_ isCoDefinedWith this) + + override def owner = ( + if (!isMethod && needsFlatClasses) rawowner.owner + else rawowner + ) + override def name: TermName = ( + if (!isMethod && needsFlatClasses) { + if (flatname eq null) + flatname = nme.flattenedName(rawowner.name, rawname) + + flatname + } + else rawname + ) + } + implicit val ModuleSymbolTag = ClassTag[ModuleSymbol](classOf[ModuleSymbol]) + + /** A class for method symbols */ + class MethodSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) + extends TermSymbol(initOwner, initPos, initName) with MethodSymbolApi { + private[this] var mtpePeriod = NoPeriod + private[this] var mtpePre: Type = _ + private[this] var mtpeResult: Type = _ + private[this] var mtpeInfo: Type = _ + + override def isLabel = this hasFlag LABEL + override def isVarargsMethod = this hasFlag VARARGS + override def isLiftedMethod = this hasFlag LIFTED + + // TODO - this seems a strange definition for "isSourceMethod", given that + // it does not make any specific effort to exclude synthetics. Figure out what + // this method is really for and what logic makes sense. + override def isSourceMethod = !(this hasFlag STABLE) // exclude all accessors + // unfortunately having the CASEACCESSOR flag does not actually mean you + // are a case accessor (you can also be a field.) + override def isCaseAccessorMethod = isCaseAccessor + + 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 + } + } + implicit val MethodSymbolTag = ClassTag[MethodSymbol](classOf[MethodSymbol]) + + class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) + extends TypeSymbol(initOwner, initPos, initName) { + type TypeOfClonedSymbol = TypeSymbol + final override def isAliasType = true + final override def dealias = info.typeSymbol.dealias + override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSymbol = + owner.newNonClassSymbol(name, pos, newFlags) + } + + class AbstractTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) + extends TypeSymbol(initOwner, initPos, initName) { + type TypeOfClonedSymbol = TypeSymbol + final override def isAbstractType = true + override def existentialBound = this.info + override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSymbol = + owner.newNonClassSymbol(name, pos, newFlags) + } + + /** A class of type symbols. Alias and abstract types are direct instances + * of this class. Classes are instances of a subclass. + */ + abstract class TypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) + extends Symbol(initOwner, initPos, initName) with TypeSymbolApi { + privateWithin = NoSymbol + private[this] var _rawname: TypeName = initName + + type TypeOfClonedSymbol >: Null <: TypeSymbol + // cloneSymbolImpl still abstract in TypeSymbol. + + def rawname = _rawname + def name = _rawname + final def asNameType(n: Name) = n.toTypeName + + override def isNonClassType = true + override def isTypeMacro = hasFlag(MACRO) + + override def resolveOverloadedFlag(flag: Long) = flag match { + case TRAIT => "<trait>" // DEFAULTPARAM + case EXISTENTIAL => "<existential>" // MIXEDIN + case COVARIANT => "<covariant>" // BYNAMEPARAM / CAPTURED + case CONTRAVARIANT => "<contravariant>" // LABEL / INCONSTRUCTOR (overridden again in ClassSymbol) + case _ => super.resolveOverloadedFlag(flag) + } + + private var tyconCache: Type = null + private var tyconRunId = NoRunId + private var tpeCache: Type = _ + private var tpePeriod = NoPeriod + + override def isAbstractType = this hasFlag DEFERRED + override def isContravariant = this hasFlag CONTRAVARIANT + override def isCovariant = this hasFlag COVARIANT + override def isExistentialQuantified = isExistentiallyBound && !isSkolem + override def isExistentiallyBound = this hasFlag EXISTENTIAL + override def isTypeParameter = isTypeParameterOrSkolem && !isSkolem + override def isTypeParameterOrSkolem = this hasFlag PARAM + + /** Overridden in subclasses for which it makes sense. + */ + def existentialBound: Type = abort("unexpected type: "+this.getClass+ " "+debugLocationString) + + // TODO - don't allow names to be renamed in this unstructured a fashion. + // Rename as little as possible. Enforce invariants on all renames. + def name_=(name: Name) { + if (name != rawname) { + log("Renaming %s %s %s to %s".format(shortSymbolClass, debugFlagString, rawname, name)) + changeNameInOwners(name) + _rawname = name.toTypeName + } + } + + private def newPrefix = if (this hasFlag EXISTENTIAL | PARAM) NoPrefix else owner.thisType + private def newTypeRef(targs: List[Type]) = typeRef(newPrefix, 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 + } + + /** @M -- tpe vs tpeHK: + * + * tpe: creates a TypeRef with dummy type arguments and kind * + * tpeHK: creates a TypeRef with no type arguments but with type parameters + * + * If typeParams is nonEmpty, calling tpe may hide errors or + * introduce spurious ones. (For example, when deriving a type from + * the symbol of a type argument that may 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. + * + * TODO: the above conditions desperately need to be enforced by code. + */ + override def tpeHK = typeConstructor // @M! used in memberType + + 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) + } + + final override def isNonBottomSubClass(that: Symbol): Boolean = ( + (this eq that) || this.isError || that.isError || + info.baseTypeIndex(that) >= 0 + ) + + override def reset(completer: Type): this.type = { + super.reset(completer) + tpePeriod = NoPeriod + tyconRunId = NoRunId + this + } + + /*** example: + * public class Test3<T> {} + * public class Test1<T extends Test3> {} + * info for T in Test1 should be >: Nothing <: Test3[_] + */ + protected def doCookJavaRawInfo() { + if (isJavaDefined || owner.isJavaDefined) { + val tpe1 = rawToExistential(info) + // println("cooking type: "+ this +": "+ info +" to "+ tpe1) + if (tpe1 ne info) { + setInfo(tpe1) + } + } + } + + incCounter(typeSymbolCount) + } + implicit val TypeSymbolTag = ClassTag[TypeSymbol](classOf[TypeSymbol]) + + /** 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 believe the following invariant holds: + * + * origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) + */ + class TypeSkolem protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName, origin: AnyRef) + extends TypeSymbol(initOwner, initPos, initName) { + type TypeOfClonedSymbol = TypeSkolem + /** The skolemization level in place when the skolem was constructed */ + val level = skolemizationLevel + + final override def isSkolem = true + + // a type symbol bound by an existential type, for instance the T in + // List[T] forSome { type T } + override def isExistentialSkolem = this hasFlag EXISTENTIAL + override def isGADTSkolem = this hasFlag CASEACCESSOR | SYNTHETIC + override def isTypeSkolem = this hasFlag PARAM + override def isAbstractType = this hasFlag DEFERRED + + override def isExistentialQuantified = false + override def existentialBound = if (isAbstractType) this.info else super.existentialBound + + /** 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 + + //@M! (not deSkolemize.typeParams!!), also can't leave superclass definition: use info, not rawInfo + override def typeParams = info.typeParams + + override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSkolem = + owner.newTypeSkolemSymbol(name, origin, pos, newFlags) + + override def nameString: String = + if (settings.debug.value) (super.nameString + "&" + level) + else super.nameString + } + + /** A class for class symbols */ + class ClassSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) + extends TypeSymbol(initOwner, initPos, initName) with ClassSymbolApi { + type TypeOfClonedSymbol = ClassSymbol + + private[this] var flatname: TypeName = _ + private[this] var _associatedFile: AbstractFileType = _ + private[this] var thissym: Symbol = this + + private[this] var thisTypeCache: Type = _ + private[this] var thisTypePeriod = NoPeriod + + override def resolveOverloadedFlag(flag: Long) = flag match { + case INCONSTRUCTOR => "<inconstructor>" // INCONSTRUCTOR / CONTRAVARIANT / LABEL + case EXISTENTIAL => "<existential>" // EXISTENTIAL / MIXEDIN + case IMPLCLASS => "<implclass>" // IMPLCLASS / PRESUPER + case _ => super.resolveOverloadedFlag(flag) + } + + final override def isNonClassType = false + final override def isAbstractType = false + final override def isAliasType = false + + override def isAbstractClass = this hasFlag ABSTRACT + override def isCaseClass = this hasFlag CASE + override def isClassLocalToConstructor = this hasFlag INCONSTRUCTOR + override def isImplClass = this hasFlag IMPLCLASS + override def isModuleClass = this hasFlag MODULE + override def isPackageClass = this hasFlag PACKAGE + override def isTrait = this hasFlag TRAIT + + override def isAnonOrRefinementClass = isAnonymousClass || isRefinementClass + override def isAnonymousClass = name containsName tpnme.ANON_CLASS_NAME + override def isConcreteClass = !(this hasFlag ABSTRACT | TRAIT) + override def isJavaInterface = hasAllFlags(JAVA | TRAIT) + override def isNestedClass = !owner.isPackageClass + override def isNumericValueClass = definitions.isNumericValueClass(this) + override def isPackageObjectClass = isModuleClass && (name == tpnme.PACKAGE) + override def isPrimitiveValueClass = definitions.isPrimitiveValueClass(this) + + // The corresponding interface is the last parent by convention. + private def lastParent = if (tpe.parents.isEmpty) NoSymbol else tpe.parents.last.typeSymbol + override def toInterface: Symbol = ( + if (isImplClass) { + if (phase.next.erasedTypes) lastParent + else owner.info.decl(tpnme.interfaceName(name)) + } + else super.toInterface + ) + + /** 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 + */ + override def isLocalClass = ( + isAnonOrRefinementClass + || isLocal + || !owner.isPackageClass && owner.isLocalClass + ) + override def isStableClass = (this hasFlag STABLE) || checkStable() + + private def checkStable() = { + 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 + } + (info.baseClasses forall hasNoAbstractTypeMember) && { + setFlag(STABLE) + true + } + } + + override def enclClassChain = this :: owner.enclClassChain + + /** 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. + */ + protected final def companionModule0: Symbol = + flatOwnerInfo.decl(name.toTermName).suchThat( + sym => sym.hasFlag(MODULE) && (sym isCoDefinedWith this) && !sym.isMethod) + + override def companionModule = companionModule0 + override def companionSymbol = companionModule0 + override def linkedClassOfClass = companionModule.moduleClass + + override def sourceModule = if (isModuleClass) companionModule else NoSymbol + + override def existentialBound = GenPolyType(this.typeParams, TypeBounds.upper(this.classBound)) + + def primaryConstructorName = if (this hasFlag TRAIT | IMPLCLASS) nme.MIXIN_CONSTRUCTOR else nme.CONSTRUCTOR + + override def primaryConstructor = { + val c = info decl primaryConstructorName + if (c.isOverloaded) c.alternatives.head else c + } + + override def associatedFile = if (owner.isPackageClass) _associatedFile else super.associatedFile + override def associatedFile_=(f: AbstractFileType) { _associatedFile = f } + + override def reset(completer: Type): this.type = { + super.reset(completer) + thissym = this + this + } + + /** 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 + } + + override def owner: Symbol = + if (needsFlatClasses) rawowner.owner else rawowner + + override def name: TypeName = ( + if (needsFlatClasses) { + if (flatname eq null) + flatname = nme.flattenedName(rawowner.name, rawname).toTypeName + + flatname + } + else rawname + ) + + /** A symbol carrying the self type of the class as its type */ + override def thisSym: Symbol = thissym + + /** Sets the self type of the class */ + override def typeOfThis_=(tp: Type) { + thissym = newThisSym(nme.this_, pos).setInfo(tp) + } + + override def cloneSymbolImpl(owner: Symbol, newFlags: Long): ClassSymbol = { + val clone = owner.newClassSymbol(name, pos, newFlags) + if (thisSym != this) { + clone.typeOfThis = typeOfThis + clone.thisSym setName thisSym.name + } + if (_associatedFile ne null) + clone.associatedFile = _associatedFile + + clone + } + + override def firstParamAccessor = + info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse NoSymbol + + private[this] var childSet: Set[Symbol] = Set() + override def children = childSet + override def addChild(sym: Symbol) { childSet = childSet + sym } + + incCounter(classSymbolCount) + } + implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol]) + + /** A class for module class symbols + * Note: Not all module classes are of this type; when unpickled, we get + * plain class symbols! + */ + class ModuleClassSymbol protected[Symbols] (owner: Symbol, pos: Position, name: TypeName) + extends ClassSymbol(owner, pos, name) { + private[this] var module: Symbol = _ + private[this] var typeOfThisCache: Type = _ + private[this] var typeOfThisPeriod = NoPeriod + + private var implicitMembersCacheValue: List[Symbol] = Nil + private var implicitMembersCacheKey1: Type = NoType + private var implicitMembersCacheKey2: ScopeEntry = null + + override def isModuleClass = true + override def linkedClassOfClass = companionClass + + /** the self type of an object foo is foo.type, not class<foo>.this.type + */ + override def typeOfThis = { + val period = typeOfThisPeriod + if (period != currentPeriod) { + typeOfThisPeriod = currentPeriod + if (!isValid(period)) + typeOfThisCache = singleType(owner.thisType, sourceModule) + } + typeOfThisCache + } + + def implicitMembers: List[Symbol] = { + val tp = info + if ((implicitMembersCacheKey1 ne tp) || (implicitMembersCacheKey2 ne tp.decls.elems)) { + // Skip a package object class, because the members are also in + // the package and we wish to avoid spurious ambiguities as in pos/t3999. + if (!isPackageObjectClass) { + implicitMembersCacheKey1 = tp + implicitMembersCacheKey2 = tp.decls.elems + implicitMembersCacheValue = tp.implicitMembers + } + } + implicitMembersCacheValue + } + // The null check seems to be necessary for the reifier. + override def sourceModule = if (module ne null) module else companionModule + override def sourceModule_=(module: Symbol) { this.module = module } + } + + class PackageObjectClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position) + extends ModuleClassSymbol(owner0, pos0, tpnme.PACKAGE) { + final override def isPackageObjectClass = true + final override def isPackageObjectOrClass = true + final override def skipPackageObject = owner + final override def setName(name: Name): this.type = { + abort("Can't rename a package object to " + name) + } + } + + trait ImplClassSymbol extends ClassSymbol { + override def sourceModule = companionModule + // override def isImplClass = true + override def typeOfThis = thisSym.tpe // don't use the ModuleClassSymbol typeOfThisCache. + } + + class PackageClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position, name0: TypeName) + extends ModuleClassSymbol(owner0, pos0, name0) { + override def sourceModule = companionModule + override def enclClassChain = Nil + override def isPackageClass = true + } + + class RefinementClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position) + extends ClassSymbol(owner0, pos0, tpnme.REFINE_CLASS_NAME) { + override def name_=(name: Name) { + assert(false, "Cannot set name of RefinementClassSymbol to " + name) + super.name_=(name) + } + override def isRefinementClass = true + override def isAnonOrRefinementClass = true + override def isLocalClass = true + override def hasMeaninglessName = true + override def companionModule: Symbol = NoSymbol + + /** The mentioned twist. A refinement class has transowner X + * if any of its parents has transowner X. + */ + override def hasTransOwner(sym: Symbol) = ( + super.hasTransOwner(sym) + || info.parents.exists(_.typeSymbol hasTransOwner sym) + ) + } + + trait FreeSymbol extends Symbol { + def origin: String + } + class FreeTermSymbol(name0: TermName, value0: => Any, val origin: String) extends TermSymbol(NoSymbol, NoPosition, name0) with FreeSymbol with FreeTermSymbolApi { + def value = value0 + } + implicit val FreeTermSymbolTag = ClassTag[FreeTermSymbol](classOf[FreeTermSymbol]) + + class FreeTypeSymbol(name0: TypeName, value0: => Any, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) with FreeSymbol with FreeTypeSymbolApi { + def value = value0 + } + implicit val FreeTypeSymbolTag = ClassTag[FreeTypeSymbol](classOf[FreeTypeSymbol]) + + /** An object representing a missing symbol */ + class NoSymbol protected[Symbols]() extends Symbol(null, NoPosition, nme.NO_NAME) { + final type NameType = TermName + type TypeOfClonedSymbol = NoSymbol + + def asNameType(n: Name) = n.toTermName + def rawname = nme.NO_NAME + def name = nme.NO_NAME + def name_=(n: Name) = abort("Cannot set NoSymbol's name to " + n) + + synchronized { + setInfo(NoType) + privateWithin = this + } + override def info_=(info: Type) = { + infos = TypeHistory(1, NoType, null) + unlock() + validTo = currentPeriod + } + override def flagMask = AllFlags + override def exists = false + override def isHigherOrderTypeParameter = false + override def companionClass = NoSymbol + override def companionModule = NoSymbol + override def companionSymbol = NoSymbol + override def isSubClass(that: Symbol) = false + override def filter(cond: Symbol => Boolean) = this + override def defString: String = toString + override def locationString: String = "" + override def enclClassChain = Nil + override def enclClass: Symbol = this + override def enclosingTopLevelClass: Symbol = this + override def enclosingPackageClass: Symbol = this + override def enclMethod: Symbol = this + override def associatedFile = 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): this.type = this + override def info: Type = NoType + override def existentialBound: Type = NoType + override def rawInfo: Type = NoType + protected def doCookJavaRawInfo() {} + override def accessBoundary(base: Symbol): Symbol = enclosingRootClass + def cloneSymbolImpl(owner: Symbol, newFlags: Long) = abort("NoSymbol.clone()") + override def originalEnclosingMethod = this + + override def owner: Symbol = + abort("no-symbol does not have an owner") + override def typeConstructor: Type = + abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") + } + + protected def makeNoSymbol: NoSymbol = new NoSymbol + + lazy val NoSymbol: NoSymbol = makeNoSymbol + + /** Derives a new list of symbols from the given list by mapping the given + * list across the given function. Then fixes the info of all the new symbols + * by substituting the new symbols for the original symbols. + * + * @param syms the prototypical symbols + * @param symFn the function to create new symbols + * @return the new list of info-adjusted symbols + */ + def deriveSymbols(syms: List[Symbol], symFn: Symbol => Symbol): List[Symbol] = { + val syms1 = syms map symFn + syms1 map (_ substInfo (syms, syms1)) + } + + /** Derives a new Type by first deriving new symbols as in deriveSymbols, + * then performing the same oldSyms => newSyms substitution on `tpe` as is + * performed on the symbol infos in deriveSymbols. + * + * @param syms the prototypical symbols + * @param symFn the function to create new symbols + * @param tpe the prototypical type + * @return the new symbol-subsituted type + */ + def deriveType(syms: List[Symbol], symFn: Symbol => Symbol)(tpe: Type): Type = { + val syms1 = deriveSymbols(syms, symFn) + tpe.substSym(syms, syms1) + } + /** Derives a new Type by instantiating the given list of symbols as + * WildcardTypes. + * + * @param syms the symbols to replace + * @return the new type with WildcardType replacing those syms + */ + def deriveTypeWithWildcards(syms: List[Symbol])(tpe: Type): Type = { + if (syms.isEmpty) tpe + else tpe.instantiateTypeParams(syms, syms map (_ => WildcardType)) + } + /** Convenience functions which derive symbols by cloning. + */ + def cloneSymbols(syms: List[Symbol]): List[Symbol] = + deriveSymbols(syms, _.cloneSymbol) + def cloneSymbolsAtOwner(syms: List[Symbol], owner: Symbol): List[Symbol] = + deriveSymbols(syms, _ cloneSymbol owner) + + /** Clone symbols and apply the given function to each new symbol's info. + * + * @param syms the prototypical symbols + * @param infoFn the function to apply to the infos + * @return the newly created, info-adjusted symbols + */ + def cloneSymbolsAndModify(syms: List[Symbol], infoFn: Type => Type): List[Symbol] = + cloneSymbols(syms) map (_ modifyInfo infoFn) + def cloneSymbolsAtOwnerAndModify(syms: List[Symbol], owner: Symbol, infoFn: Type => Type): List[Symbol] = + cloneSymbolsAtOwner(syms, owner) map (_ modifyInfo infoFn) + + /** Functions which perform the standard clone/substituting on the given symbols and type, + * then call the creator function with the new symbols and type as arguments. + */ + def createFromClonedSymbols[T](syms: List[Symbol], tpe: Type)(creator: (List[Symbol], Type) => T): T = { + val syms1 = cloneSymbols(syms) + creator(syms1, tpe.substSym(syms, syms1)) + } + def createFromClonedSymbolsAtOwner[T](syms: List[Symbol], owner: Symbol, tpe: Type)(creator: (List[Symbol], Type) => T): T = { + val syms1 = cloneSymbolsAtOwner(syms, owner) + creator(syms1, tpe.substSym(syms, syms1)) + } + + /** A deep map on a symbol's paramss. + */ + def mapParamss[T](sym: Symbol)(f: Symbol => T): List[List[T]] = mmap(sym.info.paramss)(f) + + /** An exception for cyclic references of symbol definitions */ + case class CyclicReference(sym: Symbol, info: Type) + extends TypeError("illegal cyclic reference involving " + sym) { + if (settings.debug.value) printStackTrace() + } + + case class InvalidCompanions(sym1: Symbol, sym2: Symbol) extends Throwable({ + import language.reflectiveCalls + "Companions '" + sym1 + "' and '" + sym2 + "' must be defined in same file:\n" + + " Found in " + sym1.sourceFile.canonicalPath + " and " + sym2.sourceFile.canonicalPath + }) { + 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, this) + + override def toString() = + "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" + + def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) + } +} diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala new file mode 100644 index 0000000000..c3a6fce164 --- /dev/null +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -0,0 +1,280 @@ +package scala.reflect +package internal + +abstract class TreeGen extends makro.TreeBuilder { + 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 scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name) + def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) setSymbol AnyRefClass + def scalaUnitConstr = scalaDot(tpnme.Unit) setSymbol UnitClass + def productConstr = scalaDot(tpnme.Product) setSymbol ProductRootClass + def serializableConstr = scalaDot(tpnme.Serializable) setSymbol SerializableClass + + 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) + } + + /** A creator for method calls, e.g. fn[T1, T2, ...](v1, v2, ...) + * There are a number of variations. + * + * @param receiver symbol of the method receiver + * @param methodName name of the method to call + * @param targs type arguments (if Nil, no TypeApply node will be generated) + * @param args value arguments + * @return the newly created trees. + */ + def mkMethodCall(receiver: Symbol, methodName: Name, targs: List[Type], args: List[Tree]): Tree = + mkMethodCall(Select(mkAttributedRef(receiver), methodName), targs, args) + def mkMethodCall(method: Symbol, targs: List[Type], args: List[Tree]): Tree = + mkMethodCall(mkAttributedRef(method), targs, args) + def mkMethodCall(method: Symbol, args: List[Tree]): Tree = + mkMethodCall(method, Nil, args) + def mkMethodCall(target: Tree, args: List[Tree]): Tree = + mkMethodCall(target, Nil, args) + def mkMethodCall(receiver: Symbol, methodName: Name, args: List[Tree]): Tree = + mkMethodCall(receiver, methodName, Nil, args) + def mkMethodCall(receiver: Tree, method: Symbol, targs: List[Type], args: List[Tree]): Tree = + mkMethodCall(Select(receiver, method), targs, args) + + def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = + Apply(mkTypeApply(target, targs map TypeTree), args) + + def mkNullaryCall(method: Symbol, targs: List[Type]): Tree = + mkTypeApply(mkAttributedRef(method), targs map TypeTree) + + /** 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 = { + def failMessage = "mkAttributedQualifier(" + tpe + ", " + termSym + ")" + tpe match { + case NoPrefix => + EmptyTree + case ThisType(clazz) => + if (clazz.isEffectiveRoot) EmptyTree + else mkAttributedThis(clazz) + case SingleType(pre, sym) => + mkApplyIfNeeded(mkAttributedStableRef(pre, sym)) + case TypeRef(pre, sym, args) => + if (sym.isRoot) { + mkAttributedThis(sym) + } else if (sym.isModuleClass) { + mkApplyIfNeeded(mkAttributedRef(pre, sym.sourceModule)) + } else if (sym.isModule || sym.isClass) { + assert(phase.erasedTypes, failMessage) + mkAttributedThis(sym) + } else if (sym.isType) { + assert(termSym != NoSymbol, failMessage) + 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, failMessage + " parents = " + parents) + mkAttributedQualifier(firstStable.get) + + case _ => + abort("bad qualifier received: " + failMessage) + } + } + /** If this is a reference to a method with an empty + * parameter list, wrap it in an apply. + */ + def mkApplyIfNeeded(qual: Tree) = qual.tpe match { + case MethodType(Nil, restpe) => atPos(qual.pos)(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 + } + + /** 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.effectiveOwner == qual.tpe.typeSymbol) { + val obj = sym.owner.sourceModule + Select(qual, nme.PACKAGE) 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) + } + } + + /** Builds a type application node if args.nonEmpty, returns fun otherwise. */ + def mkTypeApply(fun: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) fun else TypeApply(fun, targs) + def mkTypeApply(target: Tree, method: Symbol, targs: List[Type]): Tree = + mkTypeApply(Select(target, method), targs map TypeTree) + def mkAttributedTypeApply(target: Tree, method: Symbol, targs: List[Type]): Tree = + mkTypeApply(mkAttributedSelect(target, method), targs map TypeTree) + + private def mkSingleTypeApply(value: Tree, tpe: Type, what: Symbol, wrapInApply: Boolean) = { + val tapp = mkAttributedTypeApply(value, what, List(tpe.normalize)) + if (wrapInApply) Apply(tapp, Nil) else tapp + } + private def typeTestSymbol(any: Boolean) = if (any) Any_isInstanceOf else Object_isInstanceOf + private def typeCastSymbol(any: Boolean) = if (any) Any_asInstanceOf else Object_asInstanceOf + + /** Builds an instance test with given value and type. */ + def mkIsInstanceOf(value: Tree, tpe: Type, any: Boolean = true, wrapInApply: Boolean = true): Tree = + mkSingleTypeApply(value, tpe, typeTestSymbol(any), wrapInApply) + + /** Builds a cast with given value and type. */ + def mkAsInstanceOf(value: Tree, tpe: Type, any: Boolean = true, wrapInApply: Boolean = true): Tree = + mkSingleTypeApply(value, tpe, typeCastSymbol(any), wrapInApply) + + /** 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)) tree + else atPos(tree.pos)(mkAsInstanceOf(tree, pt, any = true, wrapInApply = !beforeRefChecks)) + + /** Apparently we smuggle a Type around as a Literal(Constant(tp)) + * and the implementation of Constant#tpe is such that x.tpe becomes + * ClassType(value.asInstanceOf[Type]), i.e. java.lang.Class[Type]. + * Can't find any docs on how/why it's done this way. See ticket + * SI-490 for some interesting comments from lauri alanko suggesting + * that the type given by classOf[T] is too strong and should be + * weakened so as not to suggest that classOf[List[String]] is any + * different from classOf[List[Int]]. + * + * !!! See deconstMap in Erasure for one bug this encoding has induced: + * I would be very surprised if there aren't more. + */ + def mkClassOf(tp: Type): Tree = + Literal(Constant(tp)) setType ConstantType(Constant(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 = tp.typeSymbol match { + case NothingClass => mkMethodCall(Predef_???, Nil) setType NothingClass.tpe + case _ => Literal(mkConstantZero(tp)) setType tp + } + + def mkConstantZero(tp: Type): Constant = tp.typeSymbol match { + case UnitClass => Constant(()) + case BooleanClass => Constant(false) + case FloatClass => Constant(0.0f) + case DoubleClass => Constant(0.0d) + case ByteClass => Constant(0.toByte) + case ShortClass => Constant(0.toShort) + case IntClass => Constant(0) + case LongClass => Constant(0L) + case CharClass => Constant(0.toChar) + case _ => Constant(null) + } + + /** Builds a tuple */ + def mkTuple(elems: List[Tree]): Tree = + if (elems.isEmpty) Literal(Constant()) + 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)) + + def mkBasisUniverseRef: Tree = + mkAttributedRef(ReflectBasis) setType singleType(ReflectBasis.owner.thisPrefix, ReflectBasis) + + def mkRuntimeUniverseRef: Tree = { + assert(ReflectRuntimeUniverse != NoSymbol) + mkAttributedRef(ReflectRuntimeUniverse) setType singleType(ReflectRuntimeUniverse.owner.thisPrefix, ReflectRuntimeUniverse) + } +} diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala new file mode 100644 index 0000000000..4b2105876d --- /dev/null +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -0,0 +1,571 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import Flags._ + +/** This class ... + * + * @author Martin Odersky + * @version 1.0 + */ +abstract class TreeInfo { + val global: SymbolTable + + import global._ + import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass } + + /* Does not seem to be used. Not sure what it does anyway. + def isOwnerDefinition(tree: Tree): Boolean = tree match { + case PackageDef(_, _) + | ClassDef(_, _, _, _) + | ModuleDef(_, _, _) + | DefDef(_, _, _, _, _, _) + | Import(_, _) => true + case _ => false + } +*/ + + // def isDefinition(tree: Tree): Boolean = tree.isDef + + /** Is tree a declaration or type definition? + */ + def isDeclarationOrTypeDef(tree: Tree): Boolean = tree match { + case x: ValOrDefDef => x.rhs eq EmptyTree + case _ => tree.isInstanceOf[TypeDef] + } + + /** 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 _ => 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 && isExprSafeToInline(rhs) + case _ => + false + } + + /** Is tree an expression which can be inlined without affecting program semantics? + * + * Note that this is not called "isExprSafeToInline" since purity (lack of side-effects) + * is not the litmus test. References to modules and lazy vals are side-effecting, + * both because side-effecting code may be executed and because the first reference + * takes a different code path than all to follow; but they are safe to inline + * because the expression result from evaluating them is always the same. + */ + def isExprSafeToInline(tree: Tree): Boolean = tree match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol.isStable + // this case is mostly to allow expressions like -5 and +7, but any + // member of an anyval should be safely pure + case Select(Literal(const), name) => + const.isAnyVal && (const.tpe.member(name) != NoSymbol) + case Select(qual, _) => + tree.symbol.isStable && isExprSafeToInline(qual) + case TypeApply(fn, _) => + isExprSafeToInline(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 && isExprSafeToInline(fn) + case Typed(expr, _) => + isExprSafeToInline(expr) + case Block(stats, expr) => + (stats forall isPureDef) && isExprSafeToInline(expr) + case _ => + false + } + + @deprecated("Use isExprSafeToInline instead", "2.10.0") + def isPureExpr(tree: Tree) = isExprSafeToInline(tree) + + def zipMethodParamsAndArgs(params: List[Symbol], args: List[Tree]): List[(Symbol, Tree)] = + mapMethodParamsAndArgs(params, args)((param, arg) => ((param, arg))) + + def mapMethodParamsAndArgs[R](params: List[Symbol], args: List[Tree])(f: (Symbol, Tree) => R): List[R] = { + val b = List.newBuilder[R] + foreachMethodParamAndArg(params, args)((param, arg) => b += f(param, arg)) + b.result + } + def foreachMethodParamAndArg(params: List[Symbol], args: List[Tree])(f: (Symbol, Tree) => Unit): Boolean = { + val plen = params.length + val alen = args.length + def fail() = { + global.debugwarn( + "Mismatch trying to zip method parameters and argument list:\n" + + " params = " + params + "\n" + + " args = " + args + "\n" + ) + false + } + + if (plen == alen) foreach2(params, args)(f) + else if (params.isEmpty) return fail + else if (isVarArgsList(params)) { + val plenInit = plen - 1 + if (alen == plenInit) { + if (alen == 0) Nil // avoid calling mismatched zip + else foreach2(params.init, args)(f) + } + else if (alen < plenInit) return fail + else { + foreach2(params.init, args take plenInit)(f) + val remainingArgs = args drop plenInit + foreach2(List.fill(remainingArgs.size)(params.last), remainingArgs)(f) + } + } + else return fail + + true + } + + /** + * Selects the correct parameter list when there are nested applications. + * Given Apply(fn, args), args might correspond to any of fn.symbol's parameter + * lists. To choose the correct one before uncurry, we have to unwrap any + * applies: for instance Apply(fn @ Apply(Apply(_, _), _), args) implies args + * correspond to the third parameter list. + * + * The argument fn is the function part of the apply node being considered. + * + * Also accounts for varargs. + */ + private def applyMethodParameters(fn: Tree): List[Symbol] = { + val depth = applyDepth(fn) + // There could be applies which go beyond the parameter list(s), + // being applied to the result of the method call. + // !!! Note that this still doesn't seem correct, although it should + // be closer than what it replaced. + if (depth < fn.symbol.paramss.size) fn.symbol.paramss(depth) + else if (fn.symbol.paramss.isEmpty) Nil + else fn.symbol.paramss.last + } + + def zipMethodParamsAndArgs(t: Tree): List[(Symbol, Tree)] = t match { + case Apply(fn, args) => zipMethodParamsAndArgs(applyMethodParameters(fn), args) + case _ => Nil + } + def foreachMethodParamAndArg(t: Tree)(f: (Symbol, Tree) => Unit): Unit = t match { + case Apply(fn, args) => foreachMethodParamAndArg(applyMethodParameters(fn), args)(f) + case _ => + } + + /** Is symbol potentially a getter of a variable? + */ + def mayBeVarGetter(sym: Symbol): Boolean = sym.info match { + case NullaryMethodType(_) => sym.owner.isClass && !sym.isStable + case PolyType(_, NullaryMethodType(_)) => sym.owner.isClass && !sym.isStable + case mt @ MethodType(_, _) => mt.isImplicit && sym.owner.isClass && !sym.isStable + case _ => false + } + + /** Is tree a mutable variable, or the getter of a mutable field? + */ + def isVariableOrGetter(tree: Tree) = { + def sym = tree.symbol + def isVar = sym.isVariable + def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != 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 this(...)? I.e. a call to a constructor of the + * same object? + */ + def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match { + case Ident(nme.CONSTRUCTOR) + | Select(This(_), nme.CONSTRUCTOR) => true + case _ => false + } + + /** Is tree a super constructor call? + */ + def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { + case Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false + } + + /** + * Named arguments can transform a constructor call into a block, e.g. + * <init>(b = foo, a = bar) + * is transformed to + * { val x$1 = foo + * val x$2 = bar + * <init>(x$2, x$1) + * } + */ + def stripNamedApplyBlock(tree: Tree) = tree match { + case Block(stats, expr) if stats.forall(_.isInstanceOf[ValDef]) => + expr + case _ => + tree + } + + /** Is tree a self or super constructor call? */ + def isSelfOrSuperConstrCall(tree: Tree) = { + // stripNamedApply for SI-3584: adaptToImplicitMethod in Typers creates a special context + // for implicit search in constructor calls, adaptToImplicitMethod(isSelfOrConstrCall) + val tree1 = stripNamedApplyBlock(tree) + isSelfConstrCall(tree1) || isSuperConstrCall(tree1) + } + + /** Is tree a variable pattern? */ + def isVarPattern(pat: Tree): Boolean = pat match { + case x: Ident => !x.isBackquoted && isVariableName(x.name) + case _ => false + } + def isDeprecatedIdentifier(tree: Tree): Boolean = tree match { + case x: Ident => !x.isBackquoted && nme.isDeprecatedIdentifierName(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 a vararg type 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 of a method definition that have vararg types 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 of the form => T? */ + 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 tree comprised of nothing but identifiers, + * but possibly in bindings or tuples? For instance + * + * foo @ (bar, (baz, quux)) + * + * is a variable pattern; if the structure matches, + * then the remainder is inevitable. + */ + def isVariablePattern(tree: Tree): Boolean = tree match { + case Bind(name, pat) => isVariablePattern(pat) + case Ident(name) => true + case Apply(sel, args) => + ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) + && (args forall isVariablePattern) + ) + 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 + } + + /** If this tree represents a type application (after unwrapping + * any applies) the first type argument. Otherwise, EmptyTree. + */ + def firstTypeArg(tree: Tree): Tree = tree match { + case Apply(fn, _) => firstTypeArg(fn) + case TypeApply(_, targ :: _) => targ + case _ => EmptyTree + } + + /** If this tree has type parameters, those. Otherwise Nil. + */ + def typeParameters(tree: Tree): List[TypeDef] = tree match { + case DefDef(_, _, tparams, _, _, _) => tparams + case ClassDef(_, _, tparams, _) => tparams + case TypeDef(_, _, tparams, _) => tparams + case _ => Nil + } + + /** Does this argument list end with an argument of the form <expr> : _* ? */ + def isWildcardStarArgList(trees: List[Tree]) = + trees.nonEmpty && isWildcardStarArg(trees.last) + + /** Is the argument a wildcard argument of the form `_` or `x @ _`? + */ + 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 + } + + /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _) + * has depth 3. Continues through type applications (without counting them.) + */ + def applyDepth(tree: Tree): Int = tree match { + case Apply(fn, _) => 1 + applyDepth(fn) + case TypeApply(fn, _) => applyDepth(fn) + case AppliedTypeTree(fn, _) => applyDepth(fn) + case _ => 0 + } + 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 + } + + /** Does list of trees start with a definition of + * a class of module with given name (ignoring imports) + */ + def firstDefinesClassOrObject(trees: List[Tree], name: Name): Boolean = trees match { + case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name) + case Annotated(_, tree1) :: Nil => firstDefinesClassOrObject(List(tree1), name) + case ModuleDef(_, `name`, _) :: Nil => true + case ClassDef(_, `name`, _, _) :: Nil => 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 => isReferenceToPredef(expr) || containsLeadingPredefImport(rest) + 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) => firstDefinesClassOrObject(defs, name) + case _ => false + } + + ( isUnitInScala(body, nme.Predef) + || 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 trees through the + * the haze of irrelevant braces: i.e. Block(Nil, SomeTree) + * should not keep us from seeing SomeTree. + */ + 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 match { + case Literal(Constant(true)) => true + case _ => false + } + } + object IsFalse extends SeeThroughBlocks[Boolean] { + protected def unapplyImpl(x: Tree): Boolean = x match { + case Literal(Constant(false)) => true + case _ => false + } + } + object IsIf extends SeeThroughBlocks[Option[(Tree, Tree, Tree)]] { + protected def unapplyImpl(x: Tree) = x match { + case If(cond, thenp, elsep) => Some((cond, thenp, elsep)) + case _ => None + } + } + + def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed) + + class DynamicApplicationExtractor(nameTest: Name => Boolean) { + def unapply(tree: Tree) = tree match { + case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree, name)) + case _ => None + } + } + object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) + object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) + object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) +} diff --git a/src/reflect/scala/reflect/internal/TreePrinters.scala b/src/reflect/scala/reflect/internal/TreePrinters.scala new file mode 100644 index 0000000000..6d035c8b9d --- /dev/null +++ b/src/reflect/scala/reflect/internal/TreePrinters.scala @@ -0,0 +1,478 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +// [Eugene++ to Martin] we need to unify this prettyprinter with NodePrinters + +package scala.reflect +package internal + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import Flags._ + +trait TreePrinters extends api.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) + def quotedName(name: String): String = quotedName(newTermName(name), false) + + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + val sym = tree.symbol + if (sym.name.toString == nme.ERROR.toString) { + "<" + quotedName(name, decoded) + ": error>" + } else if (sym != null && sym != NoSymbol) { + val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" + var suffix = "" + if (settings.uniqid.value) suffix += ("#" + sym.id) + if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) + prefix + quotedName(tree.symbol.decodedName) + suffix + } else { + quotedName(name, decoded) + } + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + + /** Turns a path into a String, introducing backquotes + * as necessary. + */ + def backquotedPath(t: Tree): String = { + t match { + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) + case Ident(name) => symName(t, name) + case _ => t.toString + } + } + + class TreePrinter(out: PrintWriter) extends super.TreePrinter { + protected var indentMargin = 0 + protected val indentStep = 2 + protected var indentString = " " // 40 + + typesPrinted = settings.printtypes.value + uniqueIds = settings.uniqid.value + protected def doPrintPositions = settings.Xprintpos.value + + def indent() = indentMargin += indentStep + def undent() = indentMargin -= indentStep + + def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) + + 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 printLabelParams(ps: List[Ident]) { + print("(") + printSeq(ps){printLabelParam}{print(", ")} + print(")") + } + + def printLabelParam(p: Ident) { + print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) + } + + 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) + + def printOpt(prefix: String, tree: Tree) { + if (!tree.isEmpty) { print(prefix, 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) { + if (!isCompilerUniverse && tree.symbol != null && tree.symbol != NoSymbol) + // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing + // the problem is that getting annotations doesn't automatically initialize the symbol + // so we might easily print something as if it doesn't have annotations, whereas it does + tree.symbol.initialize + + val annots = tree.symbol.annotations match { + case Nil => tree.asInstanceOf[MemberDef].mods.annotations + case anns => anns + } + annots foreach (annot => print("@"+annot+" ")) + } + + private var currentOwner: Symbol = NoSymbol + private var selectorType: Type = NoType + + def printTree(tree: Tree) { + tree match { + case EmptyTree => + print("<empty>") + + case ClassDef(mods, name, tparams, impl) => + printAnnotations(tree) + printModifiers(tree, mods) + val word = + if (mods.isTrait) "trait" + else if (ifSym(tree, _.isModuleClass)) "object" + else "class" + + print(word, " ", symName(tree, name)) + printTypeParams(tparams) + print(if (mods.isDeferred) " <: " else " extends ", impl) + + case PackageDef(packaged, stats) => + printAnnotations(tree) + print("package ", packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printAnnotations(tree) + printModifiers(tree, mods); + print("object " + symName(tree, name), " extends ", impl) + + case ValDef(mods, name, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print(if (mods.isMutable) "var " else "val ", symName(tree, name)) + printOpt(": ", tp) + if (!mods.isDeferred) + print(" = ", if (rhs.isEmpty) "_" else 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)); printLabelParams(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 ", backquotedPath(expr), ".") + 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("{", selectorToString(s), "}") + // If there is more than one selector braces are always needed + case many => + print(many.map(selectorToString).mkString("{", ", ", "}")) + } + + case Template(parents, self, body) => + val currentOwner1 = currentOwner + if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner +// if (parents exists isReferenceToAnyVal) { +// print("AnyVal") +// } +// else { + printRow(parents, " with ") + if (!body.isEmpty) { + if (self.name != nme.WILDCARD) { + print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") + } else if (!self.tpt.isEmpty) { + print(" { _ : ", self.tpt, " => ") + } 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(" => ", body) + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("(", elem, ")*") + + case Bind(name, t) => + print("(", symName(tree, name), " @ ", t, ")") + + case UnApply(fun, args) => + print(fun, " <unapply> "); printRow(args, "(", ", ", ")") + + case ArrayValue(elemtpt, trees) => + print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => ", body, ")") + if (uniqueIds && tree.symbol != null) print("#"+tree.symbol.id) + + case Assign(lhs, rhs) => + print(lhs, " = ", rhs) + + case AssignOrNamedArg(lhs, rhs) => + print(lhs, " = ", rhs) + + case If(cond, thenp, elsep) => + print("if (", cond, ")"); indent; println() + print(thenp); undent + if (!elsep.isEmpty) { + println(); print("else"); indent; println(); print(elsep); undent + } + + case Return(expr) => + print("return ", expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block) + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw ", expr) + + case New(tpe) => + print("new ", tpe) + + case Typed(expr, tp) => + print("(", expr, ": ", tp, ")") + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]") + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")") + + case ApplyDynamic(qual, vargs) => + print("<apply-dynamic>(", qual, "#", 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, ".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), ".", symName(tree, name)) + + case id @ Ident(name) => + val str = symName(tree, name) + print( if (id.isBackquoted) "`" + str + "`" else str ) + + case Literal(x) => + print(x.escapedStringValue) + + case tt: TypeTree => + if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { + if (tt.original != null) print("<type: ", tt.original, ">") + 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("@", tpt) + if (!args.isEmpty) + printRow(args, "(", ",", ")") + } + print(tree, if (tree.isType) " " else ": ") + printAnnot() + + case SingletonTypeTree(ref) => + print(ref, ".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier, "#", 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 { ", ";", "}") + +// SelectFromArray is no longer visible in reflect.internal. +// eliminated until we figure out what we will do with both TreePrinters and +// SelectFromArray. +// case SelectFromArray(qualifier, name, _) => +// print(qualifier); print(".<arr>"); print(symName(tree, name)) + + case tree => + xprintTree(this, tree) + } + if (typesPrinted && tree.isTerm && !tree.isEmpty) { + print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") + } + } + + def print(args: Any*): Unit = args foreach { + case tree: Tree => + printPosition(tree) + printTree(tree) + case name: Name => + print(quotedName(name)) + case arg => + out.print(if (arg == null) "null" else arg.toString) + } + } + + /** Hook for extensions */ + def xprintTree(treePrinter: TreePrinter, tree: Tree) = + treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + + 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/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala new file mode 100644 index 0000000000..11d0790100 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -0,0 +1,1592 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import Flags._ +import base.Attachments +import collection.mutable.{ListBuffer, LinkedHashSet} + +trait Trees extends api.Trees { self: SymbolTable => + + private[scala] var nodeCount = 0 + + abstract class Tree extends TreeContextApiImpl with Product { + val id = nodeCount // TODO: add to attachment? + nodeCount += 1 + + @inline final def pos: Position = rawatt.pos + def pos_=(pos: Position): Unit = rawatt = (rawatt withPos pos) + def setPos(newpos: Position): this.type = { pos = newpos; this } + + private var rawatt: Attachments { type Pos = Position } = NoPosition + def attachments = rawatt + def addAttachment(attachment: Any): this.type = { rawatt = rawatt.add(attachment); this } + def removeAttachment[T: ClassTag]: this.type = { rawatt = rawatt.remove[T]; this } + + private[this] var rawtpe: Type = _ + @inline final def tpe = rawtpe + def tpe_=(t: Type) = rawtpe = t + def setType(tp: Type): this.type = { rawtpe = tp; this } + 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 + + /** The canonical way to test if a Tree represents a term. + */ + 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 _ => false + } + + /** The canonical way to test if a Tree represents a type. + */ + 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 _ => false + } + + private[scala] def copyAttrs(tree: Tree): this.type = { + rawatt = tree.rawatt + tpe = tree.tpe + if (hasSymbol) symbol = tree.symbol + this + } + + override def hashCode(): Int = System.identityHashCode(this) + override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + + override def duplicate: this.type = + (duplicator transform this).asInstanceOf[this.type] + } + + abstract class TreeContextApiImpl extends TreeContextApi { this: Tree => + + override def orElse(alt: => Tree) = if (!isEmpty) this else alt + + override def foreach(f: Tree => Unit) { new ForeachTreeTraverser(f).traverse(this) } + + override def withFilter(f: Tree => Boolean): List[Tree] = { + val ft = new FilterTreeTraverser(f) + ft.traverse(this) + ft.hits.toList + } + + override def filter(f: Tree => Boolean): List[Tree] = withFilter(f) + + override def collect[T](pf: PartialFunction[Tree, T]): List[T] = { + val ctt = new CollectTreeTraverser[T](pf) + ctt.traverse(this) + ctt.results.toList + } + + override def find(p: Tree => Boolean): Option[Tree] = { + val ft = new FindTreeTraverser(p) + ft.traverse(this) + ft.result + } + + override def exists(p: Tree => Boolean): Boolean = !find(p).isEmpty + + override def forAll(p: Tree => Boolean): Boolean = find(!p(_)).isEmpty + + override def equalsStructure(that : Tree) = correspondsStructure(that)(_ eq _) + + def correspondsStructure(that: Tree)(f: (Tree,Tree) => Boolean): Boolean = + f(this, that) || ((productArity == that.productArity) && { + def equals0(this0: Any, that0: Any): Boolean = (this0, that0) match { + case (x: Tree, y: Tree) => f(x, y) || (x correspondsStructure 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 correspondsStructure y.original)(f) + case _ => + true + } + + (productIterator zip that.productIterator forall { case (x, y) => equals0(x, y) }) && compareOriginals() + }) + + override 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 + } + + override def freeTerms: List[FreeTermSymbol] = freeSyms[FreeTermSymbol](_.isFreeTerm, _.termSymbol) + override def freeTypes: List[FreeTypeSymbol] = freeSyms[FreeTypeSymbol](_.isFreeType, _.typeSymbol) + + private def freeSyms[S <: Symbol](isFree: Symbol => Boolean, symOfType: Type => Symbol): List[S] = { + val s = collection.mutable.LinkedHashSet[S]() + def addIfFree(sym: Symbol): Unit = if (sym != null && isFree(sym)) s += sym.asInstanceOf[S] + for (t <- this) { + addIfFree(t.symbol) + if (t.tpe != null) { + for (tp <- t.tpe) { + addIfFree(symOfType(tp)) + } + } + } + s.toList + } + + override def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree = + new TreeSymSubstituter(from, to)(this) + + override def substituteTypes(from: List[Symbol], to: List[Type]): Tree = + new TreeTypeSubstituter(from, to)(this) + + override def substituteThis(clazz: Symbol, to: Tree): Tree = + new ThisSubstituter(clazz, to) transform this + + def hasSymbolWhich(f: Symbol => Boolean) = + hasSymbol && symbol != null && f(symbol) + + def isErroneous = (tpe ne null) && tpe.isErroneous + def isTyped = (tpe ne null) && !tpe.isErroneous + + /** Sets the tree's type to the result of the given function. + * If the type is null, it remains null - the function is not called. + */ + def modifyType(f: Type => Type): Tree = + if (tpe eq null) this + else this setType f(tpe) + + /** 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) + } + + def changeOwner(pairs: (Symbol, Symbol)*): Tree = { + pairs.foldLeft(this) { case (t, (oldOwner, newOwner)) => + new ChangeOwnerTraverser(oldOwner, newOwner) apply t + } + } + + def shallowDuplicate: Tree = new ShallowDuplicator(this) transform this + def shortClass: String = (getClass.getName split "[.$]").last + + def isErrorTyped = (tpe ne null) && tpe.isError + + /** When you want to know a little more than the class, but a lot + * less than the whole tree. + */ + def summaryString: String = this match { + case Literal(const) => "Literal(" + const + ")" + case Ident(name) => "Ident(%s)".format(name.decode) + case Select(qual, name) => "Select(%s, %s)".format(qual.summaryString, name.decode) + case t: NameTree => t.name.longString + case t => + t.shortClass + ( + if (t.symbol != null && t.symbol != NoSymbol) "(" + t.symbol + ")" + else "" + ) + } + } + + trait TermTree extends Tree with TermTreeApi + + trait TypTree extends Tree with TypTreeApi + + trait SymTree extends Tree with SymTreeContextApi { + override def hasSymbol = true + override var symbol: Symbol = NoSymbol + } + + trait NameTree extends Tree with NameTreeApi { + def name: Name + } + + trait RefTree extends SymTree with NameTree with RefTreeApi { + def qualifier: Tree // empty for Idents + def name: Name + } + + abstract class DefTree extends SymTree with NameTree with DefTreeApi { + def name: Name + override def isDef = true + } + + 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 with MemberDefApi { + 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 hasFlag MUTABLE) "var" else "val" + case _ => "" + } + } + + case class PackageDef(pid: RefTree, stats: List[Tree]) + extends MemberDef with PackageDefApi { + def name = pid.name + def mods = NoMods + } + object PackageDef extends PackageDefExtractor + + abstract class ImplDef extends MemberDef with ImplDefApi { + def impl: Template + } + + case class ClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template) + extends ImplDef with ClassDefApi + object ClassDef extends ClassDefExtractor + + case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) + extends ImplDef with ModuleDefApi + object ModuleDef extends ModuleDefExtractor + + abstract class ValOrDefDef extends MemberDef with ValOrDefDefApi { + def name: Name + def tpt: Tree + def rhs: Tree + } + + case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef with ValDefApi + object ValDef extends ValDefExtractor + + case class DefDef(mods: Modifiers, name: Name, tparams: List[TypeDef], + vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef with DefDefApi + object DefDef extends DefDefExtractor + + case class TypeDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) + extends MemberDef with TypeDefApi + object TypeDef extends TypeDefExtractor + + case class LabelDef(name: TermName, params: List[Ident], rhs: Tree) + extends DefTree with TermTree with LabelDefApi + object LabelDef extends LabelDefExtractor + + case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) extends ImportSelectorApi + object ImportSelector extends ImportSelectorExtractor + + case class Import(expr: Tree, selectors: List[ImportSelector]) + extends SymTree with ImportApi + object Import extends ImportExtractor + + case class Template(parents: List[Tree], self: ValDef, body: List[Tree]) + extends SymTree with TemplateApi + object Template extends TemplateExtractor + + case class Block(stats: List[Tree], expr: Tree) + extends TermTree with BlockApi + object Block extends BlockExtractor + + case class CaseDef(pat: Tree, guard: Tree, body: Tree) + extends Tree with CaseDefApi + object CaseDef extends CaseDefExtractor + + case class Alternative(trees: List[Tree]) + extends TermTree with AlternativeApi + object Alternative extends AlternativeExtractor + + case class Star(elem: Tree) + extends TermTree with StarApi + object Star extends StarExtractor + + case class Bind(name: Name, body: Tree) + extends DefTree with BindApi + object Bind extends BindExtractor + + case class UnApply(fun: Tree, args: List[Tree]) + extends TermTree with UnApplyApi + object UnApply extends UnApplyExtractor + + case class ArrayValue(elemtpt: Tree, elems: List[Tree]) + extends TermTree with ArrayValueApi + object ArrayValue extends ArrayValueExtractor + + case class Function(vparams: List[ValDef], body: Tree) + extends TermTree with SymTree with FunctionApi + object Function extends FunctionExtractor + + case class Assign(lhs: Tree, rhs: Tree) + extends TermTree with AssignApi + object Assign extends AssignExtractor + + case class AssignOrNamedArg(lhs: Tree, rhs: Tree) + extends TermTree with AssignOrNamedArgApi + object AssignOrNamedArg extends AssignOrNamedArgExtractor + + case class If(cond: Tree, thenp: Tree, elsep: Tree) + extends TermTree with IfApi + object If extends IfExtractor + + case class Match(selector: Tree, cases: List[CaseDef]) + extends TermTree with MatchApi + object Match extends MatchExtractor + + case class Return(expr: Tree) + extends TermTree with SymTree with ReturnApi + object Return extends ReturnExtractor + + case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) + extends TermTree with TryApi + object Try extends TryExtractor + + case class Throw(expr: Tree) + extends TermTree with ThrowApi + object Throw extends ThrowExtractor + + case class New(tpt: Tree) extends TermTree with NewApi + object New extends NewExtractor + + case class Typed(expr: Tree, tpt: Tree) + extends TermTree with TypedApi + object Typed extends TypedExtractor + + abstract class GenericApply extends TermTree with GenericApplyApi { + val fun: Tree + val args: List[Tree] + } + + case class TypeApply(fun: Tree, args: List[Tree]) + extends GenericApply with TypeApplyApi { + override def symbol: Symbol = fun.symbol + override def symbol_=(sym: Symbol) { fun.symbol = sym } + } + object TypeApply extends TypeApplyExtractor + + case class Apply(fun: Tree, args: List[Tree]) + extends GenericApply with ApplyApi { + override def symbol: Symbol = fun.symbol + override def symbol_=(sym: Symbol) { fun.symbol = sym } + } + object Apply extends ApplyExtractor + + // TODO remove this class, add a tree attachment to Apply to track whether implicits were involved + // copying trees will all too easily forget to distinguish subclasses + class ApplyToImplicitArgs(fun: Tree, args: List[Tree]) extends Apply(fun, args) + + // TODO remove this class, add a tree attachment to Apply to track whether implicits were involved + // copying trees will all too easily forget to distinguish subclasses + class ApplyImplicitView(fun: Tree, args: List[Tree]) extends Apply(fun, args) + + def ApplyConstructor(tpt: Tree, args: List[Tree]) = Apply(Select(New(tpt), nme.CONSTRUCTOR), args) + + case class ApplyDynamic(qual: Tree, args: List[Tree]) + extends TermTree with SymTree with ApplyDynamicApi + object ApplyDynamic extends ApplyDynamicExtractor + + case class Super(qual: Tree, mix: TypeName) extends TermTree with SuperApi { + override def symbol: Symbol = qual.symbol + override def symbol_=(sym: Symbol) { qual.symbol = sym } + } + object Super extends SuperExtractor + + case class This(qual: TypeName) + extends TermTree with SymTree with ThisApi + object This extends ThisExtractor + + case class Select(qualifier: Tree, name: Name) + extends RefTree with SelectApi + object Select extends SelectExtractor + + case class Ident(name: Name) extends RefTree with IdentContextApi { + def qualifier: Tree = EmptyTree + def isBackquoted = this.attachments.get[BackquotedIdentifierAttachment.type].isDefined + } + object Ident extends IdentExtractor + + case class ReferenceToBoxed(ident: Ident) extends TermTree with ReferenceToBoxedApi { + override def symbol: Symbol = ident.symbol + override def symbol_=(sym: Symbol) { ident.symbol = sym } + } + object ReferenceToBoxed extends ReferenceToBoxedExtractor + + case class Literal(value: Constant) + extends TermTree with LiteralApi { + assert(value ne null) + } + object Literal extends LiteralExtractor + +// @deprecated("will be removed and then be re-introduced with changed semantics, use Literal(Constant(x)) instead") +// def Literal(x: Any) = new Literal(Constant(x)) + + case class Annotated(annot: Tree, arg: Tree) extends Tree with AnnotatedApi + object Annotated extends AnnotatedExtractor + + case class SingletonTypeTree(ref: Tree) + extends TypTree with SingletonTypeTreeApi + object SingletonTypeTree extends SingletonTypeTreeExtractor + + case class SelectFromTypeTree(qualifier: Tree, name: TypeName) + extends TypTree with RefTree with SelectFromTypeTreeApi + object SelectFromTypeTree extends SelectFromTypeTreeExtractor + + case class CompoundTypeTree(templ: Template) + extends TypTree with CompoundTypeTreeApi + object CompoundTypeTree extends CompoundTypeTreeExtractor + + case class AppliedTypeTree(tpt: Tree, args: List[Tree]) + extends TypTree with AppliedTypeTreeApi { + override def symbol: Symbol = tpt.symbol + override def symbol_=(sym: Symbol) { tpt.symbol = sym } + } + object AppliedTypeTree extends AppliedTypeTreeExtractor + + case class TypeBoundsTree(lo: Tree, hi: Tree) + extends TypTree with TypeBoundsTreeApi + object TypeBoundsTree extends TypeBoundsTreeExtractor + + case class ExistentialTypeTree(tpt: Tree, whereClauses: List[Tree]) + extends TypTree with ExistentialTypeTreeApi + object ExistentialTypeTree extends ExistentialTypeTreeExtractor + + case class TypeTree() extends TypTree with TypeTreeContextApi { + private var orig: Tree = null + private[scala] 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) + } + } + object TypeTree extends TypeTreeExtractor + + def TypeTree(tp: Type): TypeTree = TypeTree() setType tp + + class StrictTreeCopier extends TreeCopierOps { + 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.toTermName, impl).copyAttrs(tree) + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = + new ValDef(mods, name.toTermName, 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.toTermName, 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.toTermName, params, rhs).copyAttrs(tree) + def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = + new Import(expr, selectors).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 { // TODO: use a tree attachment to track whether this is an apply to implicit args or a view + case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) + case _: ApplyImplicitView => new ApplyImplicitView(fun, args) + // TODO: ApplyConstructor ??? + 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 ReferenceToBoxed(tree: Tree, idt: Ident) = + new ReferenceToBoxed(idt).copyAttrs(tree) + def Literal(tree: Tree, value: Constant) = + new Literal(value).copyAttrs(tree) + def TypeTree(tree: Tree) = + new TypeTree().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) + } + + class LazyTreeCopier extends TreeCopierOps { + val treeCopy: TreeCopier = newStrictTreeCopier + 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 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 ReferenceToBoxed(tree: Tree, idt: Ident) = tree match { + case t @ ReferenceToBoxed(idt0) + if (idt0 == idt) => t + case _ => this.treeCopy.ReferenceToBoxed(tree, idt) + } + 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 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) + } + } + + // Belongs in TreeInfo but then I can't reach it from TreePrinters. + def isReferenceToScalaMember(t: Tree, Id: Name) = t match { + case Ident(Id) => true + case Select(Ident(nme.scala_), Id) => true + case Select(Select(Ident(nme.ROOTPKG), nme.scala_), Id) => true + case _ => false + } + /** Is the tree Predef, scala.Predef, or _root_.scala.Predef? + */ + def isReferenceToPredef(t: Tree) = isReferenceToScalaMember(t, nme.Predef) + def isReferenceToAnyVal(t: Tree) = isReferenceToScalaMember(t, tpnme.AnyVal) + + // --- modifiers implementation --------------------------------------- + + /** @param privateWithin the qualifier for a private (a type name) + * or tpnme.EMPTY, if none is given. + * @param annotations the annotations for the definition. + * '''Note:''' the typechecker drops these annotations, + * use the AnnotationInfo's (Symbol.annotations) in later phases. + */ + case class Modifiers(flags: Long, + privateWithin: Name, + annotations: List[Tree]) extends ModifiersApi with HasFlags { + + var positions: Map[Long, Position] = Map() + + def setPositions(poss: Map[Long, Position]): this.type = { + positions = poss; this + } + + /* Abstract types from HasFlags. */ + type AccessBoundaryType = Name + type AnnotationType = Tree + + def hasAnnotationNamed(name: TypeName) = { + annotations exists { + case Apply(Select(New(Ident(`name`)), _), _) => true + case Apply(Select(New(Select(_, `name`)), _), _) => true + case _ => false + } + } + + def hasAccessBoundary = privateWithin != tpnme.EMPTY + def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask + def hasFlag(flag: Long) = (flag & flags) != 0L + + def & (flag: Long): Modifiers = { + val flags1 = flags & flag + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations) setPositions positions + } + def &~ (flag: Long): Modifiers = { + val flags1 = flags & (~flag) + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations) setPositions positions + } + def | (flag: Long): Modifiers = { + val flags1 = flags | flag + if (flags1 == flags) this + else Modifiers(flags1, privateWithin, annotations) setPositions positions + } + def withAnnotations(annots: List[Tree]) = + if (annots.isEmpty) this + else copy(annotations = annotations ::: annots) setPositions positions + + def withPosition(flag: Long, position: Position) = + copy() setPositions positions + (flag -> position) + + override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = + Modifiers(flags, privateWithin, f(annotations)) setPositions positions + + override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions) + } + + object Modifiers extends ModifiersCreator + + implicit val ModifiersTag = ClassTag[Modifiers](classOf[Modifiers]) + + // ---- 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 + } + + /** + * @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.toTermName, impl) setSymbol sym + } + + def ValDef(sym: Symbol, rhs: Tree): ValDef = + atPos(sym.pos) { + ValDef(Modifiers(sym.flags), sym.name.toTermName, + TypeTree(sym.tpe) setPos sym.pos.focus, + 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(mods, + sym.name.toTermName, + sym.typeParams map TypeDef, + vparamss, + TypeTree(sym.tpe.finalResultType) setPos sym.pos.focus, + 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, mapParamss(sym)(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.toTermName, params map Ident, rhs) setSymbol sym + } + + /** 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 + + def Try(body: Tree, cases: (Tree, Tree)*): Try = + Try(body, cases.toList map { case (pat, rhs) => CaseDef(pat, EmptyTree, rhs) }, EmptyTree) + + def Throw(tpe: Type, args: Tree*): Throw = + Throw(New(tpe, args: _*)) + + def Apply(sym: Symbol, args: Tree*): Tree = + Apply(Ident(sym), args.toList) + + /** 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 = argss match { + case Nil => ApplyConstructor(tpt, Nil) + case xs :: rest => { + def mkApply(fun: Tree, args: List[Tree]) = Apply(fun, args) + rest.foldLeft(ApplyConstructor(tpt, xs): Tree)(mkApply) + // [Eugene++] no longer compiles after I moved the `Apply` case class here + // rest.foldLeft(ApplyConstructor(tpt, xs): Tree)(Apply) + } + } + + /** 0-1 argument list new, based on a type. + */ + def New(tpe: Type, args: Tree*): Tree = + ApplyConstructor(TypeTree(tpe), args.toList) + + def New(sym: Symbol, args: Tree*): Tree = + New(sym.tpe, args: _*) + + 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, name: String): Select = + Select(qualifier, newTermName(name)) + + def Select(qualifier: Tree, sym: Symbol): Select = + Select(qualifier, sym.name) setSymbol sym + + def Ident(name: String): Ident = + Ident(newTermName(name)) + + def Ident(sym: Symbol): Ident = + Ident(sym.name) setSymbol sym + + /** Block factory that flattens directly nested blocks. + */ + def Block(stats: Tree*): Block = { + if (stats.isEmpty) Block(Nil, Literal(Constant(()))) + else stats match { + case Seq(b @ Block(_, _)) => b + case Seq(stat) => Block(stats.toList, Literal(Constant(()))) + case Seq(_, rest @ _*) => Block(stats.init.toList, stats.last) + } + } + + // --- generic traversers and transformers + + override protected def itraverse(traverser: Traverser, tree: Tree): Unit = { + import traverser._ + tree match { + case EmptyTree => + ; + case PackageDef(pid, stats) => + traverse(pid) + atOwner(mclass(tree.symbol)) { + traverseTrees(stats) + } + case ClassDef(mods, name, tparams, impl) => + atOwner(tree.symbol) { + traverseTrees(mods.annotations); traverseTrees(tparams); traverse(impl) + } + case ModuleDef(mods, name, impl) => + atOwner(mclass(tree.symbol)) { + 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 AssignOrNamedArg(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 ReferenceToBoxed(idt) => + traverse(idt) + 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 _ => xtraverse(traverser, tree) + } + } + + override protected def itransform(transformer: Transformer, tree: Tree): Tree = { + import transformer._ + val treeCopy = transformer.treeCopy + tree match { + case EmptyTree => + tree + case PackageDef(pid, stats) => + treeCopy.PackageDef( + tree, transform(pid).asInstanceOf[RefTree], + atOwner(mclass(tree.symbol)) { + 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(mclass(tree.symbol)) { + 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 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 ReferenceToBoxed(idt) => + treeCopy.ReferenceToBoxed(tree, transform(idt) match { case idt1: Ident => idt1 }) + case Literal(value) => + treeCopy.Literal(tree, value) + case TypeTree() => + treeCopy.TypeTree(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 _ => + xtransform(transformer, tree) + } + } + + private def mclass(sym: Symbol) = sym map (_.asModuleSymbol.moduleClass) + + // --- specific traversers and transformers + + 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 ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { + def changeOwner(tree: Tree) = tree match { + case Return(expr) => + if (tree.symbol == oldowner) { + // SI-5612 + if (newowner hasTransOwner oldowner) + log("NOT changing owner of %s because %s is nested in %s".format(tree, newowner, oldowner)) + else { + log("changing owner of %s: %s => %s".format(tree, oldowner, newowner)) + tree.symbol = newowner + } + } + case _: DefTree | _: Function => + if (tree.symbol != NoSymbol && tree.symbol.owner == oldowner) { + tree.symbol.owner = newowner + } + case _ => + } + override def traverse(tree: Tree) { + changeOwner(tree) + super.traverse(tree) + } + } + + private class ShallowDuplicator(orig: Tree) extends Transformer { + override val treeCopy = newStrictTreeCopier + override def transform(tree: Tree) = + if (tree eq orig) super.transform(tree) + else tree + } + // Create a readable string describing a substitution. + private def substituterString(fromStr: String, toStr: String, from: List[Any], to: List[Any]): String = { + "subst[%s, %s](%s)".format(fromStr, toStr, (from, to).zipped map (_ + " -> " + _) mkString ", ") + } + + // NOTE: calls shallowDuplicate on trees in `to` to avoid problems when symbols in `from` + // occur multiple times in the `tree` passed to `transform`, + // otherwise, the resulting Tree would be a graph, not a tree... this breaks all sorts of stuff, + // notably concerning the mutable aspects of Trees (such as setting their .tpe) + 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.shallowDuplicate // TODO: does it ever make sense *not* to perform a shallowDuplicate on `to.head`? + else subst(from.tail, to.tail); + subst(from, to) + case _ => + super.transform(tree) + } + override def toString = substituterString("Symbol", "Tree", from, to) + } + + /** Substitute clazz.this with `to`. `to` must be an attributed tree. + */ + class ThisSubstituter(clazz: Symbol, to: => Tree) extends Transformer { + val newtpe = to.tpe + override def transform(tree: Tree) = { + if (tree.tpe ne null) tree.tpe = tree.tpe.substThis(clazz, newtpe) + tree match { + case This(_) if tree.symbol == clazz => to + case _ => super.transform(tree) + } + } + } + + class TypeMapTreeSubstituter(val typeMap: TypeMap) extends Traverser { + override def traverse(tree: Tree) { + if (tree.tpe ne null) + tree.tpe = typeMap(tree.tpe) + if (tree.isDef) + tree.symbol modifyInfo typeMap + + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + } + + class TreeTypeSubstituter(val from: List[Symbol], val to: List[Type]) extends TypeMapTreeSubstituter(new SubstTypeMap(from, to)) { + def isEmpty = from.isEmpty && to.isEmpty + override def toString() = "TreeTypeSubstituter("+from+","+to+")" + } + + lazy val EmptyTreeTypeSubstituter = new TreeTypeSubstituter(List(), List()) + + class TreeSymSubstTraverser(val from: List[Symbol], val to: List[Symbol]) extends TypeMapTreeSubstituter(new SubstSymMap(from, to)) { + override def toString() = "TreeSymSubstTraverser/" + substituterString("Symbol", "Symbol", 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) if tree.symbol != NoSymbol => + 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/" + substituterString("Symbol", "Symbol", from, to) + } + + + 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 CollectTreeTraverser[T](pf: PartialFunction[Tree, T]) extends Traverser { + val results = new ListBuffer[T] + override def traverse(t: Tree) { + if (pf.isDefinedAt(t)) results += pf(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) + } + } + } + + private lazy val duplicator = new Transformer { + override val treeCopy = newStrictTreeCopier + override def transform(t: Tree) = { + val t1 = super.transform(t) + if ((t1 ne t) && t1.pos.isRange) t1 setPos t.pos.focus + t1 + } + } + + // ------ copiers ------------------------------------------- + + def copyDefDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + tparams: List[TypeDef] = null, + vparamss: List[List[ValDef]] = null, + tpt: Tree = null, + rhs: Tree = null + ): DefDef = tree match { + case DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) => + treeCopy.DefDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (tparams eq null) tparams0 else tparams, + if (vparamss eq null) vparamss0 else vparamss, + if (tpt eq null) tpt0 else tpt, + if (rhs eq null) rhs0 else rhs + ) + case t => + sys.error("Not a DefDef: " + t + "/" + t.getClass) + } + def copyValDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + tpt: Tree = null, + rhs: Tree = null + ): ValDef = tree match { + case ValDef(mods0, name0, tpt0, rhs0) => + treeCopy.ValDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (tpt eq null) tpt0 else tpt, + if (rhs eq null) rhs0 else rhs + ) + case t => + sys.error("Not a ValDef: " + t + "/" + t.getClass) + } + def copyClassDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + tparams: List[TypeDef] = null, + impl: Template = null + ): ClassDef = tree match { + case ClassDef(mods0, name0, tparams0, impl0) => + treeCopy.ClassDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (tparams eq null) tparams0 else tparams, + if (impl eq null) impl0 else impl + ) + case t => + sys.error("Not a ClassDef: " + t + "/" + t.getClass) + } + + def deriveDefDef(ddef: Tree)(applyToRhs: Tree => Tree): DefDef = ddef match { + case DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) => + treeCopy.DefDef(ddef, mods0, name0, tparams0, vparamss0, tpt0, applyToRhs(rhs0)) + case t => + sys.error("Not a DefDef: " + t + "/" + t.getClass) + } + def deriveValDef(vdef: Tree)(applyToRhs: Tree => Tree): ValDef = vdef match { + case ValDef(mods0, name0, tpt0, rhs0) => + treeCopy.ValDef(vdef, mods0, name0, tpt0, applyToRhs(rhs0)) + case t => + sys.error("Not a ValDef: " + t + "/" + t.getClass) + } + def deriveTemplate(templ: Tree)(applyToBody: List[Tree] => List[Tree]): Template = templ match { + case Template(parents0, self0, body0) => + treeCopy.Template(templ, parents0, self0, applyToBody(body0)) + case t => + sys.error("Not a Template: " + t + "/" + t.getClass) + } + def deriveClassDef(cdef: Tree)(applyToImpl: Template => Template): ClassDef = cdef match { + case ClassDef(mods0, name0, tparams0, impl0) => + treeCopy.ClassDef(cdef, mods0, name0, tparams0, applyToImpl(impl0)) + case t => + sys.error("Not a ClassDef: " + t + "/" + t.getClass) + } + def deriveModuleDef(mdef: Tree)(applyToImpl: Template => Template): ModuleDef = mdef match { + case ModuleDef(mods0, name0, impl0) => + treeCopy.ModuleDef(mdef, mods0, name0, applyToImpl(impl0)) + case t => + sys.error("Not a ModuleDef: " + t + "/" + t.getClass) + } + def deriveCaseDef(cdef: Tree)(applyToBody: Tree => Tree): CaseDef = cdef match { + case CaseDef(pat0, guard0, body0) => + treeCopy.CaseDef(cdef, pat0, guard0, applyToBody(body0)) + case t => + sys.error("Not a CaseDef: " + t + "/" + t.getClass) + } + def deriveLabelDef(ldef: Tree)(applyToRhs: Tree => Tree): LabelDef = ldef match { + case LabelDef(name0, params0, rhs0) => + treeCopy.LabelDef(ldef, name0, params0, applyToRhs(rhs0)) + case t => + sys.error("Not a LabelDef: " + t + "/" + t.getClass) + } + + implicit val TreeTag = ClassTag[Tree](classOf[Tree]) + implicit val TermTreeTag = ClassTag[TermTree](classOf[TermTree]) + implicit val TypTreeTag = ClassTag[TypTree](classOf[TypTree]) + implicit val SymTreeTag = ClassTag[SymTree](classOf[SymTree]) + implicit val NameTreeTag = ClassTag[NameTree](classOf[NameTree]) + implicit val RefTreeTag = ClassTag[RefTree](classOf[RefTree]) + implicit val DefTreeTag = ClassTag[DefTree](classOf[DefTree]) + implicit val MemberDefTag = ClassTag[MemberDef](classOf[MemberDef]) + implicit val PackageDefTag = ClassTag[PackageDef](classOf[PackageDef]) + implicit val ImplDefTag = ClassTag[ImplDef](classOf[ImplDef]) + implicit val ClassDefTag = ClassTag[ClassDef](classOf[ClassDef]) + implicit val ModuleDefTag = ClassTag[ModuleDef](classOf[ModuleDef]) + implicit val ValOrDefDefTag = ClassTag[ValOrDefDef](classOf[ValOrDefDef]) + implicit val ValDefTag = ClassTag[ValDef](classOf[ValDef]) + implicit val DefDefTag = ClassTag[DefDef](classOf[DefDef]) + implicit val TypeDefTag = ClassTag[TypeDef](classOf[TypeDef]) + implicit val LabelDefTag = ClassTag[LabelDef](classOf[LabelDef]) + implicit val ImportSelectorTag = ClassTag[ImportSelector](classOf[ImportSelector]) + implicit val ImportTag = ClassTag[Import](classOf[Import]) + implicit val TemplateTag = ClassTag[Template](classOf[Template]) + implicit val BlockTag = ClassTag[Block](classOf[Block]) + implicit val CaseDefTag = ClassTag[CaseDef](classOf[CaseDef]) + implicit val AlternativeTag = ClassTag[Alternative](classOf[Alternative]) + implicit val StarTag = ClassTag[Star](classOf[Star]) + implicit val BindTag = ClassTag[Bind](classOf[Bind]) + implicit val UnApplyTag = ClassTag[UnApply](classOf[UnApply]) + implicit val ArrayValueTag = ClassTag[ArrayValue](classOf[ArrayValue]) + implicit val FunctionTag = ClassTag[Function](classOf[Function]) + implicit val AssignTag = ClassTag[Assign](classOf[Assign]) + implicit val AssignOrNamedArgTag = ClassTag[AssignOrNamedArg](classOf[AssignOrNamedArg]) + implicit val IfTag = ClassTag[If](classOf[If]) + implicit val MatchTag = ClassTag[Match](classOf[Match]) + implicit val ReturnTag = ClassTag[Return](classOf[Return]) + implicit val TryTag = ClassTag[Try](classOf[Try]) + implicit val ThrowTag = ClassTag[Throw](classOf[Throw]) + implicit val NewTag = ClassTag[New](classOf[New]) + implicit val TypedTag = ClassTag[Typed](classOf[Typed]) + implicit val GenericApplyTag = ClassTag[GenericApply](classOf[GenericApply]) + implicit val TypeApplyTag = ClassTag[TypeApply](classOf[TypeApply]) + implicit val ApplyTag = ClassTag[Apply](classOf[Apply]) + implicit val ApplyDynamicTag = ClassTag[ApplyDynamic](classOf[ApplyDynamic]) + implicit val SuperTag = ClassTag[Super](classOf[Super]) + implicit val ThisTag = ClassTag[This](classOf[This]) + implicit val SelectTag = ClassTag[Select](classOf[Select]) + implicit val IdentTag = ClassTag[Ident](classOf[Ident]) + implicit val ReferenceToBoxedTag = ClassTag[ReferenceToBoxed](classOf[ReferenceToBoxed]) + implicit val LiteralTag = ClassTag[Literal](classOf[Literal]) + implicit val AnnotatedTag = ClassTag[Annotated](classOf[Annotated]) + implicit val SingletonTypeTreeTag = ClassTag[SingletonTypeTree](classOf[SingletonTypeTree]) + implicit val SelectFromTypeTreeTag = ClassTag[SelectFromTypeTree](classOf[SelectFromTypeTree]) + implicit val CompoundTypeTreeTag = ClassTag[CompoundTypeTree](classOf[CompoundTypeTree]) + implicit val AppliedTypeTreeTag = ClassTag[AppliedTypeTree](classOf[AppliedTypeTree]) + implicit val TypeBoundsTreeTag = ClassTag[TypeBoundsTree](classOf[TypeBoundsTree]) + implicit val ExistentialTypeTreeTag = ClassTag[ExistentialTypeTree](classOf[ExistentialTypeTree]) + implicit val TypeTreeTag = ClassTag[TypeTree](classOf[TypeTree]) +} diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala new file mode 100644 index 0000000000..33f6a645e8 --- /dev/null +++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala @@ -0,0 +1,71 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect +package internal + +trait TypeDebugging { + self: SymbolTable => + + import definitions._ + + // @M toString that is safe during debugging (does not normalize, ...) + object typeDebug { + private def to_s(x: Any): String = x match { + // otherwise case classes are caught looking like products + case _: Tree | _: Type => "" + x + case x: TraversableOnce[_] => x mkString ", " + case x: Product => x.productIterator mkString ("(", ", ", ")") + case _ => "" + x + } + def ptIndent(x: Any) = ("" + x).replaceAll("\\n", " ") + def ptBlock(label: String, pairs: (String, Any)*): String = { + if (pairs.isEmpty) label + "{ }" + else { + val width = (pairs map (_._1.length)).max + val fmt = "%-" + (width + 1) + "s %s" + val strs = pairs map { case (k, v) => fmt.format(k, to_s(v)) } + + strs.mkString(label + " {\n ", "\n ", "\n}") + } + } + def ptLine(label: String, pairs: (String, Any)*): String = { + val strs = pairs map { case (k, v) => k + "=" + to_s(v) } + strs.mkString(label + ": ", ", ", "") + } + def ptTree(t: Tree) = t match { + case PackageDef(pid, _) => "package " + pid + case ModuleDef(_, name, _) => "object " + name + case ClassDef(_, name, tparams, _) => "class " + name + str.brackets(tparams) + case _ => to_s(t) + } + + 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 ", "}") + } + + 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.getClass.getName+"?"//tp.toString might produce cyclic error... + } + def debugString(tp: Type) = debug(tp) + } + def paramString(tp: Type) = typeDebug.str parentheses (tp.params map (_.defString)) + def typeParamsString(tp: Type) = typeDebug.str brackets (tp.typeParams map (_.defString)) + def typeArgsString(tp: Type) = typeDebug.str brackets (tp.typeArgs map (_.safeToString)) + def debugString(tp: Type) = typeDebug debugString tp +} diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala new file mode 100644 index 0000000000..23921d73cc --- /dev/null +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -0,0 +1,6820 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal + +import scala.collection.{ mutable, immutable, generic } +import generic.Clearable +import scala.ref.WeakReference +import mutable.ListBuffer +import Flags._ +import scala.util.control.ControlThrowable +import scala.annotation.tailrec +import util.Statistics._ +import language.postfixOps + +/* 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. + case ErasedValueType(tp) + // only used during erasure of derived value classes. +*/ + +trait Types extends api.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 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 = sys.props contains "scalac.debug.lub" + private final val traceTypeVars = sys.props contains "scalac.debug.tvar" + /** In case anyone wants to turn off lub verification without reverting anything. */ + private final val verifyLubs = true + /** In case anyone wants to turn off type parameter bounds being used + * to seed type constraints. + */ + private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" + + protected val enableTypeVarExperimentals = settings.Xexperimental.value + + /** Empty immutable maps to avoid allocations. */ + private val emptySymMap = immutable.Map[Symbol, Symbol]() + private val emptySymCount = immutable.Map[Symbol, Int]() + + /** 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. + */ + lazy val undoLog = newUndoLog + + protected def newUndoLog = new UndoLog + + class UndoLog extends Clearable { + private type UndoPairs = List[(TypeVar, TypeConstraint)] + private var log: UndoPairs = List() + + // register with the auto-clearing cache manager + perRunCaches.recordCache(this) + + /** Undo all changes to constraints to type variables upto `limit`. */ + private def undoTo(limit: UndoPairs) { + while ((log ne limit) && log.nonEmpty) { + val (tv, constr) = log.head + tv.constr = constr + log = log.tail + } + } + + /** No sync necessary, because record should only + * be called from within a undo or undoUnless block, + * which is already synchronized. + */ + private[reflect] def record(tv: TypeVar) = { + log ::= ((tv, tv.constr.cloneInternal)) + } + + def clear() { + if (settings.debug.value) + self.log("Clearing " + log.size + " entries from the undoLog.") + + log = Nil + } + def size = log.size + + // `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 = perRunCaches.newWeakMap[List[Type], WeakReference[Type]]() + + /** 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) + } + + case object UnmappableTree extends TermTree { + override def toString = "<unmappable>" + super.tpe_=(NoType) + override def tpe_=(t: Type) = if (t != NoType) { + throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") + } + } + + abstract class TypeApiImpl extends TypeApi { this: Type => + def declaration(name: Name): Symbol = decl(name) + def nonPrivateDeclaration(name: Name): Symbol = nonPrivateDecl(name) + def declarations = decls + def typeArguments = typeArgs + def erasure = this match { + case ConstantType(value) => widen.erasure // [Eugene to Martin] constant types are unaffected by erasure. weird. + case _ => + var result: Type = transformedType(this) + result = result.normalize match { // necessary to deal with erasures of HK types, typeConstructor won't work + case PolyType(undets, underlying) => existentialAbstraction(undets, underlying) // we don't want undets in the result + case _ => result + } + // [Eugene] erasure screws up all ThisTypes for modules into PackageTypeRefs + // we need to unscrew them, or certain typechecks will fail mysteriously + // http://groups.google.com/group/scala-internals/browse_thread/thread/6d3277ae21b6d581 + result = result.map(tpe => tpe match { + case tpe: PackageTypeRef => ThisType(tpe.sym) + case _ => tpe + }) + result + } + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Type = substSym(from, to) + def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) + + // [Eugene] to be discussed and refactored + def isConcrete = { + def notConcreteSym(sym: Symbol) = + sym.isAbstractType && !sym.isExistential + + def notConcreteTpe(tpe: Type): Boolean = tpe match { + case ThisType(_) => false + case SuperType(_, _) => false + case SingleType(pre, sym) => notConcreteSym(sym) + case ConstantType(_) => false + case TypeRef(_, sym, args) => notConcreteSym(sym) || (args exists (arg => notConcreteTpe(arg))) + case RefinedType(_, _) => false + case ExistentialType(_, _) => false + case AnnotatedType(_, tp, _) => notConcreteTpe(tp) + case _ => true + } + + !notConcreteTpe(this) + } + + // [Eugene] is this comprehensive? + // the only thingies that we want to splice are: 1) type parameters, 2) type members + // the thingies that we don't want to splice are: 1) concrete types (obviously), 2) existential skolems + // this check seems to cover them all, right? + // todo. after we discuss this, move the check to subclasses + def isSpliceable = { + this.isInstanceOf[TypeRef] && typeSymbol.isAbstractType && !typeSymbol.isExistential + } + } + + /** The base class for all types */ + abstract class Type extends TypeApiImpl with Annotatable[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) + * A type's typeSymbol should if possible not be inspected directly, due to + * the likelihood that what is true for tp.typeSymbol is not true for + * tp.sym, due to normalization. + */ + def typeSymbol: Symbol = NoSymbol + + /** The term symbol ''directly'' associated with the type. + */ + def termSymbolDirect: Symbol = termSymbol + + /** The type symbol ''directly'' associated with the type. + * In other words, no normalization is performed: if this is an alias type, + * the symbol returned is that of the alias, not the underlying 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. + */ + /* + Note: this implementation of narrow is theoretically superior to the one + in use below, but imposed a significant performance penalty. It was in trunk + from svn r24960 through r25080. + */ + /* + def narrow: Type = + if (phase.erasedTypes) this + else commonOwner(this) freshExistential ".type" setInfo singletonBounds(this) tpe + */ + + /** Map to a singleton type which is a subtype of this type. + * The fallback implemented here gives: + * {{{ + * T.narrow = (T {}).this.type + * }}} + * Overridden where we know more about where types come from. + */ + def narrow: Type = + if (phase.erasedTypes) this + else { + val cowner = commonOwner(this) + refinedType(List(this), cowner, EmptyScope, cowner.pos).narrow + } + + /** 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 class with nonEmpty parents, the first parent. + * Otherwise some specific fixed top type. + */ + def firstParent = if (parents.nonEmpty) parents.head else ObjectClass.tpe + + /** 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() + + /** A list of placeholder types derived from the type parameters. + * Used by RefinedType and TypeRef. + */ + protected def dummyArgs: List[Type] = typeParams map (_.typeConstructor) + + /** 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 = ApproximateDependentMap(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) + + /** A list of all non-private members defined or declared in this type. */ + def nonPrivateDecls: List[Symbol] = decls filter (x => !x.isPrivate) toList + + /** 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] = membersBasedOnFlags(0, 0) + + /** A list of all non-private members of this type (defined or inherited) */ + def nonPrivateMembers: List[Symbol] = membersBasedOnFlags(BridgeAndPrivateFlags, 0) + + /** 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] = membersBasedOnFlags(BridgeAndPrivateFlags & ~admit, 0) + + /** A list of all implicit symbols of this type (defined or inherited) */ + def implicitMembers: List[Symbol] = membersBasedOnFlags(BridgeFlags, IMPLICIT) + + /** A list of all deferred symbols of this type (defined or inherited) */ + def deferredMembers: List[Symbol] = membersBasedOnFlags(BridgeFlags, DEFERRED) + + /** The member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def member(name: Name): Symbol = + memberBasedOnName(name, BridgeFlags) + + /** 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 = + memberBasedOnName(name, BridgeAndPrivateFlags) + + /** All members with the given flags, excluding bridges. + */ + def membersWithFlags(requiredFlags: Long): List[Symbol] = + membersBasedOnFlags(BridgeFlags, requiredFlags) + + /** All non-private members with the given flags, excluding bridges. + */ + def nonPrivateMembersWithFlags(requiredFlags: Long): List[Symbol] = + membersBasedOnFlags(BridgeAndPrivateFlags, requiredFlags) + + /** The non-private member with given name, admitting members with given flags `admit`. + * "Admitting" refers to the fact that members with a PRIVATE, BRIDGE, or VBRIDGE + * flag are usually excluded from findMember results, but supplying any of those flags + * to this method disables that exclusion. + * + * An OverloadedSymbol if several exist, NoSymbol if none exists. + */ + def nonPrivateMemberAdmitting(name: Name, admit: Long): Symbol = + memberBasedOnName(name, BridgeAndPrivateFlags & ~admit) + + /** The non-local member with given name, + * an OverloadedSymbol if several exist, NoSymbol if none exist */ + def nonLocalMember(name: Name): Symbol = + memberBasedOnName(name, BridgeFlags | LOCAL) + + /** Members excluding and requiring the given flags. + * Note: unfortunately it doesn't work to exclude DEFERRED this way. + */ + def membersBasedOnFlags(excludedFlags: Long, requiredFlags: Long): List[Symbol] = + findMember(nme.ANYNAME, excludedFlags, requiredFlags, false).alternatives + + def memberBasedOnName(name: Name, excludedFlags: Long): Symbol = + findMember(name, excludedFlags, 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) this + else { +// scala.tools.nsc.util.trace.when(pre.isInstanceOf[ExistentialType])("X "+this+".asSeenfrom("+pre+","+clazz+" = ") { + incCounter(asSeenFromCount) + val start = startTimer(asSeenFromNanos) + val m = new AsSeenFromMap(pre.normalize, clazz) + val tp = m apply this + val tp1 = existentialAbstraction(m.capturedParams, tp) + val result: Type = + if (m.capturedSkolems.isEmpty) tp1 + else deriveType(m.capturedSkolems, _.cloneSymbol setFlag CAPTURED)(tp1) + + stopTimer(asSeenFromNanos, start) + result + } + } + + /** 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 = + if (from.isEmpty) this + else 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) || from.isEmpty) 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 substThis(from: Symbol, to: Symbol): Type = + substThis(from, to.thisType) + + /** Performs both substThis and substSym, in that order. + * + * [JZ] Reverted `SubstThisAndSymMap` from 334872, which was not the same as + * `substThis(from, to).substSym(symsFrom, symsTo)`. + * + * `SubstThisAndSymMap` performs a breadth-first map over this type, which meant that + * symbol substitution occured before `ThisType` substitution. Consequently, in substitution + * of a `SingleType(ThisType(`from`), sym), symbols were rebound to `from` rather than `to`. + */ + def substThisAndSym(from: Symbol, to: Type, symsFrom: List[Symbol], symsTo: List[Symbol]): Type = + if (symsFrom eq symsTo) substThis(from, to) + else substThis(from, to).substSym(symsFrom, symsTo) + + /** Returns all parts of this type which satisfy predicate `p` */ + def filter(p: Type => Boolean): List[Type] = new FilterTypeCollector(p) collect this + def withFilter(p: Type => Boolean) = new FilterMapForeach(p) + + class FilterMapForeach(p: Type => Boolean) extends FilterTypeCollector(p){ + def foreach[U](f: Type => U): Unit = collect(Type.this) foreach f + def map[T](f: Type => T): List[T] = collect(Type.this) map f + } + + /** 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 `pf' to each part of this type on which the function is defined */ + def collect[T](pf: PartialFunction[Type, T]): List[T] = new CollectTypeCollector(pf).collect(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 = typeToString(this) + + /** 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 == "type") widen.toString + else if ((str endsWith ".type") && !typeSymbol.isModuleClass) str + " (with underlying type " + widen + ")" + else str + } + + /** The string representation of this type when the direct object in a sentence. + * Normally this is no different from the regular representation, but modules + * read better as "object Foo" here and "Foo.type" the rest of the time. + */ + def directObjectString = safeToString + + /** A test whether a type contains any unification type variables. + * Overridden with custom logic except where trivially true. + */ + def isGround: Boolean = this match { + 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 = { + // 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. + val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) + + 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) + if (suspension ne null) suspension foreach (_.suspended = false) + 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 = newScope + members enter member + members enter sym + } + } else { + var prevEntry = members.lookupEntry(sym.name) + var symtpe: Type = null + while ((prevEntry ne null) && + !(prevEntry.sym == sym || + prevEntry.sym.owner != sym.owner && + !sym.hasFlag(PRIVATE) && { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + self.memberType(prevEntry.sym) matches symtpe + })) { + 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) + if (suspension ne null) suspension foreach (_.suspended = false) + if (members eq null) { + if (member == NoSymbol) incCounter(noMemberCount) + member + } else { + incCounter(multMemberCount) + baseClasses.head.newOverloaded(this, members.toList) + } + } + /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ + def skolemsExceptMethodTypeParams: 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.isExistentialSkolem || sym.isGADTSkolem) && // treat GADT skolems like existential skolems + !((boundSyms contains sym) || (skolems contains sym))) + skolems = sym :: skolems + case _ => + } + } + skolems + } + + // Implementation of Annotatable for all types but AnnotatedType, which + // overrides these. + def annotations: List[AnnotationInfo] = Nil + def withoutAnnotations: Type = this + def filterAnnotations(p: AnnotationInfo => Boolean): Type = this + def setAnnotations(annots: List[AnnotationInfo]): Type = annotatedType(annots, this) + def withAnnotations(annots: List[AnnotationInfo]): Type = annotatedType(annots, 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 extends Product { + final override val hashCode = scala.runtime.ScalaRunTime._hashCode(this) + } + + /** 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 = { + // Avoiding printing Predef.type and scala.package.type as "type", + // since in all other cases we omit those prefixes. + val pre = underlying.typeSymbol.skipPackageObject + if (pre.isOmittablePrefix) pre.fullName + ".type" + else 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" + } + /** BoundedWildcardTypes, used only during type inference, are created in + * two places that I can find: + * + * 1. If the expected type of an expression is an existential type, + * its hidden symbols are replaced with bounded wildcards. + * 2. When an implicit conversion is being sought based in part on + * the name of a method in the converted type, a HasMethodMatching + * type is created: a MethodType with parameters typed as + * BoundedWildcardTypes. + */ + case class BoundedWildcardType(override val bounds: TypeBounds) extends Type with BoundedWildcardTypeApi { + override def isWildcard = true + override def safeToString: String = "?" + bounds + override def kind = "BoundedWildcardType" + } + + object BoundedWildcardType extends BoundedWildcardTypeExtractor + + /** 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 with ThisTypeApi { + assert(sym.isClass) + //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.isOmittablePrefix) "" + else if (sym.isModuleClass) sym.fullNameString + "." + else sym.nameString + ".this." + override def safeToString: String = + if (sym.isEffectiveRoot) "" + sym.name + else super.safeToString + override def narrow: Type = this + override def kind = "ThisType" + } + + final class UniqueThisType(sym: Symbol) extends ThisType(sym) with UniqueType { } + + object ThisType extends ThisTypeExtractor { + def apply(sym: Symbol): Type = { + if (!phase.erasedTypes) unique(new UniqueThisType(sym)) + 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. + */ + abstract case class SingleType(pre: Type, sym: Symbol) extends SingletonType with SingleTypeApi { + override val isTrivial: Boolean = pre.isTrivial + override def isGround = sym.isPackageClass || pre.isGround + + // override def isNullable = underlying.isNullable + override def isNotNull = underlying.isNotNull + private[reflect] var underlyingCache: Type = NoType + private[reflect] var underlyingPeriod = NoPeriod + override def underlying: Type = { + val cache = underlyingCache + if (underlyingPeriod == currentPeriod && cache != null) cache + else { + defineUnderlyingOfSingleType(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 = ( + if (sym.skipPackageObject.isOmittablePrefix) "" + else if (sym.isPackageObjectOrClass) pre.prefixString + else pre.prefixString + sym.nameString + "." + ) + override def kind = "SingleType" + } + + final class UniqueSingleType(pre: Type, sym: Symbol) extends SingleType(pre, sym) with UniqueType { } + + object SingleType extends SingleTypeExtractor { + def apply(pre: Type, sym: Symbol): Type = { + unique(new UniqueSingleType(pre, sym)) + } + } + + protected def defineUnderlyingOfSingleType(tpe: SingleType) = { + val period = tpe.underlyingPeriod + if (period != currentPeriod) { + tpe.underlyingPeriod = currentPeriod + if (!isValid(period)) { + // [Eugene to Paul] needs review + tpe.underlyingCache = if (tpe.sym == NoSymbol) ThisType(rootMirror.RootClass) else tpe.pre.memberType(tpe.sym).resultType; + assert(tpe.underlyingCache ne tpe, tpe) + } + } + } + + abstract case class SuperType(thistpe: Type, supertpe: Type) extends SingletonType with SuperTypeApi { + 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("""\bthis\.$""", "super.") + override def narrow: Type = thistpe.narrow + override def kind = "SuperType" + } + + final class UniqueSuperType(thistp: Type, supertp: Type) extends SuperType(thistp, supertp) with UniqueType { } + + object SuperType extends SuperTypeExtractor { + def apply(thistp: Type, supertp: Type): Type = { + if (phase.erasedTypes) supertp + else unique(new UniqueSuperType(thistp, supertp)) + } + } + + /** A class for the bounds of abstract types and type parameters + */ + abstract case class TypeBounds(lo: Type, hi: Type) extends SubType with TypeBoundsApi { + 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 + } + private def lowerString = if (emptyLowerBound) "" else " >: " + lo + private def upperString = if (emptyUpperBound) "" else " <: " + hi + private def emptyLowerBound = lo.typeSymbolDirect eq NothingClass + private def emptyUpperBound = hi.typeSymbolDirect eq AnyClass + def isEmptyBounds = emptyLowerBound && emptyUpperBound + + // override def isNullable: Boolean = NullClass.tpe <:< lo; + override def safeToString = lowerString + upperString + override def kind = "TypeBoundsType" + } + + final class UniqueTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) with UniqueType { } + + object TypeBounds extends TypeBoundsExtractor { + 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 UniqueTypeBounds(lo, hi)).asInstanceOf[TypeBounds] + } + } + + /** A common base class for intersection types and class types + */ + abstract class CompoundType extends Type { + + private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ + private[reflect] var baseTypeSeqPeriod = NoPeriod + private[reflect] var baseClassesCache: List[Symbol] = _ + private[reflect] var baseClassesPeriod = NoPeriod + + override def baseTypeSeq: BaseTypeSeq = { + val cached = baseTypeSeqCache + if (baseTypeSeqPeriod == currentPeriod && cached != null && cached != undetBaseTypeSeq) + cached + else { + defineBaseTypeSeqOfCompoundType(this) + if (baseTypeSeqCache eq undetBaseTypeSeq) + throw new RecoverableCyclicReference(typeSymbol) + + baseTypeSeqCache + } + } + + override def baseTypeSeqDepth: Int = baseTypeSeq.maxDepth + + override def baseClasses: List[Symbol] = { + val cached = baseClassesCache + if (baseClassesPeriod == currentPeriod && cached != null) cached + else { + defineBaseClassesOfCompoundType(this) + if (baseClassesCache eq null) + throw new RecoverableCyclicReference(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.exists(_.isPossibleInRefinement) + + // override def isNullable: Boolean = + // parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType); + + override def safeToString: String = parentsString(parents) + ( + (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + decls.mkString("{", "; ", "}") else "") + ) + } + + protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = { + val period = tpe.baseTypeSeqPeriod + if (period != currentPeriod) { + tpe.baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + if (tpe.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 <- tpe.parents) + for (t <- p) t match { + case tv: TypeVar => tvs += tv + case _ => + } + val varToParamMap: Map[Type, Symbol] = + mapFrom[TypeVar, Type, Symbol](tvs.toList)(_.origin.typeSymbol.cloneSymbol) + 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(tpe.asInstanceOf[RefinedType], tpe.parents map varToParam, varToParam mapOver tpe.decls).baseTypeSeq + tpe.baseTypeSeqCache = bts lateMap paramToVar + } else { + incCounter(compoundBaseTypeSeqCount) + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) + tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) + else + compoundBaseTypeSeq(tpe) + // [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 (tpe.baseTypeSeqCache eq undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) + } + + protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = { + def computeBaseClasses: List[Symbol] = + if (tpe.parents.isEmpty) List(tpe.typeSymbol) + else { + //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = tpe.firstParent + var mixins = tpe.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 + } + tpe.typeSymbol :: bcs + } + val period = tpe.baseClassesPeriod + if (period != currentPeriod) { + tpe.baseClassesPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + tpe.baseClassesCache = null + tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) + } + } + if (tpe.baseClassesCache eq null) + throw new TypeError("illegal cyclic reference involving " + tpe.typeSymbol) + } + + /** 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 with RefinedTypeApi { + + override def isHigherKinded = ( + parents.nonEmpty && + (parents forall (_.isHigherKinded)) && + !phase.erasedTypes + ) + + override def typeParams = + if (isHigherKinded) firstParent.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) + + final override def normalize: Type = + if (phase.erasedTypes) normalizeImpl + else { + if (normalized eq null) normalized = normalizeImpl + normalized + } + + private var normalized: Type = _ + private def normalizeImpl = { + // TODO see comments around def intersectionType and def merge + def flatten(tps: List[Type]): List[Type] = tps flatMap { case RefinedType(parents, ds) if ds.isEmpty => flatten(parents) case tp => List(tp) } + val flattened = flatten(parents).distinct + if (decls.isEmpty && flattened.tail.isEmpty) { + flattened.head + } else if (flattened != parents) { + refinedType(flattened, if (typeSymbol eq NoSymbol) NoSymbol else typeSymbol.owner, decls, NoPosition) + } else if (isHigherKinded) { + // 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 + 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" + } + + final class RefinedType0(parents: List[Type], decls: Scope, clazz: Symbol) extends RefinedType(parents, decls) { + override def typeSymbol = clazz + } + + object RefinedType extends RefinedTypeExtractor { + def apply(parents: List[Type], decls: Scope, clazz: Symbol): RefinedType = + new RefinedType0(parents, decls, clazz) + } + + /** Overridden in reflection compiler */ + def validateClassInfo(tp: ClassInfoType) {} + + /** A class representing a class info + */ + case class ClassInfoType( + override val parents: List[Type], + override val decls: Scope, + override val typeSymbol: Symbol) extends CompoundType with ClassInfoTypeApi + { + validateClassInfo(this) + + /** 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 + */ + private[scala] 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 + * Syncnote: This var need not be protected with synchronized, because + * it is accessed only from expansiveRefs, which is called only from + * Typer. + */ + private var refs: Array[RefMap] = _ + + /** The initialization state of the class: UnInialized --> Initializing --> Initialized + * Syncnote: This var need not be protected with synchronized, because + * it is accessed only from expansiveRefs, which is called only from + * Typer. + */ + 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). + } + + private object enterRefs extends TypeMap { + private var tparam: Symbol = _ + + def apply(tp: Type): Type = { + tp match { + case tr @ TypeRef(_, sym, args) if args.nonEmpty => + val tparams = tr.initializedTypeParams + if (settings.debug.value && !sameLength(tparams, args)) + debugwarn("Mismatched zip in computeRefs(): " + sym.info.typeParams + ", " + args) + + foreach2(tparams, args) { (tparam1, arg) => + if (arg contains tparam) { + addRef(NonExpansive, tparam, tparam1) + if (arg.typeSymbol != tparam) + addRef(Expansive, tparam, tparam1) + } + } + case _ => + } + mapOver(tp) + } + def enter(tparam0: Symbol, parent: Type) { + this.tparam = tparam0 + this(parent) + } + } + + /** Compute initial (one-step) references and set state to `Initializing`. + */ + private def computeRefs() { + refs = Array(Map(), Map()) + typeSymbol.typeParams foreach { tparam => + parents foreach { p => + enterRefs.enter(tparam, 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" + + override def safeToString = + if (settings.debug.value || decls.size > 1) + formattedToString + else + super.safeToString + + /** A nicely formatted string with newlines and such. + */ + def formattedToString: String = + parents.mkString("\n with ") + + (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + decls.mkString(" {\n ", "\n ", "\n}") else "") + } + + object ClassInfoType extends ClassInfoTypeExtractor + + 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 with ConstantTypeApi { + 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" + } + + final class UniqueConstantType(value: Constant) 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 + } + + object ConstantType extends ConstantTypeExtractor { + def apply(value: Constant): ConstantType = { + val tpe = new UniqueConstantType(value) + if (value.tag == ClazzTag) { + // if we carry a classOf, we might be in trouble + // http://groups.google.com/group/scala-internals/browse_thread/thread/45185b341aeb6a30 + // I don't have time for a thorough fix, so I put a hacky workaround here + val alreadyThere = uniques findEntry tpe + if ((alreadyThere ne null) && (alreadyThere ne tpe) && (alreadyThere.toString != tpe.toString)) { + // we need to remove a stale type that has the same hashcode as we do + // HashSet doesn't support removal, and this makes our task non-trivial + // also we cannot simply recreate it, because that'd skew hashcodes (that change over time, omg!) + // the only solution I can see is getting into the underlying array and sneakily manipulating it + val ftable = uniques.getClass.getDeclaredFields().find(f => f.getName endsWith "table").get + ftable.setAccessible(true) + val table = ftable.get(uniques).asInstanceOf[Array[AnyRef]] + def overwrite(hc: Int, x: Type) { + def index(x: Int): Int = math.abs(x % table.length) + var h = index(hc) + var entry = table(h) + while (entry ne null) { + if (x == entry) + table(h) = x + h = index(h + 1) + entry = table(h) + } + } + overwrite(tpe.##, tpe) + } + } + unique(tpe).asInstanceOf[ConstantType] + } + } + + /* Syncnote: The `volatile` var and `pendingVolatiles` mutable set need not be protected + * with synchronized, because they are accessed only from isVolatile, which is called only from + * Typer. + */ + private var volatileRecursions: Int = 0 + private val pendingVolatiles = new mutable.HashSet[Symbol] + + class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) with UniqueType { + require(args0.nonEmpty, this) + + /** No unapplied type params size it has (should have) equally as many args. */ + override def isHigherKinded = false + override def typeParams = Nil + + override def transform(tp: Type): Type = { + // This situation arises when a typevar is encountered for which + // too little information is known to determine its kind, and + // it later turns out not to have kind *. See SI-4070. Only + // logging it for now. + if (sym.typeParams.size != args.size) + log("!!! %s.transform(%s), but tparams.isEmpty and args=".format(this, tp, args)) + + asSeenFromOwner(tp).instantiateTypeParams(sym.typeParams, args) + } + + // 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. + override def typeConstructor = TypeRef(pre, sym, Nil) + } + + class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { + require(sym.isModuleClass, sym) + private[this] var narrowedCache: Type = _ + override def isStable = true + override def narrow = { + if (narrowedCache eq null) + narrowedCache = singleType(pre, sym.sourceModule) + + narrowedCache + } + final override def isNotNull = true + override protected def finishPrefix(rest: String) = objectPrefix + rest + override def directObjectString = super.safeToString + override def toLongString = toString + override def safeToString = narrow.toString + } + class PackageTypeRef(pre0: Type, sym0: Symbol) extends ModuleTypeRef(pre0, sym0) { + require(sym.isPackageClass, sym) + override protected def finishPrefix(rest: String) = packagePrefix + rest + } + class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { + require(sym.isRefinementClass, sym) + + // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers + override protected def normalizeImpl: Type = sym.info.normalize + override protected def finishPrefix(rest: String) = "" + thisInfo + } + + class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) with UniqueType { + // 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, but 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 = typeParams.nonEmpty + override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams + private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym) + + override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = + if (isHigherKinded) { + if (sameLength(formals intersect typeParams, typeParams)) + copyTypeRef(this, pre, sym, actuals) + // partial application (needed in infer when bunching type arguments from classes and methods together) + else + copyTypeRef(this, pre, sym, dummyArgs).instantiateTypeParams(formals, actuals) + } + else + super.instantiateTypeParams(formals, actuals) + + override def transform(tp: Type): Type = { + val res = asSeenFromOwner(tp) + if (isHigherKinded && !isRaw) + res.instantiateTypeParams(typeParams, dummyArgs) + else + res + } + + override def transformInfo(tp: Type): Type = + appliedType(asSeenFromOwner(tp), dummyArgs) + + override def narrow = + if (sym.isModuleClass) singleType(pre, sym.sourceModule) + else super.narrow + + override def typeConstructor = this + // eta-expand, subtyping relies on eta-expansion of higher-kinded types + + override protected def normalizeImpl: Type = + if (isHigherKinded) etaExpand else super.normalizeImpl + } + + trait ClassTypeRef extends TypeRef { + // !!! There are scaladoc-created symbols arriving which violate this require. + // require(sym.isClass, sym) + + override def baseType(clazz: Symbol): Type = + if (sym == clazz) this + else transform(sym.info.baseType(clazz)) + } + + trait NonClassTypeRef extends TypeRef { + require(sym.isNonClassType, sym) + + /* Syncnote: These are pure caches for performance; no problem to evaluate these + * several times. Hence, no need to protected with synchronzied in a mutli-threaded + * usage scenario. + */ + private var relativeInfoCache: Type = _ + private var memberInfoCache: Type = _ + + private[Types] def relativeInfo = { + val memberInfo = pre.memberInfo(sym) + if (relativeInfoCache == null || (memberInfo ne memberInfoCache)) { + memberInfoCache = memberInfo + relativeInfoCache = transformInfo(memberInfo) + } + relativeInfoCache + } + + override def baseType(clazz: Symbol): Type = + if (sym == clazz) this else baseTypeOfNonClassTypeRef(this, clazz) + } + + protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = try { + basetypeRecursions += 1 + if (basetypeRecursions < LogPendingBaseTypesThreshold) + tpe.relativeInfo.baseType(clazz) + else if (pendingBaseTypes contains tpe) + if (clazz == AnyClass) clazz.tpe else NoType + else + try { + pendingBaseTypes += tpe + tpe.relativeInfo.baseType(clazz) + } finally { + pendingBaseTypes -= tpe + } + } finally { + basetypeRecursions -= 1 + } + + trait AliasTypeRef extends NonClassTypeRef { + require(sym.isAliasType, sym) + + override def dealias = if (typeParamsMatchArgs) betaReduce.dealias else super.dealias + override def isStable = normalize.isStable + override def isVolatile = normalize.isVolatile + override def narrow = normalize.narrow + override def thisInfo = normalize + override def prefix = if (this ne normalize) normalize.prefix else pre + override def termSymbol = if (this ne normalize) normalize.termSymbol else super.termSymbol + override def typeSymbol = if (this ne normalize) normalize.typeSymbol else sym + + // beta-reduce, but don't do partial application -- cycles have been checked in typeRef + override protected def normalizeImpl = + if (typeParamsMatchArgs) betaReduce.normalize + else if (isHigherKinded) super.normalizeImpl + else ErrorType + + // 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. + // + // this crashes pos/depmet_implicit_tpbetareduce.scala + // appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner) + def betaReduce = transform(sym.info.resultType) + + // #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 + override def coevolveSym(pre1: Type): Symbol = + if (pre eq pre1) sym else (pre, pre1) match { + // don't look at parents -- it would be an error to override alias types anyway + case (RefinedType(_, _), RefinedType(_, decls1)) => decls1 lookup sym.name + // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre? + case _ => sym + } + override def kind = "AliasTypeRef" + } + + trait AbstractTypeRef extends NonClassTypeRef { + require(sym.isAbstractType, sym) + + /** Syncnote: Pure performance caches; no need to synchronize in multi-threaded environment + */ + private var symInfoCache: Type = _ + private var thisInfoCache: Type = _ + + override def isVolatile = { + // 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 def thisInfo = { + val symInfo = sym.info + if (thisInfoCache == null || (symInfo ne symInfoCache)) { + symInfoCache = symInfo + thisInfoCache = transformInfo(symInfo) match { + // If a subtyping cycle is not detected here, we'll likely enter an infinite + // loop before a sensible error can be issued. SI-5093 is one example. + case x: SubType if x.supertype eq this => + throw new RecoverableCyclicReference(sym) + case tp => tp + } + } + thisInfoCache + } + override def isStable = bounds.hi.typeSymbol isSubClass SingletonClass + override def bounds = thisInfo.bounds + // def transformInfo(tp: Type): Type = appliedType(tp.asSeenFrom(pre, sym.owner), typeArgsOrDummies) + override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this + override def kind = "AbstractTypeRef" + } + + /** 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.typeParams.nonEmpty, but args.isEmpty + */ + abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends Type with TypeRefApi { + private[reflect] var parentsCache: List[Type] = _ + private[reflect] var parentsPeriod = NoPeriod + private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ + private[reflect] var baseTypeSeqPeriod = NoPeriod + private var normalized: Type = _ + + // @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 + + // eta-expand, subtyping relies on eta-expansion of higher-kinded types + protected def normalizeImpl: Type = if (isHigherKinded) etaExpand else super.normalize + + // TODO: test case that is compiled in a specific order and in different runs + final override def normalize: Type = { + // arises when argument-dependent types are approximated (see def depoly in implicits) + if (pre eq WildcardType) WildcardType + else if (phase.erasedTypes) normalizeImpl + else { + if (normalized eq null) + normalized = normalizeImpl + normalized + } + } + + override def isGround = ( + sym.isPackageClass + || pre.isGround && args.forall(_.isGround) + ) + + def etaExpand: Type = { + // must initialise symbol, see test/files/pos/ticket0137.scala + val tpars = initializedTypeParams + if (tpars.isEmpty) this + else typeFunAnon(tpars, copyTypeRef(this, pre, sym, tpars map (_.tpeHK))) // todo: also beta-reduce? + } + + // only need to rebind type aliases, as typeRef already handles abstract types + // (they are allowed to be rebound more liberally) + def coevolveSym(pre1: Type): Symbol = sym + + //@M! use appliedType on the polytype that represents the bounds (or if aliastype, the rhs) + def transformInfo(tp: Type): Type = appliedType(asSeenFromOwner(tp), args) + + def thisInfo = sym.info + def initializedTypeParams = sym.info.typeParams + def typeParamsMatchArgs = sameLength(initializedTypeParams, args) + def asSeenFromOwner(tp: Type) = tp.asSeenFrom(pre, sym.owner) + + override def baseClasses = thisInfo.baseClasses + override def baseTypeSeqDepth = baseTypeSeq.maxDepth + override def isStable = (sym eq NothingClass) || (sym eq SingletonClass) + override def prefix = pre + override def termSymbol = super.termSymbol + override def termSymbolDirect = super.termSymbol + override def typeArgs = args + override def typeOfThis = transform(sym.typeOfThis) + override def typeSymbol = sym + override def typeSymbolDirect = sym + + override lazy val isTrivial: Boolean = + !sym.isTypeParameter && pre.isTrivial && args.forall(_.isTrivial) + + override def isNotNull = + sym.isModuleClass || sym == NothingClass || (sym isNonBottomSubClass NotNullClass) || super.isNotNull + + override def parents: List[Type] = { + val cache = parentsCache + if (parentsPeriod == currentPeriod && cache != null) cache + else { + defineParentsOfTypeRef(this) + parentsCache + } + } + + override def decls: Scope = { + sym.info match { + case TypeRef(_, sym1, _) => + assert(sym1 != sym, this) // @MAT was != typeSymbol + case _ => + } + thisInfo.decls + } + + protected[Types] def baseTypeSeqImpl: BaseTypeSeq = sym.info.baseTypeSeq map transform + + override def baseTypeSeq: BaseTypeSeq = { + val cache = baseTypeSeqCache + if (baseTypeSeqPeriod == currentPeriod && cache != null && cache != undetBaseTypeSeq) + cache + else { + defineBaseTypeSeqOfTypeRef(this) + if (baseTypeSeqCache == undetBaseTypeSeq) + throw new RecoverableCyclicReference(sym) + + baseTypeSeqCache + } + } + + // ensure that symbol is not a local copy with a name coincidence + private def needsPreString = ( + settings.debug.value + || !shorthands(sym.fullName) + || sym.ownerChain.exists(s => !s.isClass) + ) + private def preString = if (needsPreString) pre.prefixString else "" + private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") + + def refinementString = ( + if (sym.isStructuralRefinement) ( + decls filter (sym => sym.isPossibleInRefinement && sym.isPublic) + map (_.defString) + mkString("{", "; ", "}") + ) + else "" + ) + + protected def finishPrefix(rest: String) = ( + if (sym.isInitialized && sym.isAnonymousClass && !phase.erasedTypes) + parentsString(thisInfo.parents) + refinementString + else rest + ) + private def customToString = sym match { + case RepeatedParamClass => args.head + "*" + case ByNameParamClass => "=> " + args.head + case _ => + def targs = normalize.typeArgs + + if (isFunctionType(this)) { + // Aesthetics: printing Function1 as T => R rather than (T) => R + // ...but only if it's not a tuple, so ((T1, T2)) => R is distinguishable + // from (T1, T2) => R. + targs match { + case in :: out :: Nil if !isTupleType(in) => + // A => B => C should be (A => B) => C or A => (B => C) + val in_s = if (isFunctionType(in)) "(" + in + ")" else "" + in + val out_s = if (isFunctionType(out)) "(" + out + ")" else "" + out + in_s + " => " + out_s + case xs => + xs.init.mkString("(", ", ", ")") + " => " + xs.last + } + } + else if (isTupleType(this)) + targs.mkString("(", ", ", if (hasLength(targs, 1)) ",)" else ")") + else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne this.normalize)) + "" + normalize + else + "" + } + override def safeToString = { + val custom = if (settings.debug.value) "" else customToString + if (custom != "") custom + else finishPrefix(preString + sym.nameString + argsString) + } + override def prefixString = "" + ( + if (settings.debug.value) + super.prefixString + else if (sym.isOmittablePrefix) + "" + else if (sym.isPackageClass || sym.isPackageObjectOrClass) + sym.skipPackageObject.fullName + "." + else if (isStable && nme.isSingletonName(sym.name)) + tpnme.dropSingletonName(sym.name) + "." + else + super.prefixString + ) + override def kind = "TypeRef" + } + + object TypeRef extends TypeRefExtractor { + def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({ + if (args.nonEmpty) { + if (sym.isAliasType) new ArgsTypeRef(pre, sym, args) with AliasTypeRef + else if (sym.isAbstractType) new ArgsTypeRef(pre, sym, args) with AbstractTypeRef + else new ArgsTypeRef(pre, sym, args) with ClassTypeRef + } + else { + if (sym.isAliasType) new NoArgsTypeRef(pre, sym) with AliasTypeRef + else if (sym.isAbstractType) new NoArgsTypeRef(pre, sym) with AbstractTypeRef + else if (sym.isRefinementClass) new RefinementTypeRef(pre, sym) + else if (sym.isPackageClass) new PackageTypeRef(pre, sym) + else if (sym.isModuleClass) new ModuleTypeRef(pre, sym) + else new NoArgsTypeRef(pre, sym) with ClassTypeRef + } + }) + } + + protected def defineParentsOfTypeRef(tpe: TypeRef) = { + val period = tpe.parentsPeriod + if (period != currentPeriod) { + tpe.parentsPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + tpe.parentsCache = tpe.thisInfo.parents map tpe.transform + } else if (tpe.parentsCache == null) { // seems this can happen if things are corrupted enough, see #2641 + tpe.parentsCache = List(AnyClass.tpe) + } + } + } + + protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = { + val period = tpe.baseTypeSeqPeriod + if (period != currentPeriod) { + tpe.baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + incCounter(typerefBaseTypeSeqCount) + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl + } + } + if (tpe.baseTypeSeqCache == undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + tpe.sym) + } + + /** 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 with MethodTypeApi { + override def isTrivial: Boolean = isTrivial0 && (resultType eq resultType.withoutAnnotations) + private lazy val isTrivial0 = + resultType.isTrivial && params.forall{p => p.tpe.isTrivial && ( + !(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 = resultType.boundSyms ++ params + + override def resultType(actuals: List[Type]) = + if (isTrivial || phase.erasedTypes) resultType + else if (sameLength(actuals, params)) { + val idm = new InstantiateDependentMap(params, actuals) + val res = idm(resultType) + existentialAbstraction(idm.existentialsNeeded, res) + } + 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 = cloneSymbolsAtOwner(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" + } + + object MethodType extends MethodTypeExtractor + + class JavaMethodType(ps: List[Symbol], rt: Type) extends MethodType(ps, rt) { + override def isJava = true + } + + case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi { + override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations) + 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" + } + + object NullaryMethodType extends NullaryMethodTypeExtractor + + /** 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 with PolyTypeApi { + //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 = cloneSymbolsAtOwner(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" + } + + object PolyType extends PolyTypeExtractor + + /** A creator for existential types which flattens nested existentials. + */ + def newExistentialType(quantified: List[Symbol], underlying: Type): Type = + if (quantified.isEmpty) underlying + else underlying match { + case ExistentialType(qs, restpe) => newExistentialType(quantified ::: qs, restpe) + case _ => ExistentialType(quantified, underlying) + } + + case class ExistentialType(quantified: List[Symbol], + override val underlying: Type) extends RewrappingTypeProxy with ExistentialTypeApi + { + 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.tpeHK) + if (tpe1 eq param.tpeHK) 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) = + deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying) + + private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map { + case TypeRef(_, sym, _) if (qset contains sym) => + "_"+sym.infoString(sym.info) + case arg => + arg.toString + } + + /** An existential can only be printed with wildcards if: + * - the underlying type is a typeref + * - every quantified variable appears at most once as a type argument and + * nowhere inside a type argument + * - no quantified type argument contains a quantified variable in its bound + * - the typeref's symbol is not itself quantified + * - the prefix is not quanitified + */ + def isRepresentableWithWildcards = { + val qset = quantified.toSet + underlying match { + case TypeRef(pre, sym, args) => + def isQuantified(tpe: Type): Boolean = { + (tpe exists (t => qset contains t.typeSymbol)) || + tpe.typeSymbol.isRefinementClass && (tpe.parents exists isQuantified) + } + val (wildcardArgs, otherArgs) = args partition (arg => qset contains arg.typeSymbol) + wildcardArgs.distinct == wildcardArgs && + !(otherArgs exists (arg => isQuantified(arg))) && + !(wildcardArgs exists (arg => isQuantified(arg.typeSymbol.info.bounds))) && + !(qset contains sym) && + !isQuantified(pre) + case _ => false + } + } + + override def safeToString: String = { + def clauses = { + val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") + if (settings.explaintypes.value) "(" + str + ")" else str + } + underlying match { + case TypeRef(pre, sym, args) if !settings.debug.value && isRepresentableWithWildcards => + "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => + "(" + underlying + ")" + clauses + case _ => + "" + underlying + clauses + } + } + + override def cloneInfo(owner: Symbol) = + createFromClonedSymbolsAtOwner(quantified, owner, underlying)(newExistentialType) + + 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)) + } + } + } + + object ExistentialType extends ExistentialTypeExtractor + + /** 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" + } + + def overloadedType(pre: Type, alternatives: List[Symbol]): Type = + if (alternatives.tail.isEmpty) pre memberType alternatives.head + else OverloadedType(pre, alternatives) + + /** 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 + + object HasTypeMember { + def apply(name: TypeName, tp: Type): Type = { + val bound = refinedType(List(WildcardType), NoSymbol) + val bsym = bound.typeSymbol.newAliasType(name) + bsym setInfo tp + bound.decls enter bsym + bound + } + def unapply(tp: Type): Option[(TypeName, Type)] = tp match { + case RefinedType(List(WildcardType), Scope(sym)) => Some((sym.name.toTypeName, sym.info)) + case _ => None + } + } + + // Not used yet. + object HasTypeParams { + def unapply(tp: Type): Option[(List[Symbol], Type)] = tp match { + case AnnotatedType(_, tp, _) => unapply(tp) + case ExistentialType(tparams, qtpe) => Some((tparams, qtpe)) + case PolyType(tparams, restpe) => Some((tparams, restpe)) + case _ => None + } + } + + //@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 { + @inline final def trace[T](action: String, msg: => String)(value: T): T = { + if (traceTypeVars) { + val s = msg match { + case "" => "" + case str => "( " + str + " )" + } + Console.err.println("[%10s] %-25s%s".format(action, value, s)) + } + value + } + + /** Create a new TypeConstraint based on the given symbol. + */ + private def deriveConstraint(tparam: Symbol): TypeConstraint = { + /** Must force the type parameter's info at this point + * or things don't end well for higher-order type params. + * See SI-5359. + */ + val bounds = tparam.info.bounds + /** We can seed the type constraint with the type parameter + * bounds as long as the types are concrete. This should lower + * the complexity of the search even if it doesn't improve + * any results. + */ + if (propagateParameterBoundsToTypeVars) { + val exclude = bounds.isEmptyBounds || bounds.exists(_.typeSymbolDirect.isNonClassType) + + if (exclude) new TypeConstraint + else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds)) + } + else new TypeConstraint + } + def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) + def untouchable(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = true) + def apply(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = false) + def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil) + def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar = + createTypeVar(origin, constr, args, params, untouchable = false) + + /** This is the only place TypeVars should be instantiated. + */ + private def createTypeVar(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol], untouchable: Boolean): TypeVar = { + val tv = ( + if (args.isEmpty && params.isEmpty) { + if (untouchable) new TypeVar(origin, constr) with UntouchableTypeVar + else new TypeVar(origin, constr) + } + else if (args.size == params.size) { + if (untouchable) new AppliedTypeVar(origin, constr, params zip args) with UntouchableTypeVar + else new AppliedTypeVar(origin, constr, params zip args) + } + else if (args.isEmpty) { + if (untouchable) new HKTypeVar(origin, constr, params) with UntouchableTypeVar + else new HKTypeVar(origin, constr, params) + } + else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) + ) + + trace("create", "In " + tv.originLocation)(tv) + } + private def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar = + createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable) + } + + /** Repack existential types, otherwise they sometimes get unpacked in the + * wrong location (type inference comes up with an unexpected skolem) + */ + def repackExistential(tp: Type): Type = ( + if (tp == NoType) tp + else existentialAbstraction(existentialsInType(tp), tp) + ) + def containsExistential(tpe: Type) = + tpe exists (_.typeSymbol.isExistentiallyBound) + + def existentialsInType(tpe: Type) = ( + for (tp <- tpe ; if tp.typeSymbol.isExistentiallyBound) yield + tp.typeSymbol + ) + + /** Precondition: params.nonEmpty. (args.nonEmpty enforced structurally.) + */ + class HKTypeVar( + _origin: Type, + _constr: TypeConstraint, + override val params: List[Symbol] + ) extends TypeVar(_origin, _constr) { + + require(params.nonEmpty, this) + override def isHigherKinded = true + override protected def typeVarString = params.map(_.name).mkString("[", ", ", "]=>" + originName) + } + + /** Precondition: zipped params/args nonEmpty. (Size equivalence enforced structurally.) + */ + class AppliedTypeVar( + _origin: Type, + _constr: TypeConstraint, + zippedArgs: List[(Symbol, Type)] + ) extends TypeVar(_origin, _constr) { + + require(zippedArgs.nonEmpty, this) + + override def params: List[Symbol] = zippedArgs map (_._1) + override def typeArgs: List[Type] = zippedArgs map (_._2) + + override protected def typeVarString = ( + zippedArgs map { case (p, a) => p.name + "=" + a } mkString (origin + "[", ", ", "]") + ) + } + + trait UntouchableTypeVar extends TypeVar { + override def untouchable = true + override def isGround = true + override def registerTypeEquality(tp: Type, typeVarLHS: Boolean) = tp match { + case t: TypeVar if !t.untouchable => + t.registerTypeEquality(this, !typeVarLHS) + case _ => + super.registerTypeEquality(tp, typeVarLHS) + } + override def registerBound(tp: Type, isLowerBound: Boolean, isNumericBound: Boolean = false): Boolean = tp match { + case t: TypeVar if !t.untouchable => + t.registerBound(this, !isLowerBound, isNumericBound) + case _ => + super.registerBound(tp, isLowerBound, isNumericBound) + } + } + + /** A class representing a type variable: not used after phase `typer`. + * + * A higher-kinded TypeVar has params (Symbols) and typeArgs (Types). + * A TypeVar with nonEmpty typeArgs can only be instantiated by a higher-kinded + * type that can be applied to those args. A TypeVar is much like a TypeRef, + * except it has special logic for equality and subtyping. + * + * Precondition for this class, enforced structurally: args.isEmpty && params.isEmpty. + */ + class TypeVar( + val origin: Type, + val constr0: TypeConstraint + ) extends Type { + def untouchable = false // by other typevars + override def params: List[Symbol] = Nil + override def typeArgs: List[Type] = Nil + override def isHigherKinded = false + + /** The constraint associated with the variable + * Syncnote: Type variables are assumed to be used from only one + * thread. They are not exposed in api.Types and are used only locally + * in operations that are exposed from types. Hence, no syncing of `constr` + * or `encounteredHigherLevel` or `suspended` accesses should be necessary. + */ + var constr = constr0 + def instValid = constr.instValid + override def isGround = instValid && constr.inst.isGround + + /** 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 && typeArgs.isEmpty) + this + else if (newArgs.size == params.size) { + val tv = TypeVar(origin, constr, newArgs, params) + TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) + } + else + throw new Error("Invalid type application in TypeVar: " + params + ", " + newArgs) + ) + // newArgs.length may differ from args.length (could've been empty before) + // + // !!! @PP - I need an example of this, since this exception never triggers + // even though I am requiring the size match. + // + // 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 + + // When comparing to types containing skolems, remember the highest level + // of skolemization. If that highest level is higher than our initial + // skolemizationLevel, we can't re-use those skolems as the solution of this + // typevar, which means we'll need to repack our constr.inst into a fresh + // existential. + // were we compared to skolems at a higher skolemizationLevel? + // EXPERIMENTAL: value will not be considered unless enableTypeVarExperimentals is true + // see SI-5729 for why this is still experimental + private var encounteredHigherLevel = false + private def shouldRepackType = enableTypeVarExperimentals && encounteredHigherLevel + + // <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 + // if we were compared against later typeskolems, repack the existential, + // because skolems are only compatible if they were created at the same level + val res = if (shouldRepackType) repackExistential(tp) else tp + constr.inst = TypeVar.trace("setInst", "In " + originLocation + ", " + originName + "=" + res)(res) + } + + def addLoBound(tp: Type, isNumericBound: Boolean = false) { + assert(tp != this, tp) // 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[Types] 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) + + // side effect: adds the type to upper or lower bounds + def addBound(tp: Type) { + if (isLowerBound) addLoBound(tp, isNumericBound) + else addHiBound(tp, isNumericBound) + } + // swaps the arguments if it's an upper bound + def checkSubtype(tp1: Type, tp2: Type) = { + val lhs = if (isLowerBound) tp1 else tp2 + val rhs = if (isLowerBound) tp2 else tp1 + + if (isNumericBound) lhs weak_<:< rhs + else lhs <:< rhs + } + + /** 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) + true + } + ) + + /** 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(tpe: Type) = { + // The alias/widen variations are often no-ops. + val tpes = ( + if (isLowerBound) List(tpe, tpe.widen, tpe.dealias, tpe.widen.dealias).distinct + else List(tpe) + ) + tpes exists { tp => + val lhs = if (isLowerBound) tp.typeArgs else typeArgs + val rhs = if (isLowerBound) typeArgs else tp.typeArgs + + sameLength(lhs, rhs) && { + // 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) + isSubArgs(lhs, rhs, params) + } + } + } + + // There's a <: test taking place right now, where tp is a concrete type and this is a typevar + // attempting to satisfy that test. Either the test will be unsatisfiable, in which case + // registerBound will return false; or the upper or lower bounds of this type var will be + // supplemented with the type being tested against. + // + // Eventually the types which have accumulated in the upper and lower bounds will be lubbed + // (resp. glbbed) to instantiate the typevar. + // + // The only types which are eligible for unification are those with the same number of + // typeArgs as this typevar, or Any/Nothing, which are kind-polymorphic. For the upper bound, + // any parent or base type of `tp` may be tested here (leading to a corresponding relaxation + // in the upper bound.) The universe of possible glbs, being somewhat more infinite, is not + // addressed here: all lower bounds are retained and their intersection calculated when the + // bounds are solved. + // + // In a side-effect free universe, checking tp and tp.parents beofre checking tp.baseTypeSeq + // would be pointless. In this case, each check we perform causes us to lose specificity: in + // the end the best we'll do is the least specific type we tested against, since the typevar + // does not see these checks as "probes" but as requirements to fulfill. + // TODO: can the `suspended` flag be used to poke around without leaving a trace? + // + // So the strategy used here is to test first the type, then the direct parents, and finally + // to fall back on the individual base types. This warrants eventual re-examination. + + // AM: I think we could use the `suspended` flag to avoid side-effecting during unification + if (suspended) // constraint accumulation is disabled + checkSubtype(tp, origin) + else if (constr.instValid) // type var is already set + checkSubtype(tp, constr.inst) + else isRelatable(tp) && { + unifySimple || unifyFull(tp) || ( + // only look harder if our gaze is oriented toward Any + isLowerBound && ( + (tp.parents exists unifyFull) || ( + // @PP: Is it going to be faster to filter out the parents we just checked? + // That's what's done here but I'm not sure it matters. + tp.baseTypeSeq.toList.tail filterNot (tp.parents contains _) exists unifyFull + ) + ) + ) + } + } + + def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = { +// println("regTypeEq: "+(safeToString, debugString(tp), tp.getClass, if (typeVarLHS) "in LHS" else "in RHS", if (suspended) "ZZ" else if (constr.instValid) "IV" else "")) //@MDEBUG +// println("constr: "+ constr) + 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) + (constr isWithinBounds newInst) && { setInst(tp); true } + } + } + + /** + * `?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 = { + registerBound(HasTypeMember(sym.name.toTypeName, tp), false) + } + + private def isSkolemAboveLevel(tp: Type) = tp.typeSymbol match { + case ts: TypeSkolem => ts.level > level + case _ => false + } + // side-effects encounteredHigherLevel + private def containsSkolemAboveLevel(tp: Type) = + (tp exists isSkolemAboveLevel) && { encounteredHigherLevel = true ; true } + + /** 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) = ( + shouldRepackType // short circuit if we already know we've seen higher levels + || !containsSkolemAboveLevel(tp) // side-effects tracking boolean + || enableTypeVarExperimentals // -Xexperimental: always say we're relatable, track consequences + ) + + 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 tparamsOfSym(sym: Symbol) = sym.info match { + case PolyType(tparams, _) if tparams.nonEmpty => + tparams map (_.defString) mkString("[", ",", "]") + case _ => "" + } + def originName = origin.typeSymbolDirect.decodedName + def originLocation = { + val sym = origin.typeSymbolDirect + val encl = sym.owner.logicallyEnclosingMember + + // This should display somewhere between one and three + // things which enclose the origin: at most, a class, a + // a method, and a term. At least, a class. + List( + Some(encl.enclClass), + if (encl.isMethod) Some(encl) else None, + if (sym.owner.isTerm && (sym.owner != encl)) Some(sym.owner) else None + ).flatten map (s => s.decodedName + tparamsOfSym(s)) mkString "#" + } + private def levelString = if (settings.explaintypes.value) level else "" + protected def typeVarString = originName + override def safeToString = ( + if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>" + else if (constr.inst ne NoType) "=?" + constr.inst + else (if(untouchable) "!?" else "?") + levelString + originName + ) + 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, this) + TypeVar.trace("clone", originLocation)( + 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 `underlying`; + * 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 with AnnotatedTypeApi { + + assert(!annotations.isEmpty, "" + underlying) + + override protected def rewrap(tp: Type) = copy(underlying = tp) + + override def isTrivial: Boolean = isTrivial0 + private lazy val isTrivial0 = underlying.isTrivial && annotations.forall(_.isTrivial) + + override def safeToString = annotations.mkString(underlying + " @", " @", "") + + override def filterAnnotations(p: AnnotationInfo => Boolean): Type = { + val (yes, no) = annotations partition p + if (yes.isEmpty) underlying + else if (no.isEmpty) this + else copy(annotations = yes) + } + override def setAnnotations(annots: List[AnnotationInfo]): Type = + if (annots.isEmpty) underlying + else copy(annotations = annots) + + /** Add a number of annotations to this type */ + override def withAnnotations(annots: List[AnnotationInfo]): Type = + if (annots.isEmpty) this + else copy(annots ::: this.annotations) + + /** Remove any annotations from this type. + * TODO - is it allowed to nest AnnotatedTypes? If not then let's enforce + * that at creation. At the moment if they do ever turn up nested this + * recursively calls withoutAnnotations. + */ + override def withoutAnnotations = underlying.withoutAnnotations + + /** Set the self symbol */ + override def withSelfsym(sym: Symbol) = copy(selfsym = sym) + + /** Drop the annotations on the bounds, unless 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" + } + + /** Creator for AnnotatedTypes. It returns the underlying type if annotations.isEmpty + * rather than walking into the assertion. + */ + def annotatedType(annots: List[AnnotationInfo], underlying: Type, selfsym: Symbol = NoSymbol): Type = + if (annots.isEmpty) underlying + else AnnotatedType(annots, underlying, selfsym) + + object AnnotatedType extends AnnotatedTypeExtractor + + /** 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 De Bruijn index referring to a previous type argument. Only used + * as a serialization format. + */ + case class DeBruijnIndex(level: Int, idx: Int, args: List[Type]) extends Type { + override def safeToString: String = "De Bruijn index("+level+","+idx+")" + } + + /** A binder defining data associated with De Bruijn indices. Only used + * as a serialization format. + */ + case class DeBruijnBinder(pnames: List[Name], ptypes: List[Type], restpe: Type) extends Type { + override def safeToString = { + val kind = if (pnames.head.isTypeName) "poly" else "method" + "De Bruijn "+kind+"("+(pnames mkString ",")+";"+(ptypes mkString ",")+";"+restpe+")" + } + } + + abstract case class ErasedValueType(sym: Symbol) extends Type { + override def safeToString = sym.name+"$unboxed" + } + + final class UniqueErasedValueType(sym: Symbol) extends ErasedValueType(sym) with UniqueType + + object ErasedValueType { + def apply(sym: Symbol): Type = { + assert(sym ne NoSymbol, "ErasedValueType cannot be NoSymbol") + unique(new UniqueErasedValueType(sym)) + } + } + + /** 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" + } + + abstract class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType { + override def safeToString = + (if (typeParams.isEmpty) "" else typeParamsString(this)) + super.safeToString + } + + // def mkLazyType(tparams: Symbol*)(f: Symbol => Unit): LazyType = ( + // if (tparams.isEmpty) new LazyType { override def complete(sym: Symbol) = f(sym) } + // else new LazyPolyType(tparams.toList) { override def complete(sym: Symbol) = f(sym) } + // ) + +// Creators --------------------------------------------------------------- + + /** Rebind symbol `sym` to an overriding member in type `pre`. */ + private def rebind(pre: Type, sym: Symbol): Symbol = { + if (!sym.isOverridableMember || sym.owner == pre.typeSymbol) sym + else pre.nonPrivateMember(sym.name).suchThat(sym => sym.isType || sym.isStable) orElse 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(sym.moduleClass) + else { + var sym1 = rebind(pre, sym) + val pre1 = removeSuper(pre, sym1) + if (pre1 ne pre) sym1 = rebind(pre1, sym1) + SingleType(pre1, sym1) + } + } + + /** 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(pos) // TODO: why were we passing in NoPosition instead of pos? + 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, newScope, 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 modifyInfo (_ substThisAndSym(original.typeSymbol, resultThis, 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. + + val sym1 = if (sym.isAbstractType) rebind(pre, sym) else sym + // don't expand cyclical type alias + // we require that object is initialized, thus info.typeParams instead of typeParams. + if (sym1.isAliasType && sameLength(sym1.info.typeParams, args) && !sym1.lockOK) + throw new RecoverableCyclicReference(sym1) + + val pre1 = pre match { + case x: SuperType if sym1.isEffectivelyFinal || sym1.isDeferred => + x.thistpe + 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 => pre + case parent :: _ => parent + } + case _ => pre + } + if (pre eq pre1) TypeRef(pre, sym1, args) + else if (sym1.isAbstractType && !sym1.isClass) typeRef(pre1, rebind(pre1, sym1), args) + else typeRef(pre1, sym1, args) + } + + // Optimization to avoid creating unnecessary new typerefs. + def copyTypeRef(tp: Type, pre: Type, sym: Symbol, args: List[Type]): Type = tp match { + case TypeRef(pre0, sym0, _) if pre == pre0 && sym0.name == sym.name => + if (sym.isAliasType && sameLength(sym.info.typeParams, args) && !sym.lockOK) + throw new RecoverableCyclicReference(sym) + + TypeRef(pre, sym, args) + case _ => + typeRef(pre, sym, 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. + * + * !!! Repeated parent classes are not merged - is this a bug in the + * comment or in the code? + */ + def intersectionType(tps: List[Type], owner: Symbol): Type = tps match { + case tp :: Nil => tp + case _ => refinedType(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 tp :: Nil => tp + case _ => refinedType(tps, commonOwner(tps)) + } + +/**** This implementation to merge parents was checked in in commented-out + form and has languished unaltered for five years. I think we should + use it or lose it. + + 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 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), _) => copyTypeRef(tycon, pre, sym, Nil) //@M drop type args to Any/Nothing + case TypeRef(pre, sym, _) => copyTypeRef(tycon, pre, sym, args) + case PolyType(tparams, restpe) => restpe.instantiateTypeParams(tparams, args) + case ExistentialType(tparams, restpe) => newExistentialType(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)) + } + + /** Very convenient. */ + def appliedType(tyconSym: Symbol, args: Type*): Type = + appliedType(tyconSym.typeConstructor, args.toList) + + /** A creator for existential types where the type arguments, + * rather than being applied directly, are interpreted as the + * upper bounds of unknown types. For instance if the type argument + * list given is List(AnyRefClass), the resulting type would be + * e.g. Set[_ <: AnyRef] rather than Set[AnyRef] . + */ + def appliedTypeAsUpperBounds(tycon: Type, args: List[Type]): Type = { + tycon match { + case TypeRef(pre, sym, _) if sameLength(sym.typeParams, args) => + val eparams = typeParamsToExistentials(sym) + val bounds = args map (TypeBounds upper _) + foreach2(eparams, bounds)(_ setInfo _) + + newExistentialType(eparams, typeRef(pre, sym, eparams map (_.tpe))) + case _ => + appliedType(tycon, args) + } + } + + /** A creator and extractor 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). + * + * PP to AM: I've co-opted this for where I know tparams may well be empty, and + * expecting to get back `tpe` in such cases. Re being "forgiving" below, + * can we instead say this is the canonical creator for polyTypes which + * may or may not be poly? (It filched the standard "canonical creator" name.) + */ + object GenPolyType { + def apply(tparams: List[Symbol], tpe: Type): Type = ( + if (tparams nonEmpty) typeFun(tparams, tpe) + else tpe // it's okay to be forgiving here + ) + def unapply(tpe: Type): Option[(List[Symbol], Type)] = tpe match { + case PolyType(tparams, restpe) => Some((tparams, restpe)) + case _ => Some((Nil, tpe)) + } + } + def genPolyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) + + @deprecated("use genPolyType(...) instead", "2.10.0") + def polyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) + + /** 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 { + val tpe = deAlias(tpe0) + val tpe1 = new ExistentialExtrapolation(tparams) 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) } + } + } + newExistentialType(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) + } + } + } + + /** Substitutes the empty scope for any non-empty decls in the type. */ + object dropAllRefinements extends TypeMap { + def apply(tp: Type): Type = tp match { + case rt @ RefinedType(parents, decls) if !decls.isEmpty => + mapOver(copyRefinedType(rt, parents, EmptyScope)) + case ClassInfoType(parents, decls, clazz) if !decls.isEmpty => + mapOver(ClassInfoType(parents, EmptyScope, clazz)) + case _ => + mapOver(tp) + } + } + + /** Type with all top-level occurrences of abstract types replaced by their bounds */ + def abstractTypesToBounds(tp: Type): Type = tp match { // @M don't normalize here (compiler loops on pos/bug1090.scala ) + case TypeRef(_, sym, _) if sym.isAbstractType => + abstractTypesToBounds(tp.bounds.hi) + case TypeRef(_, sym, _) if sym.isAliasType => + abstractTypesToBounds(tp.normalize) + case rtp @ RefinedType(parents, decls) => + copyRefinedType(rtp, parents mapConserve abstractTypesToBounds, decls) + case AnnotatedType(_, underlying, _) => + abstractTypesToBounds(underlying) + case _ => + tp + } + + // Set to true for A* => Seq[A] + // (And it will only rewrite A* in method result types.) + // This is the pre-existing behavior. + // Or false for Seq[A] => Seq[A] + // (It will rewrite A* everywhere but method parameters.) + // This is the specified behavior. + protected def etaExpandKeepsStar = false + + object dropRepeatedParamType extends TypeMap { + def apply(tp: Type): Type = tp match { + case MethodType(params, restpe) => + MethodType(params, apply(restpe)) + case PolyType(tparams, restpe) => + PolyType(tparams, apply(restpe)) + case TypeRef(_, RepeatedParamClass, arg :: Nil) => + seqType(arg) + case _ => + if (etaExpandKeepsStar) tp else mapOver(tp) + } + } + + object toDeBruijn extends TypeMap { + private var paramStack: List[List[Symbol]] = Nil + def mkDebruijnBinder(params: List[Symbol], restpe: Type) = { + paramStack = params :: paramStack + try { + DeBruijnBinder(params map (_.name), params map (p => this(p.info)), this(restpe)) + } finally paramStack = paramStack.tail + } + def apply(tp: Type): Type = tp match { + case PolyType(tparams, restpe) => + mkDebruijnBinder(tparams, restpe) + case MethodType(params, restpe) => + mkDebruijnBinder(params, restpe) + case TypeRef(NoPrefix, sym, args) => + val level = paramStack indexWhere (_ contains sym) + if (level < 0) mapOver(tp) + else DeBruijnIndex(level, paramStack(level) indexOf sym, args mapConserve this) + case _ => + mapOver(tp) + } + } + + def fromDeBruijn(owner: Symbol) = new TypeMap { + private var paramStack: List[List[Symbol]] = Nil + def apply(tp: Type): Type = tp match { + case DeBruijnBinder(pnames, ptypes, restpe) => + val isType = pnames.head.isTypeName + val newParams = for (name <- pnames) yield + if (isType) owner.newTypeParameter(name.toTypeName) + else owner.newValueParameter(name.toTermName) + paramStack = newParams :: paramStack + try { + foreach2(newParams, ptypes)((p, t) => p setInfo this(t)) + val restpe1 = this(restpe) + if (isType) PolyType(newParams, restpe1) + else MethodType(newParams, restpe1) + } finally paramStack = paramStack.tail + case DeBruijnIndex(level, idx, args) => + TypeRef(NoPrefix, paramStack(level)(idx), args map this) + case _ => + mapOver(tp) + } + } + +// Hash consing -------------------------------------------------------------- + + private val initialUniquesCapacity = 4096 + private var uniques: util.HashSet[Type] = _ + private var uniqueRunId = NoRunId + + protected 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, avoidWidening0: Boolean = false) { + def this(lo0: List[Type], hi0: List[Type]) = this(lo0, hi0, NoType, NoType) + def this(bounds: TypeBounds) = this(List(bounds.lo), List(bounds.hi)) + def this() = this(List(), List()) + + /* Syncnote: Type constraints are assumed to be used from only one + * thread. They are not exposed in api.Types and are used only locally + * in operations that are exposed from types. Hence, no syncing of any + * variables should be ncessesary. + */ + + /** Guard these lists against AnyClass and NothingClass appearing, + * else loBounds.isEmpty will have different results for an empty + * constraint and one with Nothing as a lower bound. [Actually + * guarding addLoBound/addHiBound somehow broke raw types so it + * only guards against being created with them.] + */ + private var lobounds = lo0 filterNot (_.typeSymbolDirect eq NothingClass) + private var hibounds = hi0 filterNot (_.typeSymbolDirect eq AnyClass) + private var numlo = numlo0 + private var numhi = numhi0 + private var avoidWidening = avoidWidening0 + + def loBounds: List[Type] = if (numlo == NoType) lobounds else numlo :: lobounds + def hiBounds: List[Type] = if (numhi == NoType) hibounds else numhi :: hibounds + def avoidWiden: Boolean = avoidWidening + + 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 checkWidening(tp: Type) { + if(tp.isStable) avoidWidening = true + else tp match { + case HasTypeMember(_, _) => avoidWidening = true + case _ => + } + } + + def addHiBound(tp: Type, isNumericBound: Boolean = false) { + checkWidening(tp) + 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, avoidWidening) + tc.inst = inst + tc + } + + override def toString = { + val boundsStr = { + val lo = loBounds filterNot (_.typeSymbolDirect eq NothingClass) + val hi = hiBounds filterNot (_.typeSymbolDirect eq AnyClass) + val lostr = if (lo.isEmpty) Nil else List(lo.mkString(" >: (", ", ", ")")) + val histr = if (hi.isEmpty) Nil else List(hi.mkString(" <: (", ", ", ")")) + + lostr ++ histr mkString ("[", " | ", "]") + } + if (inst eq NoType) boundsStr + else boundsStr + " _= " + inst.safeToString + } + } + + class TypeUnwrapper(poly: Boolean, existential: Boolean, annotated: Boolean, nullary: Boolean) extends (Type => Type) { + def apply(tp: Type): Type = tp match { + case AnnotatedType(_, underlying, _) if annotated => apply(underlying) + case ExistentialType(_, underlying) if existential => apply(underlying) + case PolyType(_, underlying) if poly => apply(underlying) + case NullaryMethodType(underlying) if nullary => apply(underlying) + case tp => tp + } + } + class ClassUnwrapper(existential: Boolean) extends TypeUnwrapper(poly = true, existential, annotated = true, nullary = false) { + override def apply(tp: Type) = super.apply(tp.normalize) + } + + object unwrapToClass extends ClassUnwrapper(existential = true) { } + object unwrapToStableClass extends ClassUnwrapper(existential = false) { } + object unwrapWrapperTypes extends TypeUnwrapper(true, true, true, true) { } + + trait AnnotationFilter extends TypeMap { + def keepAnnotation(annot: AnnotationInfo): Boolean + + override def mapOver(annot: AnnotationInfo) = + if (keepAnnotation(annot)) super.mapOver(annot) + else UnmappableAnnotation + } + + trait KeepOnlyTypeConstraints extends AnnotationFilter { + // filter keeps only type constraint annotations + def keepAnnotation(annot: AnnotationInfo) = annot matches TypeConstraintClass + } + + trait VariantTypeMap extends TypeMap { + private[this] var _variance = 1 + + override def variance = _variance + def variance_=(x: Int) = _variance = x + + override protected def noChangeToSymbols(origSyms: List[Symbol]) = { + origSyms forall { sym => + val v = variance + if (sym.isAliasType) variance = 0 + val result = this(sym.info) + variance = v + result eq sym.info + } + } + + override protected 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 type */ + override def mapOver(tp: Type): Type = tp match { + case MethodType(params, result) => + variance = -variance + val params1 = mapOver(params) + variance = -variance + val result1 = this(result) + if ((params1 eq params) && (result1 eq result)) tp + 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 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 tr @ TypeRef(pre, sym, args) => + val pre1 = this(pre) + val args1 = + if (args.isEmpty) + args + else if (variance == 0) // fast & safe path: don't need to look at typeparams + args mapConserve this + else { + val tparams = sym.typeParams + if (tparams.isEmpty) args + else mapOverArgs(args, tparams) + } + if ((pre1 eq pre) && (args1 eq args)) tp + else copyTypeRef(tp, pre1, tr.coevolveSym(pre1), args1) + case _ => + super.mapOver(tp) + } + } + + // todo. move these into scala.reflect.api + + /** A prototype for mapping a function over all possible types + */ + abstract class TypeMap extends (Type => Type) { + def apply(tp: Type): Type + + /** Mix in VariantTypeMap if you want variances to be significant. + */ + def variance = 0 + + /** Map this function over given type */ + def mapOver(tp: Type): Type = tp match { + case tr @ TypeRef(pre, sym, args) => + val pre1 = this(pre) + val args1 = args mapConserve this + if ((pre1 eq pre) && (args1 eq args)) tp + else copyTypeRef(tp, pre1, tr.coevolveSym(pre1), 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) => + val params1 = mapOver(params) + val result1 = this(result) + if ((params1 eq params) && (result1 eq result)) tp + else copyMethodType(tp, params1, result1.substSym(params, params1)) + case PolyType(tparams, result) => + val tparams1 = mapOver(tparams) + 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) => + val lo1 = this(lo) + 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 newExistentialType(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 DeBruijnIndex(shift, idx, args) => + val args1 = args mapConserve this + if (args1 eq args) tp + else DeBruijnIndex(shift, idx, args1) +/* + case ErrorType => tp + case WildcardType => tp + case NoType => tp + case NoPrefix => tp + case ErasedSingleType(sym) => tp +*/ + case _ => + tp + // throw new Error("mapOver inapplicable for " + tp); + } + + protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = + args mapConserve this + + /** Called by mapOver to determine whether the original symbols can + * be returned, or whether they must be cloned. Overridden in VariantTypeMap. + */ + protected def noChangeToSymbols(origSyms: List[Symbol]) = + origSyms forall (sym => sym.info eq this(sym.info)) + + /** 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 newScopeWith(elems1: _*) + } + + /** Map this function over given list of symbols */ + def mapOver(origSyms: List[Symbol]): List[Symbol] = { + // fast path in case nothing changes due to map + if (noChangeToSymbols(origSyms)) origSyms + // map is not the identity --> do cloning properly + else cloneSymbolsAndModify(origSyms, TypeMap.this) + } + + def mapOver(annot: AnnotationInfo): AnnotationInfo = { + val AnnotationInfo(atp, args, assocs) = annot + 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)) annot + else if (args1.isEmpty && args.nonEmpty) UnmappableAnnotation // some annotation arg was unmappable + else AnnotationInfo(atp1, args1, assocs) setPos annot.pos + } + + def mapOverAnnotations(annots: List[AnnotationInfo]): List[AnnotationInfo] = { + val annots1 = annots mapConserve mapOver + if (annots1 eq annots) annots + else annots1 filterNot (_ eq UnmappableAnnotation) + } + + /** 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 mapConserve mapOver + if (args1 contains UnmappableTree) Nil + else args1 + } + + def mapOver(tree: Tree): Tree = + mapOver(tree, () => return UnmappableTree) + + /** 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) + } + } + } + + abstract class TypeTraverser extends TypeMap { + def traverse(tp: Type): Unit + def apply(tp: Type): Type = { traverse(tp); tp } + } + + abstract class TypeTraverserWithResult[T] extends TypeTraverser { + def result: T + def clear(): Unit + } + + abstract class TypeCollector[T](initial: T) extends TypeTraverser { + var result: T = _ + def collect(tp: Type) = { + result = initial + traverse(tp) + result + } + } + + /** A collector that tests for existential types appearing at given variance in a type + * @PP: Commenting out due to not being used anywhere. + */ + // class ContainsVariantExistentialCollector(v: Int) extends TypeCollector(false) with VariantTypeMap { + // variance = v + // + // def traverse(tp: Type) = tp match { + // case ExistentialType(_, _) if (variance == v) => result = true + // case _ => mapOver(tp) + // } + // } + // + // val containsCovariantExistentialCollector = new ContainsVariantExistentialCollector(1) + // val containsContravariantExistentialCollector = new ContainsVariantExistentialCollector(-1) + + def typeParamsToExistentials(clazz: Symbol, tparams: List[Symbol]): List[Symbol] = { + val eparams = mapWithIndex(tparams)((tparam, i) => + clazz.newExistential(newTypeName("?"+i), clazz.pos) setInfo tparam.info.bounds) + + eparams map (_ substInfo (tparams, eparams)) + } + def typeParamsToExistentials(clazz: Symbol): List[Symbol] = + typeParamsToExistentials(clazz, clazz.typeParams) + + // 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). + */ + def rawToExistential = new 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)) + existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) + } finally { + expanded -= sym + } + case _ => + mapOver(tp) + } + } + + /** Used by existentialAbstraction. + */ + class ExistentialExtrapolation(tparams: List[Symbol]) extends VariantTypeMap { + private val occurCount = mutable.HashMap[Symbol, Int]() + private def countOccs(tp: Type) = { + tp foreach { + case TypeRef(_, sym, _) => + if (tparams contains sym) + occurCount(sym) += 1 + case _ => () + } + } + def extrapolate(tpe: Type): Type = { + tparams foreach (t => occurCount(t) = 0) + countOccs(tpe) + for (tparam <- tparams) + countOccs(tparam.info) + + apply(tpe) + } + + def apply(tp: Type): Type = { + val tp1 = mapOver(tp) + if (variance == 0) tp1 + else tp1 match { + case TypeRef(pre, sym, args) if tparams contains 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.isBottomClass && 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) + } + + // 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. + override def mapOver(tree: Tree) = tree match { + case Ident(_) if tree.tpe.isStable => tree + case _ => super.mapOver(tree) + } + } + + 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 with KeepOnlyTypeConstraints { + var capturedSkolems: List[Symbol] = List() + var capturedParams: List[Symbol] = List() + var capturedPre = emptySymMap + + 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.name.toTermName, pre.typeSymbol.pos) // what symbol should really be used? + setInfo pre + ) + 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) + } + + 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) + + val symclazz = sym.owner + if (symclazz == clazz && !pre.widen.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) => + + def instParam(ps: List[Symbol], as: List[Type]): Type = + if (ps.isEmpty) { + if (forInteractive) { + val saved = settings.uniqid.value + try { + settings.uniqid.value = true + println("*** stale type parameter: " + tp + sym.locationString + " cannot be instantiated from " + pre.widen) + println("*** confused with params: " + sym + " in " + sym.owner + " not in " + ps + " of " + basesym) + println("*** stacktrace = ") + new Error().printStackTrace() + } finally settings.uniqid.value = saved + instParamRelaxed(basesym.typeParams, baseargs) + } else 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) + + /** Relaxed version of instParams which matches on names not symbols. + * This is a last fallback in interactive mode because races in calls + * from the IDE to the compiler may in rare cases lead to symbols referring + * to type parameters that are no longer current. + */ + def instParamRelaxed(ps: List[Symbol], as: List[Type]): Type = + if (ps.isEmpty) throwError + else if (sym.name == ps.head.name) + // @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 instParamRelaxed(ps.tail, as.tail) + + //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 + if (symclazz.tpe.parents.exists(_.isErroneous)) + ErrorType // don't be to overzealous with throwing exceptions, see #2641 + else + throw new Error( + "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) => + capturedSkolems = capturedSkolems union tparams + toInstance(qtpe, clazz) + case t => + 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 { + 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 + + protected def renameBoundSyms(tp: Type): Type = tp match { + case MethodType(ps, restp) => + createFromClonedSymbols(ps, restp)((ps1, tp1) => copyMethodType(tp, ps1, renameBoundSyms(tp1))) + case PolyType(bs, restp) => + createFromClonedSymbols(bs, restp)((ps1, tp1) => PolyType(ps1, renameBoundSyms(tp1))) + case ExistentialType(bs, restp) => + createFromClonedSymbols(bs, restp)(newExistentialType) + case _ => + tp + } + + def apply(tp0: Type): Type = if (from.isEmpty) tp0 else { + @tailrec 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) + + val boundSyms = tp0.boundSyms + val tp1 = if (boundSyms exists from.contains) 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) => copyTypeRef(fromtp, pre, sym, args) + case SingleType(pre, _) => singleType(pre, sym) + } + override def apply(tp: Type): Type = if (from.isEmpty) tp else { + @tailrec 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(copyTypeRef(tp, 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) = from indexOf sym match { + case -1 => None + case idx => Some(to(idx)) + } + + 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) => + from indexOf tree.symbol match { + case -1 => super.transform(tree) + case idx => + val totpe = to(idx) + if (totpe.isStable) tree.duplicate setType totpe + else giveup() + } + 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 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], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints { + private val actuals = actuals0.toIndexedSeq + private val existentials = new Array[Symbol](actuals.size) + def existentialsNeeded: List[Symbol] = existentials.filter(_ ne null).toList + + private object StableArg { + def unapply(param: Symbol) = Arg unapply param map actuals filter (tp => + tp.isStable && (tp.typeSymbol != NothingClass) + ) + } + private object Arg { + def unapply(param: Symbol) = Some(params indexOf param) filter (_ >= 0) + } + + def apply(tp: Type): Type = mapOver(tp) match { + // unsound to replace args by unstable actual #3873 + case SingleType(NoPrefix, StableArg(arg)) => arg + // (soundly) expand type alias selections on implicit arguments, + // see depmet_implicit_oopsla* test cases -- typically, `param.isImplicit` + case tp1 @ TypeRef(SingleType(NoPrefix, Arg(pid)), sym, targs) => + val arg = actuals(pid) + val res = typeRef(arg, sym, targs) + if (res.typeSymbolDirect.isAliasType) res.dealias else tp1 + // don't return the original `tp`, which may be different from `tp1`, + // due to dropping annotations + case tp1 => tp1 + } + + /* Return the type symbol for referencing a parameter inside the existential quantifier. + * (Only needed if the actual is unstable.) + */ + private def existentialFor(pid: Int) = { + if (existentials(pid) eq null) { + val param = params(pid) + existentials(pid) = ( + param.owner.newExistential(newTypeName(param.name + ".type"), param.pos, param.flags) + setInfo singletonBounds(actuals(pid)) + ) + } + existentials(pid) + } + + //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 = { + // TODO: this should be simplified; in the stable case, one can + // probably just use an Ident to the tree.symbol. + // + // @PP: That leads to failure here, where stuff no longer has type + // 'String @Annot("stuff")' but 'String @Annot(x)'. + // + // def m(x: String): String @Annot(x) = x + // val stuff = m("stuff") + // + // (TODO cont.) Why an existential in the non-stable case? + // + // @PP: In the following: + // + // def m = { val x = "three" ; val y: String @Annot(x) = x; y } + // + // m is typed as 'String @Annot(x) forSome { val x: String }'. + // + // Both examples are from run/constrained-types.scala. + object treeTrans extends Transformer { + override def transform(tree: Tree): Tree = tree.symbol match { + case StableArg(actual) => + gen.mkAttributedQualifier(actual, tree.symbol) + case Arg(pid) => + val sym = existentialFor(pid) + Ident(sym) copyAttrs tree setType typeRef(NoPrefix, sym, Nil) + case _ => + super.transform(tree) + } + } + 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(bounds)) + 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 + } + 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) + + arg + } + } + + /** A map to implement the `filter` method. */ + class FilterTypeCollector(p: Type => Boolean) extends TypeCollector[List[Type]](Nil) { + def withFilter(q: Type => Boolean) = new FilterTypeCollector(tp => p(tp) && q(tp)) + + override def collect(tp: Type) = super.collect(tp).reverse + + def traverse(tp: Type) { + if (p(tp)) result ::= tp + mapOver(tp) + } + } + + /** A map to implement the `collect` method. */ + class CollectTypeCollector[T](pf: PartialFunction[Type, T]) extends TypeCollector[List[T]](Nil) { + override def collect(tp: Type) = super.collect(tp).reverse + + def traverse(tp: Type) { + if (pf.isDefinedAt(tp)) result ::= pf(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) + } + } + } + + /** 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 = commonOwner(t :: Nil) + + /** 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 (tps.isEmpty) NoSymbol + else { + commonOwnerMap.clear() + tps foreach (commonOwnerMap traverse _) + if (commonOwnerMap.result ne null) commonOwnerMap.result else NoSymbol + } + } + + protected def commonOwnerMap: CommonOwnerMap = commonOwnerMapObj + + protected class CommonOwnerMap extends TypeTraverserWithResult[Symbol] { + var result: Symbol = _ + + def clear() { result = null } + + private def register(sym: Symbol) { + // First considered type is the trivial result. + if ((result eq null) || (sym eq NoSymbol)) + result = sym + else + while ((result ne NoSymbol) && (result ne sym) && !(sym isNestedIn result)) + result = result.owner + } + def traverse(tp: Type) = tp.normalize match { + case ThisType(sym) => register(sym) + case TypeRef(NoPrefix, sym, args) => register(sym.owner) ; args foreach traverse + case SingleType(NoPrefix, sym) => register(sym.owner) + case _ => mapOver(tp) + } + } + + private lazy val commonOwnerMapObj = new CommonOwnerMap + + 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.isRootSymbol || (pre eq NoPrefix) || (pre eq NoType) || sym.isPackageClass) + sym + else if (sym.isModuleClass) { + val sourceModule1 = adaptToNewRun(pre, sym.sourceModule) + + sourceModule1.moduleClass orElse sourceModule1.initialize.moduleClass orElse { + val msg = "Cannot adapt module class; sym = %s, sourceModule = %s, sourceModule.moduleClass = %s => sourceModule1 = %s, sourceModule1.moduleClass = %s" + debuglog(msg.format(sym, sym.sourceModule, sym.sourceModule.moduleClass, sourceModule1, sourceModule1.moduleClass)) + sym + } + } + else { + var rebind0 = pre.findMember(sym.name, BRIDGE, 0, true) orElse { + if (sym.isAliasType) throw missingAliasException + debugwarn(pre+"."+sym+" does no longer exist, phase = "+phase) + throw new MissingTypeControl // For build manager and presentation compiler purposes + } + /** 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)) { + debuglog("ADAPT1 pre = "+pre+", sym = "+sym.fullLocationString+", rebind = "+rebind0.fullLocationString) + 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) + debuglog( + "ADAPT2 pre = " + pre + + ", bcs.head = " + bcs.head + + ", sym = " + sym.fullLocationString + + ", rebind = " + rebind0.fullLocationString + ) + } + rebind0.suchThat(sym => sym.isType || sym.isStable) orElse { + debuglog("" + phase + " " +phase.flatClasses+sym.owner+sym.name+" "+sym.isType) + throw new MalformedType(pre, sym.nameString) + } + } + } + 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) + try { + val sym1 = adaptToNewRun(pre1, sym) + if ((pre1 eq pre) && (sym1 eq sym)) tp + else singleType(pre1, sym1) + } catch { + case _: MissingTypeControl => + tp + } + } + 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 if (sym1 == NoSymbol) { + debugwarn("adapt fail: "+pre+" "+pre1+" "+sym) + tp + } else { + copyTypeRef(tp, 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 => + // suspend TypeVars in types compared by =:=, + // since we don't want to mutate them simply to check whether a subtype test is pending + // in addition to making subtyping "more correct" for type vars, + // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion) + // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold) + @inline def suspend(tp: Type) = + if (tp.isGround) null else suspendTypeVarsInType(tp) + @inline def revive(suspension: List[TypeVar]) = + if (suspension ne null) suspension foreach (_.suspended = false) + + val suspensions = Array(tp1, stp.tp1, tp2, stp.tp2) map suspend + + val sameTypes = (tp1 =:= stp.tp1) && (tp2 =:= stp.tp2) + + suspensions foreach revive + + sameTypes + 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 && + forall3(args1, args2, sym1.typeParams) { (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(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(copyTypeRef(patType, 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 isSubPre(pre1: Type, pre2: Type, sym: Symbol) = + if ((pre1 ne pre2) && (pre1 ne NoPrefix) && (pre2 ne NoPrefix) && pre1 <:< pre2) { + if (settings.debug.value) println(s"new isSubPre $sym: $pre1 <:< $pre2") + true + } else + false + + 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) // !! I do not see how this would be warranted by the spec + tp2.typeSymbol.isPackageClass + else if (tp2 eq NoPrefix) // !! I do not see how this would be warranted by the spec + 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 _: SingleType => + return isSameType2(tp2, tp1) // put singleton type on the left, caught below + 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 TypeRef(pre2, sym2, Nil) => + if (sym2.isModuleClass && equalSymsAndPrefixes(st1.sym, st1.pre, sym2.sourceModule, pre2)) 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) + 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 => + return isSameTypes(mt1.paramTypes, mt2.paramTypes) && + mt1.resultType =:= mt2.resultType.substSym(mt2.params, mt1.params) && + 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) => + copyTypeRef(tp, 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) + })) + + 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 // !! I do not see how the "isPackageClass" would be warranted by the spec + 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) || + sym2.isAbstractType && isSubPre(pre1, pre2, sym2)))) && + 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 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) && + mt1.isImplicit == mt2.isImplicit && + matchingParams(params1, params2, mt1.isJava, mt2.isJava) && + (res1 <:< res2.substSym(params2, params1))) + // 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(pre1, sym1, _) => + sym1 match { + case NothingClass => true + case NullClass => + tp2 match { + case TypeRef(_, sym2, _) => + containsNull(sym2) + case _ => + isSingleType(tp2) && tp1 <:< tp2.widen + } + case _: ClassSymbol => + if (isRaw(sym1, tr1.args)) + isSubType(rawToExistential(tp1), tp2, depth) + else if (sym1.isModuleClass) tp2 match { + case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1.sourceModule, pre1, sym2, pre2) + case _ => false + } + else if (sym1.isRefinementClass) + isSubType(sym1.info, tp2, depth) + else false + + 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 + } + + private def containsNull(sym: Symbol): Boolean = + sym.isClass && sym != NothingClass && + !(sym isNonBottomSubClass AnyValClass) && + !(sym isNonBottomSubClass NotNullClass) + + /** 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 && containsNull(sym.owner) || + (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(_, _) => + false + case _ => + alwaysMatchSimple || tp1 =:= tp2 + } + tp1 match { + case mt1 @ MethodType(params1, res1) => + tp2 match { + case mt2 @ MethodType(params2, res2) => + // sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performancewise?) + mt1.isImplicit == mt2.isImplicit && + matchingParams(params1, params2, mt1.isJava, mt2.isJava) && + matchesQuantified(params1, params2, res1, res2) + case NullaryMethodType(res2) => + if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple) + else matchesType(tp1, res2, alwaysMatchSimple) + case ExistentialType(_, res2) => + alwaysMatchSimple && matchesType(tp1, res2, true) + case TypeRef(_, sym, Nil) => + params1.isEmpty && sym.isModuleClass && matchesType(res1, tp2, alwaysMatchSimple) + 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 TypeRef(_, sym, Nil) if sym.isModuleClass => + matchesType(res1, tp2, alwaysMatchSimple) + case _ => + matchesType(res1, tp2, alwaysMatchSimple) + } + case PolyType(tparams1, res1) => + tp2 match { + case PolyType(tparams2, res2) => + if ((tparams1 corresponds tparams2)(_ eq _)) + matchesType(res1, res2, alwaysMatchSimple) + else + 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 TypeRef(_, sym, Nil) if sym.isModuleClass => + tp2 match { + case MethodType(Nil, res2) => matchesType(tp1, res2, alwaysMatchSimple) + case NullaryMethodType(res2) => matchesType(tp1, res2, alwaysMatchSimple) + case _ => 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 = { + + 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 + foreach3(tvars, tparams, variances)((tvar2, tparam2, variance2) => { + val ok = (tparam2 != tparam) && ( + (bound contains tparam2) + || up && (tparam2.info.bounds.lo =:= tparam.tpeHK) + || !up && (tparam2.info.bounds.hi =:= tparam.tpeHK) + ) + if (ok) { + 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.tpeHK.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.tpeHK.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))) + foreach3(tvars, tparams, variances)(solveOne) + tvars forall (tvar => tvar.constr.isWithinBounds(tvar.constr.inst)) + } + + /** Do type arguments `targs` conform to formal parameters `tparams`? + */ + 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 --------------------------------------------------------- + + private def printLubMatrix(btsMap: Map[Type, List[Type]], depth: Int) { + import util.TableDef + import TableDef.Column + def str(tp: Type) = { + if (tp == NoType) "" + else { + val s = ("" + tp).replaceAll("""[\w.]+\.(\w+)""", "$1") + if (s.length < 60) s + else (s take 57) + "..." + } + } + + val sorted = btsMap.toList.sortWith((x, y) => x._1.typeSymbol isLess y._1.typeSymbol) + val maxSeqLength = sorted map (_._2.size) max + val padded = sorted map (_._2.padTo(maxSeqLength, NoType)) + val transposed = padded.transpose + + val columns: List[Column[List[Type]]] = mapWithIndex(sorted) { + case ((k, v), idx) => + Column(str(k), (xs: List[Type]) => str(xs(idx)), true) + } + + val tableDef = TableDef(columns: _*) + val formatted = tableDef.table(transposed) + println("** Depth is " + depth + "\n" + formatted) + } + + /** From a list of types, find any which take type parameters + * where the type parameter bounds contain references to other + * any types in the list (including itself.) + * + * @return List of symbol pairs holding the recursive type + * parameter and the parameter which references it. + */ + def findRecursiveBounds(ts: List[Type]): List[(Symbol, Symbol)] = { + if (ts.isEmpty) Nil + else { + val sym = ts.head.typeSymbol + require(ts.tail forall (_.typeSymbol == sym), ts) + for (p <- sym.typeParams ; in <- sym.typeParams ; if in.info.bounds contains p) yield + p -> in + } + } + + /** Given a matrix `tsBts` whose columns are basetype sequences (and the symbols `tsParams` that should be interpreted as type parameters in this matrix), + * compute its least sorted upwards closed upper bound relative to the following ordering <= between lists of types: + * + * xs <= ys iff forall y in ys exists x in xs such that x <: y + * + * @arg tsParams for each type in the original list of types `ts0`, its list of type parameters (if that type is a type constructor) + * (these type parameters may be referred to by type arguments in the BTS column of those types, + * and must be interpreted as bound variables; i.e., under a type lambda that wraps the types that refer to these type params) + * @arg tsBts a matrix whose columns are basetype sequences + * the first row is the original list of types for which we're computing the lub + * (except that type constructors have been applied to their dummyArgs) + * @See baseTypeSeq for a definition of sorted and upwards closed. + */ + private def lubList(ts: List[Type], depth: Int): List[Type] = { + // Matching the type params of one of the initial types means dummies. + val initialTypeParams = ts map (_.typeParams) + def isHotForTs(xs: List[Type]) = initialTypeParams contains xs.map(_.typeSymbol) + + def elimHigherOrderTypeParam(tp: Type) = tp match { + case TypeRef(pre, sym, args) if args.nonEmpty && isHotForTs(args) => tp.typeConstructor + case _ => tp + } + var lubListDepth = 0 + def loop(tsBts: List[List[Type]]): List[Type] = { + lubListDepth += 1 + + if (tsBts.isEmpty || tsBts.exists(_.isEmpty)) Nil + else if (tsBts.tail.isEmpty) tsBts.head + else { + // ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts. + // Invariant: all symbols "under" (closer to the first row) the frontier + // are smaller (according to _.isLess) than the ones "on and beyond" the frontier + val ts0 = tsBts map (_.head) + + // Is the frontier made up of types with the same symbol? + val isUniformFrontier = (ts0: @unchecked) match { + case t :: ts => ts forall (_.typeSymbol == t.typeSymbol) + } + + // Produce a single type for this frontier by merging the prefixes and arguments of those + // typerefs that share the same symbol: that symbol is the current maximal symbol for which + // the invariant holds, i.e., the one that conveys most information wrt subtyping. Before + // merging, strip targs that refer to bound tparams (when we're computing the lub of type + // constructors.) Also filter out all types that are a subtype of some other type. + if (isUniformFrontier) { + if (settings.debug.value || printLubs) { + val fbounds = findRecursiveBounds(ts0) + if (fbounds.nonEmpty) { + println("Encountered " + fbounds.size + " recursive bounds while lubbing " + ts0.size + " types.") + for ((p0, p1) <- fbounds) { + val desc = if (p0 == p1) "its own bounds" else "the bounds of " + p1 + + println(" " + p0.fullLocationString + " appears in " + desc) + println(" " + p1 + " " + p1.info.bounds) + } + println("") + } + } + val tails = tsBts map (_.tail) + mergePrefixAndArgs(elimSub(ts0 map elimHigherOrderTypeParam, depth), 1, depth) match { + case Some(tp) => tp :: loop(tails) + case _ => loop(tails) + } + } + else { + // frontier is not uniform yet, move it beyond the current minimal symbol; + // lather, rinSe, repeat + val sym = minSym(ts0) + val newtps = tsBts map (ts => if (ts.head.typeSymbol == sym) ts.tail else ts) + if (printLubs) { + val str = (newtps.zipWithIndex map { case (tps, idx) => + tps.map(" " + _ + "\n").mkString(" (" + idx + ")\n", "", "\n") + }).mkString("") + + println("Frontier(\n" + str + ")") + printLubMatrix(ts zip tsBts toMap, lubListDepth) + } + + loop(newtps) + } + } + } + + val initialBTSes = ts map (_.baseTypeSeq.toList) + if (printLubs) + printLubMatrix(ts zip initialBTSes toMap, depth) + + loop(initialBTSes) + } + + /** 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 + } + def elimAnonymousClass(t: Type) = t match { + case TypeRef(pre, clazz, Nil) if clazz.isAnonymousClass => + clazz.classBound.asSeenFrom(pre, clazz.owner) + case _ => + t + } + def elimRefinement(t: Type) = t match { + case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) + case _ => t + } + + /** 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 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 tv@TypeVar(_, constr) => + if (tv.instValid) constr.inst + else if (tv.untouchable) tv + 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) + } + + /** The isNumericValueType tests appear redundant, but without them + * test/continuations-neg/function3.scala goes into an infinite loop. + * (Even if the calls are to typeSymbolDirect.) + */ + 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 = ts match { + case List() => NothingClass.tpe + case List(t) => t + case _ => + try { + lub(ts, lubDepth(ts)) + } finally { + lubResults.clear() + glbResults.clear() + } + } + + /** The least upper bound wrt <:< of a list of types */ + private 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 = map2(tparams, matchingBounds(ts, tparams).transpose)((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 lubBaseTypes: List[Type] = lubList(ts, 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 excludeFromLub(sym: Symbol) = ( + sym.isClass + || sym.isConstructor + || !sym.isPublic + || isGetClass(sym) + || narrowts.exists(t => !refines(t, sym)) + ) + 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 = + map2(narrowts, syms)((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.name.toTypeName, proto.pos) + .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))) + } + // add a refinement symbol for all non-class members of lubBase + // which are refined by every type in ts. + for (sym <- lubBase.nonPrivateMembers ; if !excludeFromLub(sym)) { + try { + val lsym = lubsym(sym) + if (lsym != NoSymbol) addMember(lubThisType, lubRefined, lsym) + } catch { + case ex: NoCommonType => + } + } + if (lubRefined.decls.isEmpty) lubBase + else if (!verifyLubs) lubRefined + else { + // Verify that every given type conforms to the calculated lub. + // In theory this should not be necessary, but higher-order type + // parameters are not handled correctly. + val ok = ts forall { t => + (t <:< lubRefined) || { + if (settings.debug.value || printLubs) { + Console.println( + "Malformed lub: " + lubRefined + "\n" + + "Argument " + t + " does not conform. Falling back to " + lubBase + ) + } + false + } + } + // If not, fall back on the more conservative calculation. + if (ok) lubRefined + else lubBase + } + } + 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 stripSuffix " " + 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 + + /** The greatest lower bound wrt <:< of a list of types */ + def glb(ts: List[Type]): Type = elimSuper(ts) match { + case List() => AnyClass.tpe + case List(t) => t + case ts0 => + try { + glbNorm(ts0, lubDepth(ts0)) + } finally { + lubResults.clear() + glbResults.clear() + } + } + + private def glb(ts: List[Type], depth: Int): Type = elimSuper(ts) match { + case List() => AnyClass.tpe + case List(t) => t + case ts0 => glbNorm(ts0, depth) + } + + /** The greatest lower bound wrt <:< of a list of types, which have been normalized + * wrt elimSuper */ + protected def glbNorm(ts: List[Type], depth: Int): Type = { + def glb0(ts0: List[Type]): Type = ts0 match { + case List() => AnyClass.tpe + case List(t) => t + case ts @ PolyType(tparams, _) :: _ => + val tparams1 = map2(tparams, matchingBounds(ts, tparams).transpose)((tparam, bounds) => + tparam.cloneSymbol.setInfo(lub(bounds, depth))) + PolyType(tparams1, glbNorm(matchingInstTypes(ts, tparams1), depth)) + case ts @ MethodType(params, _) :: rest => + MethodType(params, glbNorm(matchingRestypes(ts, params map (_.tpe)), depth)) + case ts @ NullaryMethodType(_) :: rest => + NullaryMethodType(glbNorm(matchingRestypes(ts, Nil), depth)) + 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; 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 + } + + /** A list of the typevars in a type. */ + def typeVarsInType(tp: Type): List[TypeVar] = { + var tvs: List[TypeVar] = Nil + tp foreach { + case t: TypeVar => tvs ::= t + case _ => + } + tvs.reverse + } + /** Make each type var in this type use its original type for comparisons instead + * of collecting constraints. + */ + def suspendTypeVarsInType(tp: Type): List[TypeVar] = { + val tvs = typeVarsInType(tp) + // !!! Is it somehow guaranteed that this will not break under nesting? + // In general one has to save and restore the contents of the field... + tvs foreach (_.suspended = true) + tvs + } + + /** 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 => isPrimitiveValueClass(arg.typeSymbol))) Some(ObjectClass.tpe) + else Some(typeRef(pre, sym, List(lub(args)))) + } + } + else transposeSafe(argss) match { + case None => + // transpose freaked out because of irregular argss + // catching just in case (shouldn't happen, but also doesn't cost us) + // [JZ] It happens: see SI-5683. + debuglog("transposed irregular matrix!?" +(tps, argss)) + None + case Some(argsst) => + val args = map2(sym.typeParams, argsst) { (tparam, as) => + if (depth == 0) { + if (tparam.variance == variance) { + // Take the intersection of the upper bounds of the type parameters + // rather than falling all the way back to "Any", otherwise we end up not + // conforming to bounds. + val bounds0 = sym.typeParams map (_.info.bounds.hi) filterNot (_.typeSymbol == AnyClass) + if (bounds0.isEmpty) AnyClass.tpe + else intersectionType(bounds0 map (b => b.asSeenFrom(tps.head, sym))) + } + 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 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) + // debuglog("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]] = { + def getBounds(tp: Type): List[Type] = tp match { + case PolyType(tparams1, _) if sameLength(tparams1, tparams) => + tparams1 map (tparam => tparam.info.substSym(tparams1, tparams)) + case tp => + if (tp ne tp.normalize) getBounds(tp.normalize) + else throw new NoCommonType(tps) + } + tps map getBounds + } + + /** 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] = { + def transformResultType(tp: Type): Type = tp match { + case PolyType(tparams1, restpe) if sameLength(tparams1, tparams) => + restpe.substSym(tparams1, tparams) + case tp => + if (tp ne tp.normalize) transformResultType(tp.normalize) + else throw new NoCommonType(tps) + } + tps map transformResultType + } + + /** 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) + } + +// 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) + } + + // TODO: RecoverableCyclicReference should be separated from TypeError, + // but that would be a big change. Left for further refactoring. + /** An exception for cyclic references from which we can recover */ + case class RecoverableCyclicReference(sym: Symbol) + extends TypeError("illegal cyclic reference involving " + sym) { + if (settings.debug.value) printStackTrace() + } + + 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) + } + + /** The current indentation string for traces */ + private var indent: String = "" + + /** Perform operation `p` on arguments `tp1`, `arg2` and print trace of computation. */ + protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { + Console.println(indent + tp1 + " " + op + " " + arg2 + "?" /* + "("+tp1.getClass+","+arg2.getClass+")"*/) + indent = indent + " " + val result = p(tp1, arg2) + indent = indent stripSuffix " " + 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 isUnboundedGeneric(tp: Type) = tp match { + case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefClass.tpe) + case _ => false + } + def isBoundedGeneric(tp: Type) = tp match { + case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe) + case TypeRef(_, sym, _) => !isPrimitiveValueClass(sym) + case _ => false + } + // Add serializable to a list of parents, unless one of them already is + def addSerializable(ps: Type*): List[Type] = ( + if (ps exists (_ <:< SerializableClass.tpe)) ps.toList + else (ps :+ SerializableClass.tpe).toList + ) + + 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 + + protected def typeToString(tpe: Type): String = + if (tostringRecursions >= maxTostringRecursions) + "..." + else + try { + tostringRecursions += 1 + tpe.safeToString + } finally { + tostringRecursions -= 1 + } + + implicit val AnnotatedTypeTag = ClassTag[AnnotatedType](classOf[AnnotatedType]) + implicit val BoundedWildcardTypeTag = ClassTag[BoundedWildcardType](classOf[BoundedWildcardType]) + implicit val ClassInfoTypeTag = ClassTag[ClassInfoType](classOf[ClassInfoType]) + implicit val CompoundTypeTag = ClassTag[CompoundType](classOf[CompoundType]) + implicit val ConstantTypeTag = ClassTag[ConstantType](classOf[ConstantType]) + implicit val ExistentialTypeTag = ClassTag[ExistentialType](classOf[ExistentialType]) + implicit val MethodTypeTag = ClassTag[MethodType](classOf[MethodType]) + implicit val NullaryMethodTypeTag = ClassTag[NullaryMethodType](classOf[NullaryMethodType]) + implicit val PolyTypeTag = ClassTag[PolyType](classOf[PolyType]) + implicit val RefinedTypeTag = ClassTag[RefinedType](classOf[RefinedType]) + implicit val SingletonTypeTag = ClassTag[SingletonType](classOf[SingletonType]) + implicit val SingleTypeTag = ClassTag[SingleType](classOf[SingleType]) + implicit val SuperTypeTag = ClassTag[SuperType](classOf[SuperType]) + implicit val ThisTypeTag = ClassTag[ThisType](classOf[ThisType]) + implicit val TypeBoundsTag = ClassTag[TypeBounds](classOf[TypeBounds]) + implicit val TypeRefTag = ClassTag[TypeRef](classOf[TypeRef]) + implicit val TypeTagg = ClassTag[Type](classOf[Type]) +} diff --git a/src/reflect/scala/reflect/internal/package.scala b/src/reflect/scala/reflect/internal/package.scala new file mode 100644 index 0000000000..99b837152d --- /dev/null +++ b/src/reflect/scala/reflect/internal/package.scala @@ -0,0 +1,6 @@ +package scala.reflect + +package object internal { + + type MirrorOf[U <: base.Universe with Singleton] = base.MirrorOf[U] +} diff --git a/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala b/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala new file mode 100644 index 0000000000..4670bd4eef --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala @@ -0,0 +1,221 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.reflect.internal.pickling + +object ByteCodecs { + + def avoidZero(src: Array[Byte]): Array[Byte] = { + var i = 0 + val srclen = src.length + var count = 0 + while (i < srclen) { + if (src(i) == 0x7f) count += 1 + i += 1 + } + val dst = new Array[Byte](srclen + count) + i = 0 + var j = 0 + while (i < srclen) { + val in = src(i) + if (in == 0x7f) { + dst(j) = (0xc0).toByte + dst(j + 1) = (0x80).toByte + j += 2 + } else { + dst(j) = (in + 1).toByte + j += 1 + } + i += 1 + } + dst + } + + def regenerateZero(src: Array[Byte]): Int = { + var i = 0 + val srclen = src.length + var j = 0 + while (i < srclen) { + val in: Int = src(i) & 0xff + if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) { + src(j) = 0x7f + i += 2 + } else if (in == 0) { + src(j) = 0x7f + i += 1 + } else { + src(j) = (in - 1).toByte + i += 1 + } + j += 1 + } + j + } + + def encode8to7(src: Array[Byte]): Array[Byte] = { + val srclen = src.length + val dstlen = (srclen * 8 + 6) / 7 + val dst = new Array[Byte](dstlen) + var i = 0 + var j = 0 + while (i + 6 < srclen) { + var in: Int = src(i) & 0xff + dst(j) = (in & 0x7f).toByte + var out: Int = in >>> 7 + in = src(i + 1) & 0xff + dst(j + 1) = (out | (in << 1) & 0x7f).toByte + out = in >>> 6 + in = src(i + 2) & 0xff + dst(j + 2) = (out | (in << 2) & 0x7f).toByte + out = in >>> 5 + in = src(i + 3) & 0xff + dst(j + 3) = (out | (in << 3) & 0x7f).toByte + out = in >>> 4 + in = src(i + 4) & 0xff + dst(j + 4) = (out | (in << 4) & 0x7f).toByte + out = in >>> 3 + in = src(i + 5) & 0xff + dst(j + 5) = (out | (in << 5) & 0x7f).toByte + out = in >>> 2 + in = src(i + 6) & 0xff + dst(j + 6) = (out | (in << 6) & 0x7f).toByte + out = in >>> 1 + dst(j + 7) = out.toByte + i += 7 + j += 8 + } + if (i < srclen) { + var in: Int = src(i) & 0xff + dst(j) = (in & 0x7f).toByte; j += 1 + var out: Int = in >>> 7 + if (i + 1 < srclen) { + in = src(i + 1) & 0xff + dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1 + out = in >>> 6 + if (i + 2 < srclen) { + in = src(i + 2) & 0xff + dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1 + out = in >>> 5 + if (i + 3 < srclen) { + in = src(i + 3) & 0xff + dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1 + out = in >>> 4 + if (i + 4 < srclen) { + in = src(i + 4) & 0xff + dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1 + out = in >>> 3 + if (i + 5 < srclen) { + in = src(i + 5) & 0xff + dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1 + out = in >>> 2 + } + } + } + } + } + if (j < dstlen) dst(j) = out.toByte + } + dst + } + + def decode7to8(src: Array[Byte], srclen: Int): Int = { + var i = 0 + var j = 0 + val dstlen = (srclen * 7 + 7) / 8 + while (i + 7 < srclen) { + var out: Int = src(i) + var in: Byte = src(i + 1) + src(j) = (out | (in & 0x01) << 7).toByte + out = in >>> 1 + in = src(i + 2) + src(j + 1) = (out | (in & 0x03) << 6).toByte + out = in >>> 2 + in = src(i + 3) + src(j + 2) = (out | (in & 0x07) << 5).toByte + out = in >>> 3 + in = src(i + 4) + src(j + 3) = (out | (in & 0x0f) << 4).toByte + out = in >>> 4 + in = src(i + 5) + src(j + 4) = (out | (in & 0x1f) << 3).toByte + out = in >>> 5 + in = src(i + 6) + src(j + 5) = (out | (in & 0x3f) << 2).toByte + out = in >>> 6 + in = src(i + 7) + src(j + 6) = (out | in << 1).toByte + i += 8 + j += 7 + } + if (i < srclen) { + var out: Int = src(i) + if (i + 1 < srclen) { + var in: Byte = src(i + 1) + src(j) = (out | (in & 0x01) << 7).toByte; j += 1 + out = in >>> 1 + if (i + 2 < srclen) { + in = src(i + 2) + src(j) = (out | (in & 0x03) << 6).toByte; j += 1 + out = in >>> 2 + if (i + 3 < srclen) { + in = src(i + 3) + src(j) = (out | (in & 0x07) << 5).toByte; j += 1 + out = in >>> 3 + if (i + 4 < srclen) { + in = src(i + 4) + src(j) = (out | (in & 0x0f) << 4).toByte; j += 1 + out = in >>> 4 + if (i + 5 < srclen) { + in = src(i + 5) + src(j) = (out | (in & 0x1f) << 3).toByte; j += 1 + out = in >>> 5 + if (i + 6 < srclen) { + in = src(i + 6) + src(j) = (out | (in & 0x3f) << 2).toByte; j += 1 + out = in >>> 6 + } + } + } + } + } + } + if (j < dstlen) src(j) = out.toByte + } + dstlen + } + + def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs)) + + /** + * Destructively decodes array xs and returns the length of the decoded array. + * + * Sometimes returns (length+1) of the decoded array. Example: + * + * scala> val enc = reflect.generic.ByteCodecs.encode(Array(1,2,3)) + * enc: Array[Byte] = Array(2, 5, 13, 1) + * + * scala> reflect.generic.ByteCodecs.decode(enc) + * res43: Int = 4 + * + * scala> enc + * res44: Array[Byte] = Array(1, 2, 3, 0) + * + * However, this does not always happen. + */ + def decode(xs: Array[Byte]): Int = { + val len = regenerateZero(xs) + decode7to8(xs, len) + } +} + + + + + + + + diff --git a/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala b/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala new file mode 100644 index 0000000000..7f0895ce64 --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala @@ -0,0 +1,188 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal +package pickling + +/** Variable length byte arrays, with methods for basic pickling and unpickling. + * + * @param data The initial buffer + * @param from The first index where defined data are found + * @param to The first index where new data can be written + */ +class PickleBuffer(data: Array[Byte], from: Int, to: Int) { + + var bytes = data + var readIndex = from + var writeIndex = to + + /** Double bytes array */ + private def dble() { + val bytes1 = new Array[Byte](bytes.length * 2) + Array.copy(bytes, 0, bytes1, 0, writeIndex) + bytes = bytes1 + } + + def ensureCapacity(capacity: Int) = + while (bytes.length < writeIndex + capacity) dble() + + // -- Basic output routines -------------------------------------------- + + /** Write a byte of data */ + def writeByte(b: Int) { + if (writeIndex == bytes.length) dble() + bytes(writeIndex) = b.toByte + writeIndex += 1 + } + + /** Write a natural number in big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def writeNat(x: Int) = + writeLongNat(x.toLong & 0x00000000FFFFFFFFL) + + /** + * Like writeNat, but for longs. This is not the same as + * writeLong, which writes in base 256. Note that the + * binary representation of LongNat is identical to Nat + * if the long value is in the range Int.MIN_VALUE to + * Int.MAX_VALUE. + */ + def writeLongNat(x: Long) { + def writeNatPrefix(x: Long) { + val y = x >>> 7 + if (y != 0L) writeNatPrefix(y) + writeByte(((x & 0x7f) | 0x80).toInt) + } + val y = x >>> 7 + if (y != 0L) writeNatPrefix(y) + writeByte((x & 0x7f).toInt) + } + + /** Write a natural number <code>x</code> at position <code>pos</code>. + * If number is more than one byte, shift rest of array to make space. + * + * @param pos ... + * @param x ... + */ + def patchNat(pos: Int, x: Int) { + def patchNatPrefix(x: Int) { + writeByte(0) + Array.copy(bytes, pos, bytes, pos+1, writeIndex - (pos+1)) + bytes(pos) = ((x & 0x7f) | 0x80).toByte + val y = x >>> 7 + if (y != 0) patchNatPrefix(y) + } + bytes(pos) = (x & 0x7f).toByte + val y = x >>> 7 + if (y != 0) patchNatPrefix(y) + } + + /** Write a long number <code>x</code> in signed big endian format, base 256. + * + * @param x The long number to be written. + */ + def writeLong(x: Long) { + val y = x >> 8 + val z = x & 0xff + if (-y != (z >> 7)) writeLong(y) + writeByte(z.toInt) + } + + // -- Basic input routines -------------------------------------------- + + /** Peek at the current byte without moving the read index */ + def peekByte(): Int = bytes(readIndex) + + /** Read a byte */ + def readByte(): Int = { + val x = bytes(readIndex); readIndex += 1; x + } + + /** Read a natural number in big endian format, base 128. + * All but the last digits have bit 0x80 set.*/ + def readNat(): Int = readLongNat().toInt + + def readLongNat(): Long = { + var b = 0L + var x = 0L + do { + b = readByte() + x = (x << 7) + (b & 0x7f) + } while ((b & 0x80) != 0L); + x + } + + /** Read a long number in signed big endian format, base 256. */ + def readLong(len: Int): Long = { + var x = 0L + var i = 0 + while (i < len) { + x = (x << 8) + (readByte() & 0xff) + i += 1 + } + val leading = 64 - (len << 3) + x << leading >> leading + } + + /** Returns the buffer as a sequence of (Int, Array[Byte]) representing + * (tag, data) of the individual entries. Saves and restores buffer state. + */ + + def toIndexedSeq: IndexedSeq[(Int, Array[Byte])] = { + val saved = readIndex + readIndex = 0 + readNat() ; readNat() // discarding version + val result = new Array[(Int, Array[Byte])](readNat()) + + result.indices foreach { index => + val tag = readNat() + val len = readNat() + val bytes = data.slice(readIndex, len + readIndex) + readIndex += len + + result(index) = tag -> bytes + } + + readIndex = saved + result.toIndexedSeq + } + + /** Perform operation <code>op</code> until the condition + * <code>readIndex == end</code> is satisfied. + * Concatenate results into a list. + * + * @param end ... + * @param op ... + * @return ... + */ + def until[T](end: Int, op: () => T): List[T] = + if (readIndex == end) List() else op() :: until(end, op); + + /** Perform operation <code>op</code> the number of + * times specified. Concatenate the results into a list. + */ + def times[T](n: Int, op: ()=>T): List[T] = + if (n == 0) List() else op() :: times(n-1, op) + + /** Pickle = majorVersion_Nat minorVersion_Nat nbEntries_Nat {Entry} + * Entry = type_Nat length_Nat [actual entries] + * + * Assumes that the ..Version_Nat are already consumed. + * + * @return an array mapping entry numbers to locations in + * the byte array where the entries start. + */ + def createIndex: Array[Int] = { + val index = new Array[Int](readNat()) // nbEntries_Nat + for (i <- 0 until index.length) { + index(i) = readIndex + readByte() // skip type_Nat + readIndex = readNat() + readIndex // read length_Nat, jump to next entry + } + index + } +} diff --git a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala new file mode 100644 index 0000000000..16747af08a --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala @@ -0,0 +1,225 @@ +package scala.reflect +package internal +package pickling + +/** This object provides constants for pickling attributes. + * + * If you extend the format, be sure to increase the + * version minor number. + * + * @author Martin Odersky + * @version 1.0 + */ +object PickleFormat { + +/*************************************************** + * Symbol table attribute format: + * Symtab = nentries_Nat {Entry} + * Entry = 1 TERMNAME len_Nat NameInfo + * | 2 TYPENAME len_Nat NameInfo + * | 3 NONEsym len_Nat + * | 4 TYPEsym len_Nat SymbolInfo + * | 5 ALIASsym len_Nat SymbolInfo + * | 6 CLASSsym len_Nat SymbolInfo [thistype_Ref] + * | 7 MODULEsym len_Nat SymbolInfo + * | 8 VALsym len_Nat [defaultGetter_Ref /* no longer needed*/] SymbolInfo [alias_Ref] + * | 9 EXTref len_Nat name_Ref [owner_Ref] + * | 10 EXTMODCLASSref len_Nat name_Ref [owner_Ref] + * | 11 NOtpe len_Nat + * | 12 NOPREFIXtpe len_Nat + * | 13 THIStpe len_Nat sym_Ref + * | 14 SINGLEtpe len_Nat type_Ref sym_Ref + * | 15 CONSTANTtpe len_Nat constant_Ref + * | 16 TYPEREFtpe len_Nat type_Ref sym_Ref {targ_Ref} + * | 17 TYPEBOUNDStpe len_Nat tpe_Ref tpe_Ref + * | 18 REFINEDtpe len_Nat classsym_Ref {tpe_Ref} + * | 19 CLASSINFOtpe len_Nat classsym_Ref {tpe_Ref} + * | 20 METHODtpe len_Nat tpe_Ref {sym_Ref} + * | 21 POLYTtpe len_Nat tpe_Ref {sym_Ref} + * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {sym_Ref} /* no longer needed */ + * | 52 SUPERtpe len_Nat tpe_Ref tpe_Ref + * | 24 LITERALunit len_Nat + * | 25 LITERALboolean len_Nat value_Long + * | 26 LITERALbyte len_Nat value_Long + * | 27 LITERALshort len_Nat value_Long + * | 28 LITERALchar len_Nat value_Long + * | 29 LITERALint len_Nat value_Long + * | 30 LITERALlong len_Nat value_Long + * | 31 LITERALfloat len_Nat value_Long + * | 32 LITERALdouble len_Nat value_Long + * | 33 LITERALstring len_Nat name_Ref + * | 34 LITERALnull len_Nat + * | 35 LITERALclass len_Nat tpe_Ref + * | 36 LITERALenum len_Nat sym_Ref + * | 40 SYMANNOT len_Nat sym_Ref AnnotInfoBody + * | 41 CHILDREN len_Nat sym_Ref {sym_Ref} + * | 42 ANNOTATEDtpe len_Nat [sym_Ref /* no longer needed */] tpe_Ref {annotinfo_Ref} + * | 43 ANNOTINFO len_Nat AnnotInfoBody + * | 44 ANNOTARGARRAY len_Nat {constAnnotArg_Ref} + * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat + * | 48 EXISTENTIALtpe len_Nat type_Ref {symbol_Ref} + * | 49 TREE len_Nat 1 EMPTYtree + * | 49 TREE len_Nat 2 PACKAGEtree type_Ref sym_Ref mods_Ref name_Ref {tree_Ref} + * | 49 TREE len_Nat 3 CLASStree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 4 MODULEtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref + * | 49 TREE len_Nat 5 VALDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 6 DEFDEFtree type_Ref sym_Ref mods_Ref name_Ref numtparams_Nat {tree_Ref} numparamss_Nat {numparams_Nat {tree_Ref}} tree_Ref tree_Ref + * | 49 TREE len_Nat 7 TYPEDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 8 LABELtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 9 IMPORTtree type_Ref sym_Ref tree_Ref {name_Ref name_Ref} + * | 49 TREE len_Nat 11 DOCDEFtree type_Ref sym_Ref string_Ref tree_Ref + * | 49 TREE len_Nat 12 TEMPLATEtree type_Ref sym_Ref numparents_Nat {tree_Ref} tree_Ref {tree_Ref} + * | 49 TREE len_Nat 13 BLOCKtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 14 CASEtree type_Ref tree_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 15 SEQUENCEtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 16 ALTERNATIVEtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 17 STARtree type_Ref {tree_Ref} + * | 49 TREE len_Nat 18 BINDtree type_Ref sym_Ref name_Ref tree_Ref + * | 49 TREE len_Nat 19 UNAPPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 20 ARRAYVALUEtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 21 FUNCTIONtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 22 ASSIGNtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 23 IFtree type_Ref tree_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 24 MATCHtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 25 RETURNtree type_Ref sym_Ref tree_Ref + * | 49 TREE len_Nat 26 TREtree type_Ref tree_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 27 THROWtree type_Ref tree_Ref + * | 49 TREE len_Nat 28 NEWtree type_Ref tree_Ref + * | 49 TREE len_Nat 29 TYPEDtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 30 TYPEAPPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 31 APPLYtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 32 APPLYDYNAMICtree type_Ref sym_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 33 SUPERtree type_Ref sym_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 34 THIStree type_Ref sym_Ref name_Ref + * | 49 TREE len_Nat 35 SELECTtree type_Ref sym_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 36 IDENTtree type_Ref sym_Ref name_Ref + * | 49 TREE len_Nat 37 LITERALtree type_Ref constant_Ref + * | 49 TREE len_Nat 38 TYPEtree type_Ref + * | 49 TREE len_Nat 39 ANNOTATEDtree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 40 SINGLETONTYPEtree type_Ref tree_Ref + * | 49 TREE len_Nat 41 SELECTFROMTYPEtree type_Ref tree_Ref name_Ref + * | 49 TREE len_Nat 42 COMPOUNDTYPEtree type_Ref tree_Ref + * | 49 TREE len_Nat 43 APPLIEDTYPEtree type_Ref tree_Ref {tree_Ref} + * | 49 TREE len_Nat 44 TYPEBOUNDStree type_Ref tree_Ref tree_Ref + * | 49 TREE len_Nat 45 EXISTENTIALTYPEtree type_Ref tree_Ref {tree_Ref} + * | 50 MODIFIERS len_Nat flags_Long privateWithin_Ref + * SymbolInfo = name_Ref owner_Ref flags_LongNat [privateWithin_Ref] info_Ref + * NameInfo = <character sequence of length len_Nat in Utf8 format> + * NumInfo = <len_Nat-byte signed number in big endian format> + * Ref = Nat + * AnnotInfoBody = info_Ref {annotArg_Ref} {name_Ref constAnnotArg_Ref} + * AnnotArg = Tree | Constant + * ConstAnnotArg = Constant | AnnotInfo | AnnotArgArray + * + * len is remaining length after `len`. + */ + val MajorVersion = 5 + val MinorVersion = 0 + def VersionString = "V" + MajorVersion + "." + MinorVersion + + final val TERMname = 1 + final val TYPEname = 2 + final val NONEsym = 3 + final val TYPEsym = 4 + final val ALIASsym = 5 + final val CLASSsym = 6 + final val MODULEsym = 7 + final val VALsym = 8 + final val EXTref = 9 + final val EXTMODCLASSref = 10 + final val NOtpe = 11 + final val NOPREFIXtpe = 12 + final val THIStpe = 13 + final val SINGLEtpe = 14 + final val CONSTANTtpe = 15 + final val TYPEREFtpe = 16 + final val TYPEBOUNDStpe = 17 + final val REFINEDtpe = 18 + final val CLASSINFOtpe = 19 + final val METHODtpe = 20 + final val POLYtpe = 21 + final val IMPLICITMETHODtpe = 22 // no longer generated + + final val LITERAL = 23 // base line for literals + final val LITERALunit = 24 + final val LITERALboolean = 25 + final val LITERALbyte = 26 + final val LITERALshort = 27 + final val LITERALchar = 28 + final val LITERALint = 29 + final val LITERALlong = 30 + final val LITERALfloat = 31 + final val LITERALdouble = 32 + final val LITERALstring = 33 + final val LITERALnull = 34 + final val LITERALclass = 35 + final val LITERALenum = 36 + final val SYMANNOT = 40 + final val CHILDREN = 41 + final val ANNOTATEDtpe = 42 + final val ANNOTINFO = 43 + final val ANNOTARGARRAY = 44 + + final val SUPERtpe = 46 + final val DEBRUIJNINDEXtpe = 47 + final val EXISTENTIALtpe = 48 + + final val TREE = 49 // prefix code that means a tree is coming + final val EMPTYtree = 1 + final val PACKAGEtree = 2 + final val CLASStree = 3 + final val MODULEtree = 4 + final val VALDEFtree = 5 + final val DEFDEFtree = 6 + final val TYPEDEFtree = 7 + final val LABELtree = 8 + final val IMPORTtree = 9 + final val DOCDEFtree = 11 + final val TEMPLATEtree = 12 + final val BLOCKtree = 13 + final val CASEtree = 14 + // This node type has been removed. + // final val SEQUENCEtree = 15 + final val ALTERNATIVEtree = 16 + final val STARtree = 17 + final val BINDtree = 18 + final val UNAPPLYtree = 19 + final val ARRAYVALUEtree = 20 + final val FUNCTIONtree = 21 + final val ASSIGNtree = 22 + final val IFtree = 23 + final val MATCHtree = 24 + final val RETURNtree = 25 + final val TREtree = 26 + final val THROWtree = 27 + final val NEWtree = 28 + final val TYPEDtree = 29 + final val TYPEAPPLYtree = 30 + final val APPLYtree = 31 + final val APPLYDYNAMICtree = 32 + final val SUPERtree = 33 + final val THIStree = 34 + final val SELECTtree = 35 + final val IDENTtree = 36 + final val LITERALtree = 37 + final val TYPEtree = 38 + final val ANNOTATEDtree = 39 + final val SINGLETONTYPEtree = 40 + final val SELECTFROMTYPEtree = 41 + final val COMPOUNDTYPEtree = 42 + final val APPLIEDTYPEtree = 43 + final val TYPEBOUNDStree = 44 + final val EXISTENTIALTYPEtree = 45 + + final val MODIFIERS = 50 + + final val firstSymTag = NONEsym + final val lastSymTag = VALsym + final val lastExtSymTag = EXTMODCLASSref + + + //The following two are no longer accurate, because ANNOTATEDtpe, + //SUPERtpe, ... are not in the same range as the other types + //final val firstTypeTag = NOtpe + //final val lastTypeTag = POLYtpe +} diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala new file mode 100644 index 0000000000..757163a074 --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -0,0 +1,871 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect +package internal +package pickling + +import java.io.IOException +import java.lang.Float.intBitsToFloat +import java.lang.Double.longBitsToDouble + +import Flags._ +import PickleFormat._ +import scala.collection.{ mutable, immutable } +import collection.mutable.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: MissingRequirementError => + 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() + + private val loadingMirror = mirrorThatLoaded(classRoot) + + /** 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 = mutable.HashMap[Symbol, Scope]() + + //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + + // Laboriously unrolled for performance. + def run() { + var i = 0 + while (i < index.length) { + if (entries(i) == null && isSymbolEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + entries(i) = readSymbol() + readIndex = savedIndex + } + i += 1 + } + // read children last, fix for #3951 + i = 0 + while (i < index.length) { + if (entries(i) == null) { + if (isSymbolAnnotationEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + readSymbolAnnotation() + readIndex = savedIndex + } + else if (isChildrenEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + readChildren() + readIndex = savedIndex + } + } + i += 1 + } + } + + 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) + } + } + protected def readTermName(): TermName = readName().toTermName + protected def readTypeName(): TypeName = readName().toTypeName + + /** 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) loadingMirror.RootClass else readSymbolRef() + + def adjust(sym: Symbol) = if (tag == EXTref) sym else sym.moduleClass + + def fromName(name: Name) = name.toTermName match { + case nme.ROOT => loadingMirror.RootClass + case nme.ROOTPKG => loadingMirror.RootPackage + case _ => adjust(owner.info.decl(name)) + } + 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.toTermName)) + 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.toTermName, owner)) orElse { + // (3) Try as a nested object symbol. + nestedObjectSymbol orElse { + // (4) Otherwise, fail. + //System.err.println("missing "+name+" in "+owner+"/"+owner.id+" "+owner.info.decls) + adjust(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 isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) + def isModuleRoot = (name == moduleRoot.name) && (owner == moduleRoot.owner) + def pflags = flags & PickledFlags + + def finishSym(sym: Symbol): Symbol = { + 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 | ALIASsym => + owner.newNonClassSymbol(name.toTypeName, NoPosition, pflags) + case CLASSsym => + val sym = ( + if (isClassRoot) { + if (isModuleFlag) moduleRoot.moduleClass setFlag pflags + else classRoot setFlag pflags + } + else owner.newClassSymbol(name.toTypeName, NoPosition, pflags) + ) + 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 setFlag pflags + else owner.newLinkedModule(clazz, pflags) + case VALsym => + if (isModuleRoot) { assert(false); NoSymbol } + else owner.newTermSymbol(name.toTermName, NoPosition, pflags) + + 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() + // @PP: Where is the flag setting supposed to happen? I infer + // from the lack of flag setting in the rest of the unpickler + // that it isn't right here. See #4757 for the immediate + // motivation to fix it. + val tparams = until(end, readSymbolRef) map (_ setFlag EXISTENTIAL) + newExistentialType(tparams, 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(ClassfileAnnotArgTag) + } + 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 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, name.toTypeName, tparams, impl) + + case MODULEtree => + setSymModsName() + ModuleDef(mods, name.toTermName, readTemplateRef()) + + case VALDEFtree => + setSymModsName() + val tpt = readTreeRef() + val rhs = readTreeRef() + ValDef(mods, name.toTermName, 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.toTermName, tparams, vparamss, tpt, rhs) + + case TYPEDEFtree => + setSymModsName() + val rhs = readTreeRef() + val tparams = until(end, readTypeDefRef) + TypeDef(mods, name.toTypeName, tparams, rhs) + + case LABELtree => + setSymName() + val rhs = readTreeRef() + val params = until(end, readIdentRef) + LabelDef(name.toTermName, 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) + } + + /* 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 = readNameRef().toTypeName + protected def readTermNameRef(): TermName = readNameRef().toTermName + + 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(name: Name, owner: Symbol): Symbol = + missingHook(owner, name) orElse MissingRequirementError.signal( + s"bad reference while unpickling $filename: ${name.longString} not found in ${owner.tpe.widen}" + ) + + 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) + + /** Convert to a type error, that is printed gracefully instead of crashing. + * + * Similar in intent to what SymbolLoader does (but here we don't have access to + * error reporting, so we rely on the typechecker to report the error). + */ + def toTypeError(e: MissingRequirementError) = + new TypeError(e.msg) + + /** 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 = try { + val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` + atPhase(p) (sym setInfo tp) + if (currentRunId != definedAtRunId) + sym.setInfo(adaptToNewRunMap(tp)) + } + catch { + case e: MissingRequirementError => throw toTypeError(e) + } + 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) = try { + super.complete(sym) + var alias = at(j, readSymbol) + if (alias.isOverloaded) + alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + + sym.asInstanceOf[TermSymbol].setAlias(alias) + } + catch { + case e: MissingRequirementError => throw toTypeError(e) + } + } + } +} diff --git a/src/reflect/scala/reflect/internal/settings/AbsSettings.scala b/src/reflect/scala/reflect/internal/settings/AbsSettings.scala new file mode 100644 index 0000000000..9bbba3f079 --- /dev/null +++ b/src/reflect/scala/reflect/internal/settings/AbsSettings.scala @@ -0,0 +1,23 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.internal +package settings + +/** A Settings abstraction boiled out of the original highly mutable Settings + * class with the intention of creating an ImmutableSettings which can be used + * interchangeably. Except of course without the mutants. + */ + +trait AbsSettings { + type Setting <: AbsSettingValue // Fix to the concrete Setting type + + trait AbsSettingValue { + type T <: Any + def value: T + def isDefault: Boolean + } +} + diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala new file mode 100644 index 0000000000..8640a23aa7 --- /dev/null +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ + +package scala.reflect.internal +package settings + +/** A mutable Settings object. + */ +abstract class MutableSettings extends AbsSettings { + + type Setting <: SettingValue + type BooleanSetting <: Setting { type T = Boolean } + type IntSetting <: Setting { type T = Int } + + // basically this is a value which remembers if it's been modified + trait SettingValue extends AbsSettingValue { + protected var v: T + protected var setByUser: Boolean = false + + def postSetHook(): Unit = () + def isDefault = !setByUser + def isSetByUser = setByUser + def value: T = v + def value_=(arg: T) = { + setByUser = true + v = arg + postSetHook() + } + } + + def overrideObjects: BooleanSetting + def printtypes: BooleanSetting + def debug: BooleanSetting + def Ynotnull: BooleanSetting + def explaintypes: BooleanSetting + def verbose: BooleanSetting + def uniqid: BooleanSetting + def Yshowsymkinds: BooleanSetting + def Xprintpos: BooleanSetting + def Yrecursion: IntSetting + def maxClassfileName: IntSetting + def Xexperimental: BooleanSetting + def XoldPatmat: BooleanSetting + def XnoPatmatAnalysis: BooleanSetting +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala new file mode 100644 index 0000000000..5beec70d62 --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -0,0 +1,336 @@ +package scala.reflect +package internal +package transform + +import Flags.PARAMACCESSOR + +trait Erasure { + + val global: SymbolTable + import global._ + import definitions._ + + /** An extractor object for generic arrays */ + object GenericArray { + + /** Is `tp` an unbounded generic type (i.e. which could be instantiated + * with primitive as well as class types)?. + */ + private def genericCore(tp: Type): Type = tp.normalize match { + /* A Java Array<T> is erased to Array[Object] (T can only be a reference type), where as a Scala Array[T] is + * erased to Object. However, there is only symbol for the Array class. So to make the distinction between + * a Java and a Scala array, we check if the owner of T comes from a Java class. + * This however caused issue SI-5654. The additional test for EXSITENTIAL fixes it, see the ticket comments. + * In short, members of an existential type (e.g. `T` in `forSome { type T }`) can have pretty arbitrary + * owners (e.g. when computing lubs, <root> is used). All packageClass symbols have `isJavaDefined == true`. + */ + case TypeRef(_, sym, _) if sym.isAbstractType && (!sym.owner.isJavaDefined || sym.hasFlag(Flags.EXISTENTIAL)) => + tp + case ExistentialType(tparams, restp) => + genericCore(restp) + case _ => + NoType + } + + /** If `tp` is of the form Array[...Array[T]...] where `T` is an abstract type + * then Some((N, T)) where N is the number of Array constructors enclosing `T`, + * otherwise None. Existentials on any level are ignored. + */ + def unapply(tp: Type): Option[(Int, Type)] = tp.normalize match { + case TypeRef(_, ArrayClass, List(arg)) => + genericCore(arg) match { + case NoType => + unapply(arg) match { + case Some((level, core)) => Some((level + 1, core)) + case None => None + } + case core => + Some((1, core)) + } + case ExistentialType(tparams, restp) => + unapply(restp) + case _ => + None + } + } + + protected def unboundedGenericArrayLevel(tp: Type): Int = tp match { + case GenericArray(level, core) if !(core <:< AnyRefClass.tpe) => level + case _ => 0 + } + + // @M #2585 when generating a java generic signature that includes + // a selection of an inner class p.I, (p = `pre`, I = `cls`) must + // rewrite to p'.I, where p' refers to the class that directly defines + // the nested class I. + // + // See also #2585 marker in javaSig: there, type arguments must be + // included (use pre.baseType(cls.owner)). + // + // This requires that cls.isClass. + protected def rebindInnerClass(pre: Type, cls: Symbol): Type = { + if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? + } + + def underlyingOfValueClass(clazz: Symbol): Type = + clazz.firstParamAccessor.tpe.resultType + + abstract class ErasureMap extends TypeMap { + private lazy val ObjectArray = arrayType(ObjectClass.tpe) + private lazy val ErasedObject = erasedTypeRef(ObjectClass) + + def mergeParents(parents: List[Type]): Type + + def eraseNormalClassRef(pre: Type, clazz: Symbol): Type = + typeRef(apply(rebindInnerClass(pre, clazz)), clazz, List()) // #2585 + + protected def eraseDerivedValueClassRef(clazz: Symbol): Type = + scalaErasure(underlyingOfValueClass(clazz)) + + def apply(tp: Type): Type = tp match { + case ConstantType(_) => + tp + case st: SubType => + apply(st.supertype) + case TypeRef(pre, sym, args) => + if (sym == ArrayClass) + if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe + else if (args.head.typeSymbol.isBottomClass) ObjectArray + else typeRef(apply(pre), sym, args map applyInArray) + else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) ErasedObject + else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) + else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) + else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(sym) + else if (sym.isClass) eraseNormalClassRef(pre, sym) + else apply(sym.info) // alias type or abstract type + case PolyType(tparams, restpe) => + apply(restpe) + case ExistentialType(tparams, restpe) => + apply(restpe) + case mt @ MethodType(params, restpe) => + MethodType( + cloneSymbolsAndModify(params, ErasureMap.this), + if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass) + // this replaces each typeref that refers to an argument + // by the type `p.tpe` of the actual argument p (p in params) + else apply(mt.resultType(params map (_.tpe)))) + case RefinedType(parents, decls) => + apply(mergeParents(parents)) + case AnnotatedType(_, atp, _) => + apply(atp) + case ClassInfoType(parents, decls, clazz) => + ClassInfoType( + if (clazz == ObjectClass || isPrimitiveValueClass(clazz)) Nil + else if (clazz == ArrayClass) List(ErasedObject) + else removeLaterObjects(parents map this), + decls, clazz) + case _ => + mapOver(tp) + } + + def applyInArray(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if (sym.isDerivedValueClass) => eraseNormalClassRef(pre, sym) + case _ => apply(tp) + } + } + + protected def verifyJavaErasure = false + + /** The erasure |T| of a type T. This is: + * + * - For a constant type, itself. + * - For a type-bounds structure, the erasure of its upper bound. + * - For every other singleton type, the erasure of its supertype. + * - For a typeref scala.Array+[T] where T is an abstract type, AnyRef. + * - For a typeref scala.Array+[T] where T is not an abstract type, scala.Array+[|T|]. + * - For a typeref scala.Any or scala.AnyVal, java.lang.Object. + * - For a typeref scala.Unit, scala.runtime.BoxedUnit. + * - For a typeref P.C[Ts] where C refers to a class, |P|.C. + * (Where P is first rebound to the class that directly defines C.) + * - For a typeref P.C[Ts] where C refers to an alias type, the erasure of C's alias. + * - For a typeref P.C[Ts] where C refers to an abstract type, the + * erasure of C's upper bound. + * - For a non-empty type intersection (possibly with refinement) + * - in scala, the erasure of the intersection dominator + * - in java, the erasure of its first parent <--- @PP: not yet in spec. + * - For an empty type intersection, java.lang.Object. + * - For a method type (Fs)scala.Unit, (|Fs|)scala#Unit. + * - For any other method type (Fs)Y, (|Fs|)|T|. + * - For a polymorphic type, the erasure of its result type. + * - For the class info type of java.lang.Object, the same type without any parents. + * - For a class info type of a value class, the same type without any parents. + * - For any other class info type with parents Ps, the same type with + * parents |Ps|, but with duplicate references of Object removed. + * - for all other types, the type itself (with any sub-components erased) + */ + def erasure(sym: Symbol): ErasureMap = + if (sym == NoSymbol || !sym.enclClass.isJavaDefined) scalaErasure + else if (verifyJavaErasure && sym.isMethod) verifiedJavaErasure + else javaErasure + + /** This is used as the Scala erasure during the erasure phase itself + * It differs from normal erasure in that value classes are erased to ErasedValueTypes which + * are then later converted to the underlying parameter type in phase posterasure. + */ + def specialErasure(sym: Symbol)(tp: Type): Type = + if (sym != NoSymbol && sym.enclClass.isJavaDefined) + erasure(sym)(tp) + else if (sym.isTerm && sym.owner.isDerivedValueClass) + specialErasureAvoiding(sym.owner, tp) + else if (sym.isValue && sym.owner.isMethodWithExtension) + specialErasureAvoiding(sym.owner.owner, tp) + else + specialScalaErasure(tp) + + def specialErasureAvoiding(clazz: Symbol, tpe: Type): Type = { + tpe match { + case PolyType(tparams, restpe) => + specialErasureAvoiding(clazz, restpe) + case ExistentialType(tparams, restpe) => + specialErasureAvoiding(clazz, restpe) + case mt @ MethodType(params, restpe) => + MethodType( + cloneSymbolsAndModify(params, specialErasureAvoiding(clazz, _)), + if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass) + else specialErasureAvoiding(clazz, (mt.resultType(params map (_.tpe))))) + case TypeRef(pre, `clazz`, args) => + typeRef(pre, clazz, List()) + case _ => + specialScalaErasure(tpe) + } + } + + /** Scala's more precise erasure than java's is problematic as follows: + * + * - Symbols are read from classfiles and populated with types + * - The textual signature read from the bytecode is forgotten + * - Bytecode generation must know the precise signature of a method + * - the signature is derived from the erasure of the method type + * - If that derivation does not adhere to the rules by which the original + * signature was created, a NoSuchMethod error will result. + * + * For this reason and others (such as distinguishing constructors from other methods) + * erasure is now (Symbol, Type) => Type rather than Type => Type. + */ + class ScalaErasureMap extends ErasureMap { + /** In scala, calculate a useful parent. + * An intersection such as `Object with Trait` erases to Trait. + */ + def mergeParents(parents: List[Type]): Type = + intersectionDominator(parents) + } + + class JavaErasureMap extends ErasureMap { + /** In java, always take the first parent. + * An intersection such as `Object with Trait` erases to Object. + */ + def mergeParents(parents: List[Type]): Type = + if (parents.isEmpty) ObjectClass.tpe + else parents.head + } + + object scalaErasure extends ScalaErasureMap + + /** This is used as the Scala erasure during the erasure phase itself + * It differs from normal erasure in that value classes are erased to ErasedValueTypes which + * are then later converted to the underlying parameter type in phase posterasure. + */ + object specialScalaErasure extends ScalaErasureMap { + override def eraseDerivedValueClassRef(clazz: Symbol): Type = ErasedValueType(clazz) + } + + object javaErasure extends JavaErasureMap + + object verifiedJavaErasure extends JavaErasureMap { + override def apply(tp: Type): Type = { + val res = javaErasure(tp) + val old = scalaErasure(tp) + if (!(res =:= old)) + log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res) + res + } + } + + /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. + * + * - If the list contains one or more occurrences of scala.Array with + * type parameters El1, El2, ... then the dominator is scala.Array with + * type parameter of intersectionDominator(List(El1, El2, ...)). <--- @PP: not yet in spec. + * - Otherwise, the list is reduced to a subsequence containing only types + * which are not subtypes of other listed types (the span.) + * - If the span is empty, the dominator is Object. + * - If the span contains a class Tc which is not a trait and which is + * not Object, the dominator is Tc. <--- @PP: "which is not Object" not in spec. + * - Otherwise, the dominator is the first element of the span. + */ + def intersectionDominator(parents: List[Type]): Type = { + if (parents.isEmpty) ObjectClass.tpe + else { + val psyms = parents map (_.typeSymbol) + if (psyms contains ArrayClass) { + // treat arrays specially + arrayType( + intersectionDominator( + parents filter (_.typeSymbol == ArrayClass) map (_.typeArgs.head))) + } else { + // implement new spec for erasure of refined types. + def isUnshadowed(psym: Symbol) = + !(psyms exists (qsym => (psym ne qsym) && (qsym isNonBottomSubClass psym))) + val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first + val psym = p.typeSymbol + psym.initialize + psym.isClass && !psym.isTrait && isUnshadowed(psym) + } + (if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next() + } + } + } + + /** Type reference after erasure */ + def erasedTypeRef(sym: Symbol): Type = + typeRef(erasure(sym)(sym.owner.tpe), sym, Nil) + + /** The symbol's erased info. This is the type's erasure, except for the following symbols: + * + * - For $asInstanceOf : [T]T + * - For $isInstanceOf : [T]scala#Boolean + * - For class Array : [T]C where C is the erased classinfo of the Array class. + * - For Array[T].<init> : {scala#Int)Array[T] + * - For a type parameter : A type bounds type consisting of the erasures of its bounds. + */ + def transformInfo(sym: Symbol, tp: Type): Type = { + if (sym == Object_asInstanceOf) + sym.info + else if (sym == Object_isInstanceOf || sym == ArrayClass) + PolyType(sym.info.typeParams, specialErasure(sym)(sym.info.resultType)) + else if (sym.isAbstractType) + TypeBounds(WildcardType, WildcardType) + else if (sym.isTerm && sym.owner == ArrayClass) { + if (sym.isClassConstructor) + tp match { + case MethodType(params, TypeRef(pre, sym1, args)) => + MethodType(cloneSymbolsAndModify(params, specialErasure(sym)), + typeRef(specialErasure(sym)(pre), sym1, args)) + } + else if (sym.name == nme.apply) + tp + else if (sym.name == nme.update) + (tp: @unchecked) match { + case MethodType(List(index, tvar), restpe) => + MethodType(List(index.cloneSymbol.setInfo(specialErasure(sym)(index.tpe)), tvar), + erasedTypeRef(UnitClass)) + } + else specialErasure(sym)(tp) + } else if ( + sym.owner != NoSymbol && + sym.owner.owner == ArrayClass && + sym == Array_update.paramss.head(1)) { + // special case for Array.update: the non-erased type remains, i.e. (Int,A)Unit + // since the erasure type map gets applied to every symbol, we have to catch the + // symbol here + tp + } else { + specialErasure(sym)(tp) + } + } +} diff --git a/src/reflect/scala/reflect/internal/transform/RefChecks.scala b/src/reflect/scala/reflect/internal/transform/RefChecks.scala new file mode 100644 index 0000000000..d6108ab665 --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/RefChecks.scala @@ -0,0 +1,13 @@ +package scala.reflect +package internal +package transform + +trait RefChecks { + + val global: SymbolTable + import global._ + + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isModule && !sym.isStatic) NullaryMethodType(tp) + else tp +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala new file mode 100644 index 0000000000..c4c5dc3a1c --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -0,0 +1,41 @@ +package scala.reflect +package internal +package transform + +import language.existentials + +trait Transforms { self: SymbolTable => + + /** We need to encode laziness by hand here because the three components refChecks, uncurry and erasure + * are overwritten by objects in Global. + * It would be best of objects could override lazy values. See SI-5187. + * In the absence of this, the Lazy functionality should probably be somewhere + * in the standard library. Or is it already? + */ + private class Lazy[T](op: => T) { + private var value: T = _ + private var _isDefined = false + def isDefined = _isDefined + def force: T = { + if (!isDefined) { value = op; _isDefined = true } + value + } + } + + private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks) + private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry) + private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure) + + def refChecks = refChecksLazy.force + def uncurry = uncurryLazy.force + def erasure = erasureLazy.force + + def transformedType(sym: Symbol) = + erasure.transformInfo(sym, + uncurry.transformInfo(sym, + refChecks.transformInfo(sym, sym.info))) + + def transformedType(tpe: Type) = + erasure.scalaErasure(uncurry.uncurry(tpe)) + +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala new file mode 100644 index 0000000000..0c1640ceb9 --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -0,0 +1,64 @@ +package scala.reflect +package internal +package transform + +import Flags._ + +trait UnCurry { + + val global: SymbolTable + import global._ + import definitions._ + + private def expandAlias(tp: Type): Type = if (!tp.isHigherKinded) tp.normalize else tp + + val uncurry: TypeMap = new TypeMap { + def apply(tp0: Type): Type = { + val tp = expandAlias(tp0) + tp match { + case MethodType(params, MethodType(params1, restpe)) => + apply(MethodType(params ::: params1, restpe)) + case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) => + assert(false, "unexpected curried method types with intervening existential") + tp0 + case MethodType(h :: t, restpe) if h.isImplicit => + apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe)) + case NullaryMethodType(restpe) => + apply(MethodType(List(), restpe)) + case TypeRef(pre, ByNameParamClass, arg :: Nil) => + apply(functionType(List(), arg)) + case TypeRef(pre, RepeatedParamClass, arg :: Nil) => + apply(seqType(arg)) + case TypeRef(pre, JavaRepeatedParamClass, arg :: Nil) => + apply(arrayType( + if (isUnboundedGeneric(arg)) ObjectClass.tpe else arg)) + case _ => + expandAlias(mapOver(tp)) + } + } + } + + private val uncurryType = new TypeMap { + def apply(tp0: Type): Type = { + val tp = expandAlias(tp0) + tp match { + case ClassInfoType(parents, decls, clazz) => + val parents1 = parents mapConserve uncurry + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls?? + case PolyType(_, _) => + mapOver(tp) + case _ => + tp + } + } + } + + /** - return symbol's transformed type, + * - if symbol is a def parameter with transformed type T, return () => T + * + * @MAT: starting with this phase, the info of every symbol will be normalized + */ + def transformInfo(sym: Symbol, tp: Type): Type = + if (sym.isType) uncurryType(tp) else uncurry(tp) +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala new file mode 100644 index 0000000000..1f8eb15c90 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -0,0 +1,213 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.internal.util + +import scala.collection.{ mutable, immutable } +import scala.annotation.tailrec +import mutable.ListBuffer + +/** Profiler driven changes. + * TODO - inlining doesn't work from here because of the bug that + * methods in traits aren't inlined. + */ +trait Collections { + /** 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) + ) + + /** All these mm methods are "deep map" style methods for + * mapping etc. on a list of lists while avoiding unnecessary + * intermediate structures like those created via flatten. + */ + final def mexists[A](xss: List[List[A]])(p: A => Boolean) = + xss exists (_ exists p) + final def mforall[A](xss: List[List[A]])(p: A => Boolean) = + xss forall (_ forall p) + final def mmap[A, B](xss: List[List[A]])(f: A => B) = + xss map (_ map f) + final def mforeach[A](xss: List[List[A]])(f: A => Unit) = + xss foreach (_ foreach f) + final def mfind[A](xss: List[List[A]])(p: A => Boolean): Option[A] = { + var res: Option[A] = null + mforeach(xss)(x => if ((res eq null) && p(x)) res = Some(x)) + if (res eq null) None else res + } + final def mfilter[A](xss: List[List[A]])(p: A => Boolean) = + for (xs <- xss; x <- xs; if p(x)) yield x + + final def map2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => C): List[C] = { + val lb = new ListBuffer[C] + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + lb += f(ys1.head, ys2.head) + ys1 = ys1.tail + ys2 = ys2.tail + } + lb.toList + } + final def map3[A, B, C, D](xs1: List[A], xs2: List[B], xs3: List[C])(f: (A, B, C) => D): List[D] = { + if (xs1.isEmpty || xs2.isEmpty || xs3.isEmpty) Nil + else f(xs1.head, xs2.head, xs3.head) :: map3(xs1.tail, xs2.tail, xs3.tail)(f) + } + final def flatMap2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => List[C]): List[C] = { + val lb = new ListBuffer[C] + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + lb ++= f(ys1.head, ys2.head) + ys1 = ys1.tail + ys2 = ys2.tail + } + lb.toList + } + + final def flatCollect[A, B](elems: List[A])(pf: PartialFunction[A, Traversable[B]]): List[B] = { + val lb = new ListBuffer[B] + for (x <- elems ; if pf isDefinedAt x) + lb ++= pf(x) + + lb.toList + } + + final def distinctBy[A, B](xs: List[A])(f: A => B): List[A] = { + val buf = new ListBuffer[A] + val seen = mutable.Set[B]() + xs foreach { x => + val y = f(x) + if (!seen(y)) { + buf += x + seen += y + } + } + buf.toList + } + + @tailrec final def flattensToEmpty(xss: Seq[Seq[_]]): Boolean = { + xss.isEmpty || xss.head.isEmpty && flattensToEmpty(xss.tail) + } + + final def foreachWithIndex[A, B](xs: List[A])(f: (A, Int) => Unit) { + var index = 0 + var ys = xs + while (!ys.isEmpty) { + f(ys.head, index) + ys = ys.tail + index += 1 + } + } + + // @inline + final def findOrElse[A](xs: TraversableOnce[A])(p: A => Boolean)(orElse: => A): A = { + xs find p getOrElse orElse + } + + final def mapFrom[A, A1 >: A, B](xs: List[A])(f: A => B): Map[A1, B] = { + Map[A1, B](xs map (x => (x, f(x))): _*) + } + + final def mapWithIndex[A, B](xs: List[A])(f: (A, Int) => B): List[B] = { + val lb = new ListBuffer[B] + var index = 0 + var ys = xs + while (!ys.isEmpty) { + lb += f(ys.head, index) + ys = ys.tail + index += 1 + } + lb.toList + } + final def collectMap2[A, B, C](xs1: List[A], xs2: List[B])(p: (A, B) => Boolean): Map[A, B] = { + if (xs1.isEmpty || xs2.isEmpty) + return Map() + + val buf = immutable.Map.newBuilder[A, B] + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + val x1 = ys1.head + val x2 = ys2.head + if (p(x1, x2)) + buf += ((x1, x2)) + + ys1 = ys1.tail + ys2 = ys2.tail + } + buf.result + } + final def foreach2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Unit): Unit = { + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + f(ys1.head, ys2.head) + ys1 = ys1.tail + ys2 = ys2.tail + } + } + final def foreach3[A, B, C](xs1: List[A], xs2: List[B], xs3: List[C])(f: (A, B, C) => Unit): Unit = { + var ys1 = xs1 + var ys2 = xs2 + var ys3 = xs3 + while (!ys1.isEmpty && !ys2.isEmpty && !ys3.isEmpty) { + f(ys1.head, ys2.head, ys3.head) + ys1 = ys1.tail + ys2 = ys2.tail + ys3 = ys3.tail + } + } + final def exists2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Boolean): Boolean = { + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + if (f(ys1.head, ys2.head)) + return true + + ys1 = ys1.tail + ys2 = ys2.tail + } + false + } + final def forall2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Boolean): Boolean = { + var ys1 = xs1 + var ys2 = xs2 + while (!ys1.isEmpty && !ys2.isEmpty) { + if (!f(ys1.head, ys2.head)) + return false + + ys1 = ys1.tail + ys2 = ys2.tail + } + true + } + final def forall3[A, B, C](xs1: List[A], xs2: List[B], xs3: List[C])(f: (A, B, C) => Boolean): Boolean = { + var ys1 = xs1 + var ys2 = xs2 + var ys3 = xs3 + while (!ys1.isEmpty && !ys2.isEmpty && !ys3.isEmpty) { + if (!f(ys1.head, ys2.head, ys3.head)) + return false + + ys1 = ys1.tail + ys2 = ys2.tail + ys3 = ys3.tail + } + true + } + + final def transposeSafe[A](ass: List[List[A]]): Option[List[List[A]]] = try { + Some(ass.transpose) + } catch { + case _: IllegalArgumentException => None + } +} + +object Collections extends Collections { } + diff --git a/src/reflect/scala/reflect/internal/util/HashSet.scala b/src/reflect/scala/reflect/internal/util/HashSet.scala new file mode 100644 index 0000000000..a771dad2b0 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/HashSet.scala @@ -0,0 +1,106 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect.internal.util + +object HashSet { + def apply[T >: Null <: AnyRef](): HashSet[T] = this(16) + def apply[T >: Null <: AnyRef](label: String): HashSet[T] = this(label, 16) + def apply[T >: Null <: AnyRef](initialCapacity: Int): HashSet[T] = this("No Label", initialCapacity) + def apply[T >: Null <: AnyRef](label: String, initialCapacity: Int): HashSet[T] = + new HashSet[T](label, initialCapacity) +} + +class HashSet[T >: Null <: AnyRef](val label: String, initialCapacity: Int) extends Set[T] { + private var used = 0 + private var table = new Array[AnyRef](initialCapacity) + private def index(x: Int): Int = math.abs(x % table.length) + + def size: Int = used + def clear() { + used = 0 + table = new Array[AnyRef](initialCapacity) + } + + def findEntryOrUpdate(x: T): T = { + var h = index(x.##) + var entry = table(h) + while (entry ne null) { + if (x == entry) + return entry.asInstanceOf[T] + + h = index(h + 1) + entry = table(h) + } + table(h) = x + used += 1 + if (used > (table.length >> 2)) growTable() + x + } + + def findEntry(x: T): T = { + var h = index(x.##) + var entry = table(h) + while ((entry ne null) && x != entry) { + h = index(h + 1) + entry = table(h) + } + entry.asInstanceOf[T] + } + + def addEntry(x: T) { + var h = index(x.##) + var entry = table(h) + while (entry ne null) { + if (x == entry) return + h = index(h + 1) + entry = table(h) + } + table(h) = x + used += 1 + if (used > (table.length >> 2)) growTable() + } + def addEntries(xs: TraversableOnce[T]) { + xs foreach addEntry + } + + def iterator = new Iterator[T] { + private var i = 0 + def hasNext: Boolean = { + while (i < table.length && (table(i) eq null)) i += 1 + i < table.length + } + def next(): T = + if (hasNext) { i += 1; table(i - 1).asInstanceOf[T] } + else null + } + + private def addOldEntry(x: T) { + var h = index(x.##) + var entry = table(h) + while (entry ne null) { + h = index(h + 1) + entry = table(h) + } + table(h) = x + } + + private def growTable() { + val oldtable = table + val growthFactor = + if (table.length <= initialCapacity) 8 + else if (table.length <= (initialCapacity * 8)) 4 + else 2 + + table = new Array[AnyRef](table.length * growthFactor) + var i = 0 + while (i < oldtable.length) { + val entry = oldtable(i) + if (entry ne null) addOldEntry(entry.asInstanceOf[T]) + i += 1 + } + } + override def toString() = "HashSet %s(%d / %d)".format(label, used, table.length) +} diff --git a/src/reflect/scala/reflect/internal/util/Origins.scala b/src/reflect/scala/reflect/internal/util/Origins.scala new file mode 100644 index 0000000000..0bd5ad55ca --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/Origins.scala @@ -0,0 +1,119 @@ +/* NSC -- new scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect +package internal.util + +import NameTransformer._ +import scala.collection.{ mutable, immutable } +import Origins._ + +/** A debugging class for logging from whence a method is being called. + * Say you wanted to discover who was calling phase_= in SymbolTable. + * You could do this: + * + * {{{ + * private lazy val origins = Origins("arbitraryTag") + * // Commented out original enclosed for contrast + * // final def phase_=(p: Phase): Unit = { + * final def phase_=(p: Phase): Unit = origins { + * }}} + * + * And that's it. When the JVM exits it would issue a report something like this: + {{{ + >> Origins tag 'arbitraryTag' logged 145585 calls from 51 distinguished sources. + + 71114 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:862) + 16584 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:757) + 15411 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:869) + 11507 scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:770) + 10285 scala.tools.nsc.symtab.Symbols$Symbol.unsafeTypeParams(Symbols.scala:864) + 6860 scala.tools.nsc.transform.SpecializeTypes.specializedTypeVars(SpecializeTypes.scala:304) + ... + }}} + * + */ +abstract class Origins { + type Rep + type StackSlice = Array[StackTraceElement] + + def tag: String + def isCutoff(el: StackTraceElement): Boolean + def newRep(xs: StackSlice): Rep + def repString(rep: Rep): String + + private val origins = new mutable.HashMap[Rep, Int] withDefaultValue 0 + private def add(xs: Rep) = origins(xs) += 1 + private def total = origins.values.foldLeft(0L)(_ + _) + + // Create a stack and whittle it down to the interesting part. + def readStack(): Array[StackTraceElement] = ( + Thread.currentThread.getStackTrace dropWhile (x => !isCutoff(x)) dropWhile isCutoff drop 1 + ) + + def apply[T](body: => T): T = { + add(newRep(readStack())) + body + } + def clear() = origins.clear() + def show() = { + println("\n>> Origins tag '%s' logged %s calls from %s distinguished sources.\n".format(tag, total, origins.keys.size)) + origins.toList sortBy (-_._2) foreach { + case (k, v) => println("%7s %s".format(v, repString(k))) + } + } + def purge() = { + show() + clear() + } +} + +object Origins { + private val counters = mutable.HashMap[String, Origins]() + private val thisClass = this.getClass.getName + + locally { + sys.addShutdownHook(counters.values foreach (_.purge())) + } + + case class OriginId(className: String, methodName: String) { + def matches(el: StackTraceElement) = ( + (methodName == el.getMethodName) && (className startsWith el.getClassName) + ) + } + + def lookup(tag: String, orElse: String => Origins): Origins = + counters.getOrElseUpdate(tag, orElse(tag)) + def register(x: Origins): Origins = { + counters(x.tag) = x + x + } + + private def preCutoff(el: StackTraceElement) = ( + (el.getClassName == thisClass) + || (el.getClassName startsWith "java.lang.") + ) + private def findCutoff() = { + val cutoff = Thread.currentThread.getStackTrace dropWhile preCutoff head; + OriginId(cutoff.getClassName, cutoff.getMethodName) + } + + def apply(tag: String): Origins = counters.getOrElseUpdate(tag, new OneLine(tag, findCutoff())) + def apply(tag: String, frames: Int): Origins = counters.getOrElseUpdate(tag, new MultiLine(tag, findCutoff(), frames)) + + class OneLine(val tag: String, id: OriginId) extends Origins { + type Rep = StackTraceElement + def isCutoff(el: StackTraceElement) = id matches el + def newRep(xs: StackSlice): Rep = if ((xs eq null) || (xs.length == 0)) null else xs(0) + def repString(rep: Rep) = " " + rep + } + class MultiLine(val tag: String, id: OriginId, numLines: Int) extends Origins { + type Rep = List[StackTraceElement] + def isCutoff(el: StackTraceElement) = id matches el + def newRep(xs: StackSlice): Rep = (xs take numLines).toList + def repString(rep: Rep) = rep.map("\n " + _).mkString + override def readStack() = super.readStack() drop 1 + } +} diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala new file mode 100644 index 0000000000..3c251b3b31 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -0,0 +1,277 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + * + */ + +package scala.reflect.internal.util + +import reflect.ClassTag +import reflect.base.Attachments +import reflect.api.PositionApi + +object Position { + val tabInc = 8 + + /** Prints the message with the given position indication. */ + def formatMessage(posIn: Position, msg: String, shortenFile: Boolean): String = { + val pos = ( + if (posIn eq null) NoPosition + else if (posIn.isDefined) posIn.inUltimateSource(posIn.source) + else posIn + ) + def file = pos.source.file + def prefix = if (shortenFile) file.name else file.path + + pos match { + case FakePos(fmsg) => fmsg+" "+msg + case NoPosition => msg + case _ => + List( + "%s:%s: %s".format(prefix, pos.line, msg), + pos.lineContent.stripLineEnd, + " " * (pos.column - 1) + "^" + ) mkString "\n" + } + } +} + +abstract class Position extends PositionApi { self => + + type Pos = Position + + def pos: Position = this + + def withPos(newPos: Position): Attachments { type Pos = self.Pos } = newPos + + /** Java file corresponding to the source file of this position. + */ + // necessary for conformance with scala.reflect.api.Position + def fileInfo: java.io.File = source.file.file + + /** Contents of the source file that contains this position. + */ + // necessary for conformance with scala.reflect.api.Position + def fileContent: Array[Char] = source.content + + /** An optional value containing the source file referred to by this position, or + * None if not defined. + */ + def source: SourceFile = throw new UnsupportedOperationException("Position.source") + + /** Is this position neither a NoPosition nor a FakePosition? + * If isDefined is true, offset and source are both defined. + */ + def isDefined: Boolean = false + + /** Is this position a transparent position? */ + def isTransparent: Boolean = false + + /** Is this position a range position? */ + def isRange: Boolean = false + + /** Is this position a non-transparent range position? */ + def isOpaqueRange: Boolean = false + + /** if opaque range, make this position transparent */ + def makeTransparent: Position = this + + /** The start of the position's range, error if not a range position */ + def start: Int = throw new UnsupportedOperationException("Position.start") + + /** The start of the position's range, or point if not a range position */ + def startOrPoint: Int = point + + /** The point (where the ^ is) of the position */ + def point: Int = throw new UnsupportedOperationException("Position.point") + + /** The point (where the ^ is) of the position, or else `default` if undefined */ + def pointOrElse(default: Int): Int = default + + /** The end of the position's range, error if not a range position */ + def end: Int = throw new UnsupportedOperationException("Position.end") + + /** The end of the position's range, or point if not a range position */ + def endOrPoint: Int = point + + @deprecated("use point instead", "2.9.0") + def offset: Option[Int] = if (isDefined) Some(point) else None + + /** The same position with a different start value (if a range) */ + def withStart(off: Int): Position = this + + /** The same position with a different end value (if a range) */ + def withEnd(off: Int): Position = this + + /** The same position with a different point value (if a range or offset) */ + def withPoint(off: Int): Position = this + + /** The same position with a different source value, and its values shifted by given offset */ + def withSource(source: SourceFile, shift: Int): Position = this + + /** If this is a range, the union with the other range, with the point of this position. + * Otherwise, this position + */ + def union(pos: Position): Position = this + + /** If this is a range position, the offset position of its start. + * Otherwise the position itself + */ + def focusStart: Position = this + + /** If this is a range position, the offset position of its point. + * Otherwise the position itself + */ + def focus: Position = this + + /** If this is a range position, the offset position of its end. + * Otherwise the position itself + */ + def focusEnd: Position = this + + /** Does this position include the given position `pos`. + * This holds if `this` is a range position and its range [start..end] + * is the same or covers the range of the given position, which may or may not be a range position. + */ + def includes(pos: Position): Boolean = false + + /** Does this position properly include the given position `pos` ("properly" meaning their + * ranges are not the same)? + */ + def properlyIncludes(pos: Position): Boolean = + includes(pos) && (start < pos.startOrPoint || pos.endOrPoint < end) + + /** Does this position precede that position? + * This holds if both positions are defined and the end point of this position + * is not larger than the start point of the given position. + */ + def precedes(pos: Position): Boolean = + isDefined && pos.isDefined && endOrPoint <= pos.startOrPoint + + /** Does this position properly precede the given position `pos` ("properly" meaning their ranges + * do not share a common point). + */ + def properlyPrecedes(pos: Position): Boolean = + isDefined && pos.isDefined && endOrPoint < pos.startOrPoint + + /** Does this position overlap with that position? + * This holds if both positions are ranges and there is an interval of + * non-zero length that is shared by both position ranges. + */ + def overlaps(pos: Position): Boolean = + isRange && pos.isRange && + ((pos.start < end && start < pos.end) || (start < pos.end && pos.start < end)) + + /** Does this position cover the same range as that position? + * Holds only if both position are ranges + */ + def sameRange(pos: Position): Boolean = + isRange && pos.isRange && start == pos.start && end == pos.end + + def line: Int = throw new UnsupportedOperationException("Position.line") + + def column: Int = throw new UnsupportedOperationException("Position.column") + + /** Convert this to a position around `point` that spans a single source line */ + def toSingleLine: Position = this + + def lineContent: String = + if (isDefined) source.lineToString(line - 1) + else "NO_LINE" + + /** Map this position to a position in an original source + * file. If the SourceFile is a normal SourceFile, simply + * return this. + */ + def inUltimateSource(source : SourceFile): Position = + if (source == null) this else source.positionInUltimateSource(this) + + def dbgString: String = toString + def safeLine: Int = try line catch { case _: UnsupportedOperationException => -1 } + + def show: String = "["+toString+"]" +} + +case object NoPosition extends Position { + override def dbgString = toString +} + +case class FakePos(msg: String) extends Position { + override def toString = msg +} + +class OffsetPosition(override val source: SourceFile, override val point: Int) extends Position { + override def isDefined = true + override def pointOrElse(default: Int): Int = point + override def withPoint(off: Int) = new OffsetPosition(source, off) + override def withSource(source: SourceFile, shift: Int) = new OffsetPosition(source, point + shift) + + override def line: Int = source.offsetToLine(point) + 1 + + override def column: Int = { + var idx = source.lineToOffset(source.offsetToLine(point)) + var col = 0 + while (idx != point) { + col += (if (source.content(idx) == '\t') Position.tabInc - col % Position.tabInc else 1) + idx += 1 + } + col + 1 + } + + override def union(pos: Position) = if (pos.isRange) pos else this + + override def equals(that : Any) = that match { + case that : OffsetPosition => point == that.point && source.file == that.source.file + case that => false + } + override def hashCode = point * 37 + source.file.hashCode + + override def toString = { + val pointmsg = if (point > source.length) "out-of-bounds-" else "offset=" + "source-%s,line-%s,%s%s".format(source.file.canonicalPath, line, pointmsg, point) + } + override def show = "["+point+"]" +} + +/** new for position ranges */ +class RangePosition(source: SourceFile, override val start: Int, point: Int, override val end: Int) +extends OffsetPosition(source, point) { + if (start > end) assert(false, "bad position: "+show) + override def isRange: Boolean = true + override def isOpaqueRange: Boolean = true + override def startOrPoint: Int = start + override def endOrPoint: Int = end + override def withStart(off: Int) = new RangePosition(source, off, point, end) + override def withEnd(off: Int) = new RangePosition(source, start, point, off) + override def withPoint(off: Int) = new RangePosition(source, start, off, end) + override def withSource(source: SourceFile, shift: Int) = new RangePosition(source, start + shift, point + shift, end + shift) + override def focusStart = new OffsetPosition(source, start) + override def focus = { + if (focusCache eq NoPosition) focusCache = new OffsetPosition(source, point) + focusCache + } + override def focusEnd = new OffsetPosition(source, end) + override def makeTransparent = new TransparentPosition(source, start, point, end) + override def includes(pos: Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end + override def union(pos: Position): Position = + if (pos.isRange) new RangePosition(source, start min pos.start, point, end max pos.end) else this + + override def toSingleLine: Position = source match { + case bs: BatchSourceFile + if end > 0 && bs.offsetToLine(start) < bs.offsetToLine(end - 1) => + val pointLine = bs.offsetToLine(point) + new RangePosition(source, bs.lineToOffset(pointLine), point, bs.lineToOffset(pointLine + 1)) + case _ => this + } + + override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" + override def show = "["+start+":"+end+"]" + private var focusCache: Position = NoPosition +} + +class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int) extends RangePosition(source, start, point, end) { + override def isOpaqueRange: Boolean = false + override def isTransparent = true + override def makeTransparent = this + override def show = "<"+start+":"+end+">" +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/util/Set.scala b/src/reflect/scala/reflect/internal/util/Set.scala new file mode 100644 index 0000000000..cfc3e7eada --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/Set.scala @@ -0,0 +1,28 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect.internal.util + +/** A common class for lightweight sets. + */ +abstract class Set[T <: AnyRef] { + + def findEntry(x: T): T + + def addEntry(x: T): Unit + + def iterator: Iterator[T] + + def foreach[U](f: T => U): Unit = iterator foreach f + + def apply(x: T): Boolean = contains(x) + + @deprecated("use `iterator` instead", "2.9.0") def elements = iterator + + def contains(x: T): Boolean = + findEntry(x) ne null + + def toList = iterator.toList + +} diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala new file mode 100644 index 0000000000..7c80ddd37d --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -0,0 +1,161 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + + +package scala.reflect.internal.util + +import scala.tools.nsc.io.{ AbstractFile, VirtualFile } +import scala.collection.mutable.ArrayBuffer +import annotation.tailrec +import java.util.regex.Pattern +import java.io.IOException +import scala.reflect.internal.Chars._ + +/** abstract base class of a source file used in the compiler */ +abstract class SourceFile { + def content : Array[Char] // normalized, must end in SU + def file : AbstractFile + def isLineBreak(idx : Int) : Boolean + def isSelfContained: Boolean + def length : Int + def position(offset: Int) : Position = { + assert(offset < length, file + ": " + offset + " >= " + length) + new OffsetPosition(this, offset) + } + def position(line: Int, column: Int) : Position = new OffsetPosition(this, lineToOffset(line) + column) + + def offsetToLine(offset: Int): Int + def lineToOffset(index : Int): Int + + /** Map a position to a position in the underlying source file. + * For regular source files, simply return the argument. + */ + def positionInUltimateSource(position: Position) = position + override def toString() = file.name + def dbg(offset: Int) = (new OffsetPosition(this, offset)).dbgString + def path = file.path + + def beginsWith(offset: Int, text: String): Boolean = + (content drop offset) startsWith text + + def lineToString(index: Int): String = + content drop lineToOffset(index) takeWhile (c => !isLineBreakChar(c.toChar)) mkString + + @tailrec + final def skipWhitespace(offset: Int): Int = + if (content(offset).isWhitespace) skipWhitespace(offset + 1) else offset + + def identifier(pos: Position): Option[String] = None +} + +/** An object representing a missing source file. + */ +object NoSourceFile extends SourceFile { + def content = Array() + def file = NoFile + def isLineBreak(idx: Int) = false + def isSelfContained = true + def length = -1 + def offsetToLine(offset: Int) = -1 + def lineToOffset(index : Int) = -1 + override def toString = "<no source file>" +} + +object NoFile extends VirtualFile("<no file>", "<no file>") + +object ScriptSourceFile { + /** Length of the script header from the given content, if there is one. + * The header begins with "#!" or "::#!" and ends with a line starting + * with "!#" or "::!#". + */ + def headerLength(cs: Array[Char]): Int = { + val headerPattern = Pattern.compile("""((?m)^(::)?!#.*|^.*/env .*)(\r|\n|\r\n)""") + val headerStarts = List("#!", "::#!") + + if (headerStarts exists (cs startsWith _)) { + val matcher = headerPattern matcher cs.mkString + if (matcher.find) matcher.end + else throw new IOException("script file does not close its header with !# or ::!#") + } + else 0 + } + def stripHeader(cs: Array[Char]): Array[Char] = cs drop headerLength(cs) + + def apply(file: AbstractFile, content: Array[Char]) = { + val underlying = new BatchSourceFile(file, content) + val headerLen = headerLength(content) + val stripped = new ScriptSourceFile(underlying, content drop headerLen, headerLen) + + stripped + } +} +import ScriptSourceFile._ + +class ScriptSourceFile(underlying: BatchSourceFile, content: Array[Char], override val start: Int) extends BatchSourceFile(underlying.file, content) { + override def isSelfContained = false + + override def positionInUltimateSource(pos: Position) = + if (!pos.isDefined) super.positionInUltimateSource(pos) + else new OffsetPosition(underlying, pos.point + start) +} + +/** a file whose contents do not change over time */ +class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends SourceFile { + + def this(_file: AbstractFile) = this(_file, _file.toCharArray) + def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray) + def this(file: AbstractFile, cs: Seq[Char]) = this(file, cs.toArray) + + override def equals(that : Any) = that match { + case that : BatchSourceFile => file.path == that.file.path && start == that.start + case _ => false + } + override def hashCode = file.path.## + start.## + val length = content.length + def start = 0 + def isSelfContained = true + + override def identifier(pos: Position) = + if (pos.isDefined && pos.source == this && pos.point != -1) { + def isOK(c: Char) = isIdentifierPart(c) || isOperatorPart(c) + Some(new String(content drop pos.point takeWhile isOK)) + } else { + super.identifier(pos) + } + + def isLineBreak(idx: Int) = + if (idx >= length) false else { + val ch = content(idx) + // don't identify the CR in CR LF as a line break, since LF will do. + if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF) + else isLineBreakChar(ch) + } + + def calculateLineIndices(cs: Array[Char]) = { + val buf = new ArrayBuffer[Int] + buf += 0 + for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1 + buf += cs.length // sentinel, so that findLine below works smoother + buf.toArray + } + private lazy val lineIndices: Array[Int] = calculateLineIndices(content) + + def lineToOffset(index : Int): Int = lineIndices(index) + + private var lastLine = 0 + + /** Convert offset to line in this source file + * Lines are numbered from 0 + */ + def offsetToLine(offset: Int): Int = { + val lines = lineIndices + def findLine(lo: Int, hi: Int, mid: Int): Int = + if (offset < lines(mid)) findLine(lo, mid - 1, (lo + mid - 1) / 2) + else if (offset >= lines(mid + 1)) findLine(mid + 1, hi, (mid + 1 + hi) / 2) + else mid + lastLine = findLine(0, lines.length, lastLine) + lastLine + } +} diff --git a/src/reflect/scala/reflect/internal/util/StatBase.scala b/src/reflect/scala/reflect/internal/util/StatBase.scala new file mode 100644 index 0000000000..b033ff98bc --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/StatBase.scala @@ -0,0 +1,97 @@ +package scala.reflect.internal.util + +class StatBase { + + private var _enabled = false + + def enabled = _enabled + def enabled_=(cond: Boolean) = { + if (cond && !_enabled) { + val test = new Timer() + val start = System.nanoTime() + var total = 0L + for (i <- 1 to 10000) { + val time = System.nanoTime() + total += System.nanoTime() - time + } + val total2 = System.nanoTime() - start + println("Enabling statistics, measuring overhead = "+ + total/10000.0+"ns to "+total2/10000.0+"ns per timer") + _enabled = true + } + } + + def currentTime() = + if (_enabled) System.nanoTime() else 0L + + def showPercent(x: Double, base: Double) = + if (base == 0) "" else " ("+"%2.1f".format(x / base * 100)+"%)" + + def incCounter(c: Counter) { + if (_enabled) c.value += 1 + } + + def incCounter(c: Counter, delta: Int) { + if (_enabled) c.value += delta + } + + def startCounter(sc: SubCounter): IntPair = + if (_enabled) sc.start() else null + + def stopCounter(sc: SubCounter, start: IntPair) { + if (_enabled) sc.stop(start) + } + + def startTimer(tm: Timer): LongPair = + if (_enabled) tm.start() else null + + def stopTimer(tm: Timer, start: LongPair) { + if (_enabled) tm.stop(start) + } + + case class IntPair(x: Int, y: Int) + case class LongPair(x: Long, y: Long) + + class Counter { + var value: Int = 0 + override def toString = value.toString + } + + class SubCounter(c: Counter) { + var value: Int = 0 + def start(): IntPair = + if (_enabled) IntPair(value, c.value) else null + def stop(prev: IntPair) { + if (_enabled) { + val IntPair(value0, cvalue0) = prev + value = value0 + c.value - cvalue0 + } + } + override def toString = + value+showPercent(value, c.value) + } + + class Timer { + var nanos: Long = 0 + var timings = 0 + def start(): LongPair = + if (_enabled) { + timings += 1 + LongPair(nanos, System.nanoTime()) + } else null + def stop(prev: LongPair) { + if (_enabled) { + val LongPair(nanos0, start) = prev + nanos = nanos0 + System.nanoTime() - start + timings += 1 + } + } + override def toString = (timings/2)+" spans, "+nanos.toString+"ns" + } + + import Predef.Class + + class ClassCounts extends scala.collection.mutable.HashMap[Class[_], Int] { + override def default(key: Class[_]) = 0 + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala new file mode 100644 index 0000000000..ef17327fda --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -0,0 +1,34 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect.internal.util + +class Statistics extends StatBase { + val singletonBaseTypeSeqCount = new Counter + val compoundBaseTypeSeqCount = new Counter + val typerefBaseTypeSeqCount = new Counter + val findMemberCount = new Counter + val noMemberCount = new Counter + val multMemberCount = new Counter + val findMemberNanos = new Timer + val asSeenFromCount = new Counter + val asSeenFromNanos = new Timer + val subtypeCount = new Counter + val subtypeNanos = new Timer + val sametypeCount = new Counter + val rawTypeCount = new Counter + val rawTypeFailed = new SubCounter(rawTypeCount) + val findMemberFailed = new SubCounter(findMemberCount) + val subtypeFailed = new SubCounter(subtypeCount) + val rawTypeImpl = new SubCounter(rawTypeCount) + val findMemberImpl = new SubCounter(findMemberCount) + val subtypeImpl = new SubCounter(subtypeCount) + val baseTypeSeqCount = new Counter + val baseTypeSeqLenTotal = new Counter + val typeSymbolCount = new Counter + val classSymbolCount = new Counter +} + +object Statistics extends Statistics + diff --git a/src/reflect/scala/reflect/internal/util/StringOps.scala b/src/reflect/scala/reflect/internal/util/StringOps.scala new file mode 100644 index 0000000000..281ade8134 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/StringOps.scala @@ -0,0 +1,99 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala.reflect.internal.util + +/** This object provides utility methods to extract elements + * from Strings. + * + * @author Martin Odersky + * @version 1.0 + */ +trait StringOps { + def onull(s: String) = if (s == null) "" else s + def oempty(xs: String*) = xs filterNot (x => x == null || x == "") + def ojoin(xs: String*): String = oempty(xs: _*) mkString " " + def ojoin(xs: Seq[String], sep: String): String = oempty(xs: _*) mkString sep + def ojoinOr(xs: Seq[String], sep: String, orElse: String) = { + val ys = oempty(xs: _*) + if (ys.isEmpty) orElse else ys mkString sep + } + def trimTrailingSpace(s: String) = { + if (s.length == 0 || !s.charAt(s.length - 1).isWhitespace) s + else { + var idx = s.length - 1 + while (idx >= 0 && s.charAt(idx).isWhitespace) + idx -= 1 + + s.substring(0, idx + 1) + } + } + + def decompose(str: String, sep: Char): List[String] = { + def ws(start: Int): List[String] = + if (start == str.length) List() + else if (str.charAt(start) == sep) ws(start + 1) + else { + val end = str.indexOf(sep, start) + if (end < 0) List(str.substring(start)) + else str.substring(start, end) :: ws(end + 1) + } + ws(0) + } + + def words(str: String): List[String] = decompose(str, ' ') + + def stripPrefixOpt(str: String, prefix: String): Option[String] = + if (str startsWith prefix) Some(str drop prefix.length) + else None + + def stripSuffixOpt(str: String, suffix: String): Option[String] = + if (str endsWith suffix) Some(str dropRight suffix.length) + else None + + def splitWhere(str: String, f: Char => Boolean, doDropIndex: Boolean = false): Option[(String, String)] = + splitAt(str, str indexWhere f, doDropIndex) + + def splitAt(str: String, idx: Int, doDropIndex: Boolean = false): Option[(String, String)] = + if (idx == -1) None + else Some((str take idx, str drop (if (doDropIndex) idx + 1 else idx))) + + /** Returns a string meaning "n elements". + * + * @param n ... + * @param elements ... + * @return ... + */ + def countElementsAsString(n: Int, elements: String): String = + n match { + case 0 => "no " + elements + "s" + case 1 => "one " + elements + case 2 => "two " + elements + "s" + case 3 => "three " + elements + "s" + case 4 => "four " + elements + "s" + case _ => "" + n + " " + elements + "s" + } + + /** Turns a count into a friendly English description if n<=4. + * + * @param n ... + * @return ... + */ + def countAsString(n: Int): String = + n match { + case 0 => "none" + case 1 => "one" + case 2 => "two" + case 3 => "three" + case 4 => "four" + case _ => "" + n + } +} + +object StringOps extends StringOps { } diff --git a/src/reflect/scala/reflect/internal/util/TableDef.scala b/src/reflect/scala/reflect/internal/util/TableDef.scala new file mode 100644 index 0000000000..d692a6d8f5 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/TableDef.scala @@ -0,0 +1,94 @@ +package scala.reflect.internal.util + +import TableDef._ + +/** A class for representing tabular data in a way that preserves + * its inner beauty. See Exceptional for an example usage. + * One creates an instance of TableDef by defining the columns of + * the table, then uses that to create an instance of Table by + * passing in a sequence of rows. + */ +class TableDef[T](_cols: Column[T]*) { + /** These operators are about all there is to it. + * + * ~ appends a column to the table + * >> creates a right-justified column and appends it + * << creates a left-justified column and appends it + * >+ specifies a string to separate the previous column from the next. + * if none is specified, a space is used. + */ + def ~(next: Column[T]) = retThis(cols :+= next) + def >>(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, false) + def <<(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, true) + def >+(sep: String) = retThis(separators += ((cols.size - 1, sep))) + + /** Below this point should all be considered private/internal. + */ + private var cols: List[Column[T]] = _cols.toList + private var separators: Map[Int, String] = Map() + + def defaultSep(index: Int) = if (index > (cols.size - 2)) "" else " " + def sepAfter(i: Int): String = separators.getOrElse(i, defaultSep(i)) + def sepWidths = cols.indices map (i => sepAfter(i).length) + + def columns = cols + def colNames = cols map (_.name) + def colFunctions = cols map (_.f) + def colApply(el: T) = colFunctions map (f => f(el)) + def retThis(body: => Unit): this.type = { body ; this } + + class Table(val rows: Seq[T]) extends Seq[T] { + def iterator = rows.iterator + def apply(index: Int) = rows(index) + def length = rows.length + + def maxColWidth(col: Column[T]) = col.name +: (rows map col.f) map (_.toString.length) max + def specs = cols map (_ formatSpec rows) + + val colWidths = cols map maxColWidth + val rowFormat = mkFormatString(sepAfter) + val headFormat = mkFormatString(i => " " * sepWidths(i)) + val argLists = rows map colApply + + val headers = List( + headFormat.format(colNames: _*), + (colWidths, sepWidths).zipped map ((w1, w2) => "-" * w1 + " " * w2) mkString + ) + + def mkFormatString(sepf: Int => String): String = + specs.zipWithIndex map { case (c, i) => c + sepf(i) } mkString + + def pp(): Unit = allToSeq foreach println + + def toFormattedSeq = argLists map (xs => rowFormat.format(xs: _*)) + def allToSeq = headers ++ toFormattedSeq + + override def toString = allToSeq mkString "\n" + } + + def formatterFor(rows: Seq[T]): T => String = { + val formatStr = new Table(rows).rowFormat + + x => formatStr.format(colApply(x) : _*) + } + + def table(rows: Seq[T]) = new Table(rows) + + override def toString = cols.mkString("TableDef(", ", ", ")") +} + +object TableDef { + case class Column[-T](name: String, f: T => Any, left: Boolean) { + def maxWidth(elems: Seq[T]): Int = name +: (elems map f) map (_.toString.length) max + def formatSpec(elems: Seq[T]): String = { + val justify = if (left) "-" else "" + "%" + justify + maxWidth(elems) + "s" + } + override def toString = { + val justify = if (left) "<<" else ">>" + justify + "(" + name + ")" + } + } + + def apply[T](cols: Column[T]*) = new TableDef[T](cols: _*) +} diff --git a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala new file mode 100644 index 0000000000..5fbeb5f576 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -0,0 +1,169 @@ +package scala.reflect.internal +package util + +import scala.collection.{ mutable, immutable } +import language.postfixOps + +trait TraceSymbolActivity { + val global: SymbolTable + import global._ + + if (traceSymbolActivity && global.isCompilerUniverse) + scala.sys addShutdownHook showAllSymbols() + + private type Set[T] = scala.collection.immutable.Set[T] + private val Set = scala.collection.immutable.Set + + val allSymbols = mutable.Map[Int, Symbol]() + val allChildren = mutable.Map[Int, List[Int]]() withDefaultValue Nil + val prevOwners = mutable.Map[Int, List[(Int, Phase)]]() withDefaultValue Nil + val symsCaused = mutable.Map[Int, Int]() withDefaultValue 0 + val allTrees = mutable.Set[Tree]() + + def recordSymbolsInTree(tree: Tree) { + allTrees += tree + } + + def recordNewSymbol(sym: Symbol) { + if (sym.id > 1) { + allSymbols(sym.id) = sym + allChildren(sym.owner.id) ::= sym.id + } + } + def recordNewSymbolOwner(sym: Symbol, newOwner: Symbol) { + val sid = sym.id + val oid = sym.owner.id + val nid = newOwner.id + + prevOwners(sid) ::= (oid -> phase) + allChildren(oid) = allChildren(oid) filterNot (_ == sid) + allChildren(nid) ::= sid + } + + /** TODO. + */ + private def reachableDirectlyFromSymbol(sym: Symbol): List[Symbol] = ( + List(sym.owner, sym.alias, sym.thisSym) + ++ sym.children + ++ sym.info.parents.map(_.typeSymbol) + ++ sym.typeParams + ++ sym.paramss.flatten + ) + private def reachable[T](inputs: Traversable[T], mkSymbol: T => Symbol): Set[Symbol] = { + def loop(seen: Set[Symbol], remaining: List[Symbol]): Set[Symbol] = { + remaining match { + case Nil => seen + case head :: rest => + if ((head eq null) || (head eq NoSymbol) || seen(head)) loop(seen, rest) + else loop(seen + head, rest ++ reachableDirectlyFromSymbol(head).filterNot(seen)) + } + } + loop(immutable.Set(), inputs.toList map mkSymbol filterNot (_ eq null) distinct) + } + private def treeList(t: Tree) = { + val buf = mutable.ListBuffer[Tree]() + t foreach (buf += _) + buf.toList + } + + private def reachableFromSymbol(root: Symbol): Set[Symbol] = + reachable[Symbol](List(root, root.info.typeSymbol), x => x) + + private def reachableFromTree(tree: Tree): Set[Symbol] = + reachable[Tree](treeList(tree), _.symbol) + + private def signature(id: Int) = runBeforeErasure(allSymbols(id).defString) + + private def dashes(s: Any): String = ("" + s) map (_ => '-') + private def show(s1: Any, ss: Any*) { + println("%-12s".format(s1) +: ss mkString " ") + } + private def showHeader(s1: Any, ss: Any*) { + show(s1, ss: _*) + show(dashes(s1), ss map dashes: _*) + } + private def showSym(sym: Symbol) { + def prefix = (" " * (sym.ownerChain.length - 1)) + sym.id + try println("%s#%s %s".format(prefix, sym.accurateKindString, sym.name.decode)) + catch { + case x => println(prefix + " failed: " + x) + } + allChildren(sym.id).sorted foreach showIdAndRemove + } + private def showIdAndRemove(id: Int) { + allSymbols remove id foreach showSym + } + private def symbolStr(id: Int): String = { + if (id == 1) "NoSymbol" else { + val sym = allSymbols(id) + sym.accurateKindString + " " + sym.name.decode + } + } + private def ownerStr(id: Int): String = { + val sym = allSymbols(id) + sym.name.decode + "#" + sym.id + } + + private def freq[T, U](xs: collection.Traversable[T])(fn: T => U): List[(U, Int)] = { + val ys = xs groupBy fn mapValues (_.size) + ys.toList sortBy (-_._2) + } + + private def showMapFreq[T](xs: collection.Map[T, Traversable[_]])(showFn: T => String) { + xs.mapValues(_.size).toList.sortBy(-_._2) take 100 foreach { case (k, size) => + show(size, showFn(k)) + } + println("\n") + } + private def showFreq[T, U](xs: Traversable[T])(groupFn: T => U, showFn: U => String = (x: U) => "" + x) = { + showMapFreq(xs.toList groupBy groupFn)(showFn) + } + private lazy val findErasurePhase: Phase = { + var ph = phase + while (ph != NoPhase && ph.name != "erasure") { + ph = ph.prev + } + ph + } + private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) + + def showAllSymbols() { + if (!traceSymbolActivity) return + allSymbols(1) = NoSymbol + + println("" + allSymbols.size + " symbols created.") + println("") + + showHeader("descendants", "symbol") + showFreq(allSymbols.values flatMap (_.ownerChain drop 1))(_.id, symbolStr) + + showHeader("children", "symbol") + showMapFreq(allChildren)(symbolStr) + + if (prevOwners.nonEmpty) { + showHeader("prev owners", "symbol") + showMapFreq(prevOwners) { k => + val owners = (((allSymbols(k).owner.id, NoPhase)) :: prevOwners(k)) map { + case (oid, NoPhase) => "-> owned by " + ownerStr(oid) + case (oid, ph) => "-> owned by %s (until %s)".format(ownerStr(oid), ph) + } + signature(k) :: owners mkString "\n " + } + } + + val nameFreq = allSymbols.values.toList groupBy (_.name) + showHeader("frequency", "%-15s".format("name"), "owners") + showMapFreq(nameFreq) { name => + "%-15s %s".format(name.decode, { + val owners = freq(nameFreq(name))(_.owner) + + "%4s owners (%s)".format( + owners.size, + owners.take(3).map({ case (k, v) => v + "/" + k }).mkString(", ") + ", ..." + ) + }) + } + + allSymbols.keys.toList.sorted foreach showIdAndRemove + } +} diff --git a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala new file mode 100644 index 0000000000..9882aad5e5 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -0,0 +1,61 @@ +package scala.reflect.internal.util + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.Builder +import scala.collection.mutable.SetBuilder +import scala.collection.generic.Clearable +import scala.runtime.AbstractFunction1 + +/** A bare-bones implementation of a mutable `Set` that uses weak references + * to hold the elements. + * + * This implementation offers only add/remove/test operations, + * therefore it does not fulfill the contract of Scala collection sets. + */ +class WeakHashSet[T <: AnyRef] extends AbstractFunction1[T, Boolean] with Clearable { + private val underlying = mutable.HashSet[WeakReferenceWithEquals[T]]() + + /** Add the given element to this set. */ + def +=(elem: T): this.type = { + underlying += new WeakReferenceWithEquals(elem) + this + } + + /** Remove the given element from this set. */ + def -=(elem: T): this.type = { + underlying -= new WeakReferenceWithEquals(elem) + this + } + + /** Does the given element belong to this set? */ + def contains(elem: T): Boolean = + underlying.contains(new WeakReferenceWithEquals(elem)) + + /** Does the given element belong to this set? */ + def apply(elem: T): Boolean = contains(elem) + + /** Return the number of elements in this set, including reclaimed elements. */ + def size = underlying.size + + /** Remove all elements in this set. */ + def clear() = underlying.clear() +} + +/** A WeakReference implementation that implements equals and hashCode by + * delegating to the referent. + */ +class WeakReferenceWithEquals[T <: AnyRef](ref: T) { + def get(): T = underlying.get() + + override val hashCode = ref.hashCode + + override def equals(other: Any): Boolean = other match { + case wf: WeakReferenceWithEquals[_] => + underlying.get() == wf.get() + case _ => + false + } + + private val underlying = new java.lang.ref.WeakReference(ref) +} diff --git a/src/reflect/scala/reflect/makro/Aliases.scala b/src/reflect/scala/reflect/makro/Aliases.scala new file mode 100644 index 0000000000..c78c9a6a04 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Aliases.scala @@ -0,0 +1,27 @@ +package scala.reflect +package makro + +trait Aliases { + self: Context => + + type Symbol = universe.Symbol + type Type = universe.Type + type Name = universe.Name + type TermName = universe.TermName + type TypeName = universe.TypeName + type Tree = universe.Tree + // type Position = universe.Position + type Scope = universe.Scope + type Modifiers = universe.Modifiers + + type Expr[+T] = universe.Expr[T] + val Expr = universe.Expr + + type TypeTag[T] = universe.TypeTag[T] + type ConcreteTypeTag[T] = universe.ConcreteTypeTag[T] + val TypeTag = universe.TypeTag + val ConcreteTypeTag = universe.ConcreteTypeTag + def typeTag[T](implicit ttag: TypeTag[T]) = ttag + def concreteTypeTag[T](implicit cttag: ConcreteTypeTag[T]) = cttag + def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe +} diff --git a/src/reflect/scala/reflect/makro/CapturedVariables.scala b/src/reflect/scala/reflect/makro/CapturedVariables.scala new file mode 100644 index 0000000000..592e28b3b2 --- /dev/null +++ b/src/reflect/scala/reflect/makro/CapturedVariables.scala @@ -0,0 +1,21 @@ +package scala.reflect +package makro + +trait CapturedVariables { + self: Context => + + import mirror._ + + /** Mark a variable as captured; i.e. force boxing in a *Ref type. + */ + def captureVariable(vble: Symbol): Unit + + /** Mark given identifier as a reference to a captured variable itself + * suppressing dereferencing with the `elem` field. + */ + def referenceCapturedVariable(vble: Symbol): Tree + + /** Convert type of a captured variable to *Ref type. + */ + def capturedVariableType(vble: Symbol): Type +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/Context.scala b/src/reflect/scala/reflect/makro/Context.scala new file mode 100644 index 0000000000..f9858a063c --- /dev/null +++ b/src/reflect/scala/reflect/makro/Context.scala @@ -0,0 +1,40 @@ +package scala.reflect +package makro + +import language.experimental.macros + +// todo. introduce context hierarchy +// the most lightweight context should just expose the stuff from the SIP +// the full context should include all traits from scala.reflect.makro (and probably reside in scala-compiler.jar) + +trait Context extends Aliases + with CapturedVariables + with Enclosures + with Infrastructure + with Names + with Reifiers + with FrontEnds + with Settings + with Typers + with Parsers + with Exprs + with TypeTags + with Evals + with ExprUtils { + + /** The compile-time universe */ + val universe: Universe + + /** The mirror of the compile-time universe */ + val mirror: MirrorOf[universe.type] + + /** The type of the prefix tree from which the macro is selected */ + type PrefixType + + /** The prefix tree from which the macro is selected */ + val prefix: Expr[PrefixType] + + /** Alias to the underlying mirror's reify */ + // implementation is magically hardwired to `scala.reflect.makro.runtime.ContextReifiers` + def reify[T](expr: T): Expr[T] = macro ??? +} diff --git a/src/reflect/scala/reflect/makro/Enclosures.scala b/src/reflect/scala/reflect/makro/Enclosures.scala new file mode 100644 index 0000000000..69bd8d09c7 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Enclosures.scala @@ -0,0 +1,54 @@ +package scala.reflect +package makro + +trait Enclosures { + self: Context => + + /** The tree that undergoes macro expansion. + * Can be useful to get an offset or a range position of the entire tree being processed. + */ + val macroApplication: Tree + + /** Contexts that represent macros in-flight, including the current one. Very much like a stack trace, but for macros only. + * Can be useful for interoperating with other macros and for imposing compiler-friendly limits on macro expansion. + * + * Is also priceless for emitting sane error messages for macros that are called by other macros on synthetic (i.e. position-less) trees. + * In that dire case navigate the ``enclosingMacros'' stack, and it will most likely contain at least one macro with a position-ful macro application. + * See ``enclosingPosition'' for a default implementation of this logic. + * + * Unlike `openMacros`, this is a val, which means that it gets initialized when the context is created + * and always stays the same regardless of whatever happens during macro expansion. + */ + val enclosingMacros: List[Context] + + /** Types along with corresponding trees for which implicit arguments are currently searched. + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * + * Unlike `openImplicits`, this is a val, which means that it gets initialized when the context is created + * and always stays the same regardless of whatever happens during macro expansion. + */ + val enclosingImplicits: List[(Type, Tree)] + + /** Tries to guess a position for the enclosing application. + * But that is simple, right? Just dereference ``pos'' of ``macroApplication''? Not really. + * If we're in a synthetic macro expansion (no positions), we must do our best to infer the position of something that triggerd this expansion. + * Surprisingly, quite often we can do this by navigation the ``enclosingMacros'' stack. + */ + val enclosingPosition: Position + + /** Tree that corresponds to the enclosing application, or EmptyTree if not applicable. + */ + val enclosingApplication: Tree + + /** Tree that corresponds to the enclosing method, or EmptyTree if not applicable. + */ + val enclosingMethod: Tree + + /** Tree that corresponds to the enclosing class, or EmptyTree if not applicable. + */ + val enclosingClass: Tree + + /** Compilation unit that contains this macro application. + */ + val enclosingUnit: CompilationUnit +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/Evals.scala b/src/reflect/scala/reflect/makro/Evals.scala new file mode 100644 index 0000000000..4e5fc2f97f --- /dev/null +++ b/src/reflect/scala/reflect/makro/Evals.scala @@ -0,0 +1,9 @@ +package scala.reflect +package makro + +trait Evals { + self: Context => + + /** .. */ + def eval[T](expr: Expr[T]): T +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/ExprUtils.scala b/src/reflect/scala/reflect/makro/ExprUtils.scala new file mode 100644 index 0000000000..c3e5cc6bc1 --- /dev/null +++ b/src/reflect/scala/reflect/makro/ExprUtils.scala @@ -0,0 +1,32 @@ +package scala.reflect +package makro + +trait ExprUtils { + self: Context => + + def literalNull: Expr[Null] + + def literalUnit: Expr[Unit] + + def literalTrue: Expr[Boolean] + + def literalFalse: Expr[Boolean] + + def literal(x: Boolean): Expr[Boolean] + + def literal(x: Byte): Expr[Byte] + + def literal(x: Short): Expr[Short] + + def literal(x: Int): Expr[Int] + + def literal(x: Long): Expr[Long] + + def literal(x: Float): Expr[Float] + + def literal(x: Double): Expr[Double] + + def literal(x: String): Expr[String] + + def literal(x: Char): Expr[Char] +} diff --git a/src/reflect/scala/reflect/makro/Exprs.scala b/src/reflect/scala/reflect/makro/Exprs.scala new file mode 100644 index 0000000000..b4f8e7ac4e --- /dev/null +++ b/src/reflect/scala/reflect/makro/Exprs.scala @@ -0,0 +1,8 @@ +package scala.reflect +package makro + +trait Exprs { + self: Context => + + def Expr[T: TypeTag](tree: Tree): Expr[T] +} diff --git a/src/reflect/scala/reflect/makro/FrontEnds.scala b/src/reflect/scala/reflect/makro/FrontEnds.scala new file mode 100644 index 0000000000..5087f90174 --- /dev/null +++ b/src/reflect/scala/reflect/makro/FrontEnds.scala @@ -0,0 +1,42 @@ +package scala.reflect +package makro + +trait FrontEnds extends scala.reflect.api.FrontEnds { + self: Context => + + import mirror._ + + type Position = universe.Position + + /** Exposes means to control the compiler UI */ + def frontEnd: FrontEnd + def setFrontEnd(frontEnd: FrontEnd): this.type + def withFrontEnd[T](frontEnd: FrontEnd)(op: => T): T + + /** For sending a message which should not be labeled as a warning/error, + * but also shouldn't require -verbose to be visible. + * Use ``enclosingPosition'' if you're in doubt what position to pass to ``pos''. + */ + def echo(pos: Position, msg: String): Unit + + /** Informational messages, suppressed unless -verbose or force=true. + * Use ``enclosingPosition'' if you're in doubt what position to pass to ``pos''. + */ + def info(pos: Position, msg: String, force: Boolean): Unit + + /** Warnings and errors. + * Use ``enclosingPosition'' if you're in doubt what position to pass to ``pos''. + */ + def hasWarnings: Boolean + def hasErrors: Boolean + def warning(pos: Position, msg: String): Unit + def error(pos: Position, msg: String): Unit + + /** Abruptly terminates current macro expansion leaving a note about what happened. + * Use ``enclosingPosition'' if you're in doubt what position to pass to ``pos''. + */ + def abort(pos: Position, msg: String): Nothing + + /** Drops into interactive mode if supported by the compiler UI */ + def interactive(): Unit +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/Infrastructure.scala b/src/reflect/scala/reflect/makro/Infrastructure.scala new file mode 100644 index 0000000000..e6bfe33366 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Infrastructure.scala @@ -0,0 +1,103 @@ +package scala.reflect +package makro + +trait Infrastructure { + self: Context => + + /** Determines whether the compiler expanding a macro targets JVM. + */ + val forJVM: Boolean + + /** Determines whether the compiler expanding a macro targets CLR. + */ + val forMSIL: Boolean + + /** Determines whether the compiler expanding a macro is a presentation compiler. + */ + val forInteractive: Boolean + + /** Determines whether the compiler expanding a macro is a Scaladoc compiler. + */ + val forScaladoc: Boolean + + /** Exposes current compilation run. + */ + val currentRun: Run + + /** Exposes library classpath. + */ + val libraryClassPath: List[java.net.URL] + + /** Exposes a classloader that corresponds to the library classpath. + * + * With this classloader you can perform on-the-fly evaluation of macro arguments. + * For example, consider this code snippet: + * + * def staticEval[T](x: T) = macro staticEval[T] + * + * def staticEval[T: c.TypeTag](c: Context)(x: c.Expr[T]) = { + * import scala.reflect.runtime.{universe => ru} + * val mirror = ru.runtimeMirror(c.libraryClassLoader) + * import scala.tools.reflect.ToolBox + * val toolBox = mirror.mkToolBox() + * val importer = ru.mkImporter(c.universe).asInstanceOf[ru.Importer { val from: c.universe.type }] + * val tree = c.resetAllAttrs(x.tree.duplicate) + * val imported = importer.importTree(tree) + * val valueOfX = toolBox.runExpr(imported).asInstanceOf[T] + * ... + * } + * + * // [Eugene++] using this guy will tremendously slow down the compilation + * // https://twitter.com/xeno_by/status/201248317831774208 + * // todo. we need to address this somehow + */ + def libraryClassLoader: ClassLoader + + /** As seen by macro API, compilation run is an opaque type that can be deconstructed into: + * 1) Current compilation unit + * 2) List of all compilation units that comprise the run + */ + type Run + + val Run: RunExtractor + + abstract class RunExtractor { + def unapply(run: Run): Option[(CompilationUnit, List[CompilationUnit])] + } + + /** As seen by macro API, compilation unit is an opaque type that can be deconstructed into: + * 1) File that corresponds to the unit (if not applicable, null) + * 2) Content of the file (if not applicable, empty array) + * 3) Body, i.e. the AST that represents the compilation unit + */ + type CompilationUnit + + val CompilationUnit: CompilationUnitExtractor + + abstract class CompilationUnitExtractor { + def unapply(compilationUnit: CompilationUnit): Option[(java.io.File, Array[Char], Tree)] + } + + /** Returns a macro definition which triggered this macro expansion. + */ + val currentMacro: Symbol + + // todo. redo caches as discussed on Reflecting Meeting 2012/03/29 + // https://docs.google.com/document/d/1oUZGQpdt2qwioTlJcSt8ZFQwVLTvpxn8xa67P8OGVpU/edit + + /** A cache shared by all invocations of all macros across all compilation runs. + * + * Needs to be used with extreme care, since memory leaks here will swiftly crash the presentation compiler. + * For example, Scala IDE typically launches a compiler run on every edit action so there might be hundreds of runs per minute. + */ + val globalCache: collection.mutable.Map[Any, Any] + + /** A cache shared by all invocations of the same macro within a single compilation run. + * + * This cache is cleared automatically after a compilation run is completed or abandoned. + * It is also specific to a particular macro definition. + * + * To share data between different macros and/or different compilation runs, use ``globalCache''. + */ + val cache: collection.mutable.Map[Any, Any] +} diff --git a/src/reflect/scala/reflect/makro/Names.scala b/src/reflect/scala/reflect/makro/Names.scala new file mode 100644 index 0000000000..909976d83c --- /dev/null +++ b/src/reflect/scala/reflect/makro/Names.scala @@ -0,0 +1,15 @@ +package scala.reflect +package makro + +trait Names { + self: Context => + + /** Creates a fresh string */ + def fresh(): String + + /** Creates a fresh string from the provided string */ + def fresh(name: String): String + + /** Creates a fresh name from the provided name */ + def fresh[NameType <: Name](name: NameType): NameType +} diff --git a/src/reflect/scala/reflect/makro/Parsers.scala b/src/reflect/scala/reflect/makro/Parsers.scala new file mode 100644 index 0000000000..9866b7e491 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Parsers.scala @@ -0,0 +1,18 @@ +package scala.reflect +package makro + +trait Parsers { + self: Context => + + /** .. */ + // todo. distinguish between `parseExpr` and `parse` + def parse(code: String): Tree + + /** Represents an error during parsing + */ + type ParseError <: Throwable + val ParseError: ParseErrorExtractor + abstract class ParseErrorExtractor { + def unapply(error: ParseError): Option[(Position, String)] + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/Reifiers.scala b/src/reflect/scala/reflect/makro/Reifiers.scala new file mode 100644 index 0000000000..f39f56f935 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Reifiers.scala @@ -0,0 +1,91 @@ +package scala.reflect +package makro + +trait Reifiers { + self: Context => + + /** Reification prefix that refers to the base reflexive universe, ``scala.reflect.basis''. + * Providing it for the ``prefix'' parameter of ``reifyTree'' or ``reifyType'' will create a tree that can be inspected at runtime. + */ + val basisUniverse: Tree + + /** Reification prefix that refers to the runtime reflexive universe, ``scala.reflect.runtime.universe''. + * Providing it for the ``prefix'' parameter of ``reifyTree'' or ``reifyType'' will create a full-fledged tree that can be inspected at runtime. + */ + val runtimeUniverse: Tree + + /** Given a tree, generate a tree that when compiled and executed produces the original tree. + * For more information and examples see the documentation for ``Universe.reify''. + * + * The produced tree will be bound to the specified ``universe'' and ``mirror''. + * Possible values for ``universe'' include ``basisUniverse'' and ``runtimeUniverse''. + * Possible values for ``mirror'' include ``EmptyTree'' (in that case the reifier will automatically pick an appropriate mirror). + * + * This function is deeply connected to ``Universe.reify'', a macro that reifies arbitrary expressions into runtime trees. + * They do very similar things (``Universe.reify'' calls ``Context.reifyTree'' to implement itself), but they operate on different metalevels (see below). + * + * Let's study the differences between ``Context.reifyTree'' and ``Universe.reify'' on an example of using them inside a ``fooMacro'' macro: + * + * * Since reify itself is a macro, it will be executed when fooMacro is being compiled (metalevel -1) + * and will produce a tree that when evaluated during macro expansion of fooMacro (metalevel 0) will recreate the input tree. + * + * This provides a facility analogous to quasi-quoting. Writing "reify{ expr }" will generate an AST that represents expr. + * Afterwards this AST (or its parts) can be used to construct the return value of fooMacro. + * + * * reifyTree is evaluated during macro expansion (metalevel 0) + * and will produce a tree that when evaluated during the runtime of the program (metalevel 1) will recreate the input tree. + * + * This provides a way to retain certain trees from macro expansion time to be inspected later, in the runtime. + * For example, DSL authors may find it useful to capture DSL snippets into ASTs that are then processed at runtime in a domain-specific way. + * + * Also note the difference between universes of the runtime trees produced by two reifies: + * + * * The result of compiling and running the result of reify will be bound to the Universe that called reify. + * This is possible because it's a macro, so it can generate whatever code it wishes. + * + * * The result of compiling and running the result of reifyTree will be the ``prefix'' that needs to be passed explicitly. + * This happens because the Universe of the evaluated result is from a different metalevel than the Context the called reify. + * + * Typical usage of this function is to retain some of the trees received/created by a macro + * into the form that can be inspected (via pattern matching) or compiled/run (by a reflective ToolBox) during the runtime. + */ + def reifyTree(universe: Tree, mirror: Tree, tree: Tree): Tree + + /** Given a type, generate a tree that when compiled and executed produces the original type. + * The produced tree will be bound to the specified ``universe'' and ``mirror''. + * For more information and examples see the documentation for ``Context.reifyTree'' and ``Universe.reify''. + */ + def reifyType(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean = false): Tree + + /** Given a type, generate a tree that when compiled and executed produces the runtime class of the original type. + * If ``concrete'' is true, then this function will bail on types, who refer to abstract types (like `ClassTag` does). + */ + def reifyRuntimeClass(tpe: Type, concrete: Boolean = true): Tree + + /** Given a type, generate a tree that when compiled and executed produces the runtime class of the enclosing class or module. + * Returns `EmptyTree` if there does not exist an enclosing class or module. + */ + def reifyEnclosingRuntimeClass: Tree + + /** Undoes reification of a tree. + * + * This reversion doesn't simply restore the original tree (that would lose the context of reification), + * but does something more involved that conforms to the following laws: + * + * 1) unreifyTree(reifyTree(tree)) != tree // unreified tree is tree + saved context + * // in current implementation, the result of unreify is opaque + * // i.e. there's no possibility to inspect underlying tree/context + * + * 2) reifyTree(unreifyTree(reifyTree(tree))) == reifyTree(tree) // the result of reifying a tree in its original context equals to + * // the result of reifying a tree along with its saved context + * + * 3) compileAndEval(unreifyTree(reifyTree(tree))) ~ compileAndEval(tree) // at runtime original and unreified trees are behaviorally equivalent + */ + def unreifyTree(tree: Tree): Tree +} + +// made these guys non path-dependent, otherwise exception handling quickly becomes a mess + +case class ReificationError(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg) + +case class UnexpectedReificationError(val pos: reflect.api.PositionApi, val msg: String, val cause: Throwable = null) extends Throwable(msg, cause) diff --git a/src/reflect/scala/reflect/makro/Settings.scala b/src/reflect/scala/reflect/makro/Settings.scala new file mode 100644 index 0000000000..c6c7e5870b --- /dev/null +++ b/src/reflect/scala/reflect/makro/Settings.scala @@ -0,0 +1,40 @@ +package scala.reflect +package makro + +trait Settings { + self: Context => + + /** Exposes macro-specific settings as a list of strings. + * These settings are passed to the compiler via the "-Xmacro-settings:setting1,setting2...,settingN" command-line option. + */ + def settings: List[String] + + /** Exposes current compiler settings as a list of options. + * Use `scalac -help`, `scalac -X` and `scalac -Y` to learn about currently supported options. + */ + // [Eugene] ugly? yes, but I don't really fancy copy/pasting all our settings here and keep it synchronized at all times + // why all settings? because macros need to be in full control of the stuff going on + // maybe later we can implement a gettable/settable list of important settings, but for now let's leave it like that + def compilerSettings: List[String] + + /** Updates current compiler settings with an option string. + * Use `scalac -help`, `scalac -X` and `scalac -Y` to learn about currently supported options. + * todo. http://groups.google.com/group/scala-internals/browse_thread/thread/07c18cff41f59203 + */ + def setCompilerSettings(options: String): this.type + + /** Updates current compiler settings with a list of options. + * Use `scalac -help`, `scalac -X` and `scalac -Y` to learn about currently supported options. + */ + def setCompilerSettings(options: List[String]): this.type + + /** Temporary sets compiler settings to a given option string and executes a given closure. + * Use `scalac -help`, `scalac -X` and `scalac -Y` to learn about currently supported options. + */ + def withCompilerSettings[T](options: String)(op: => T): T + + /** Temporary sets compiler settings to a given list of options and executes a given closure. + * Use `scalac -help`, `scalac -X` and `scalac -Y` to learn about currently supported options. + */ + def withCompilerSettings[T](options: List[String])(op: => T): T +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/TreeBuilder.scala b/src/reflect/scala/reflect/makro/TreeBuilder.scala new file mode 100644 index 0000000000..c4179b9c80 --- /dev/null +++ b/src/reflect/scala/reflect/makro/TreeBuilder.scala @@ -0,0 +1,60 @@ +package scala.reflect +package makro + +// [Eugene] I added some stuff that was necessary for typetag materialization macros +// but we should think it over and pick other generally useful stuff +// same goes for tree traversers/transformers, type maps, etc +// and once we expose all that, there's another question: how do we stay in sync? +abstract class TreeBuilder { + val global: Universe + + import global._ + import definitions._ + + /** 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 + + /** 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 + + /** Builds a typed reference to given symbol with given stable prefix. */ + def mkAttributedRef(pre: Type, sym: Symbol): Tree + + /** Builds a typed reference to given symbol. */ + def mkAttributedRef(sym: Symbol): Tree + + /** Builds a typed This reference to given symbol. */ + def mkAttributedThis(sym: Symbol): Tree + + /** Builds a typed Ident with an underlying symbol. */ + def mkAttributedIdent(sym: Symbol): Tree + + /** Builds a typed Select with an underlying symbol. */ + def mkAttributedSelect(qual: Tree, sym: Symbol): Tree + + /** A creator for method calls, e.g. fn[T1, T2, ...](v1, v2, ...) + * There are a number of variations. + * + * @param receiver symbol of the method receiver + * @param methodName name of the method to call + * @param targs type arguments (if Nil, no TypeApply node will be generated) + * @param args value arguments + * @return the newly created trees. + */ + def mkMethodCall(receiver: Symbol, methodName: Name, targs: List[Type], args: List[Tree]): Tree + def mkMethodCall(method: Symbol, targs: List[Type], args: List[Tree]): Tree + def mkMethodCall(method: Symbol, args: List[Tree]): Tree + def mkMethodCall(target: Tree, args: List[Tree]): Tree + def mkMethodCall(receiver: Symbol, methodName: Name, args: List[Tree]): Tree + def mkMethodCall(receiver: Tree, method: Symbol, targs: List[Type], args: List[Tree]): Tree + def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree + def mkNullaryCall(method: Symbol, targs: List[Type]): Tree +} diff --git a/src/reflect/scala/reflect/makro/TypeTags.scala b/src/reflect/scala/reflect/makro/TypeTags.scala new file mode 100644 index 0000000000..3251c27908 --- /dev/null +++ b/src/reflect/scala/reflect/makro/TypeTags.scala @@ -0,0 +1,9 @@ +package scala.reflect +package makro + +trait TypeTags { + self: Context => + + def TypeTag[T](tpe: Type): TypeTag[T] + def ConcreteTypeTag[T](tpe: Type): ConcreteTypeTag[T] +} diff --git a/src/reflect/scala/reflect/makro/Typers.scala b/src/reflect/scala/reflect/makro/Typers.scala new file mode 100644 index 0000000000..2610d7dd50 --- /dev/null +++ b/src/reflect/scala/reflect/makro/Typers.scala @@ -0,0 +1,86 @@ +package scala.reflect +package makro + +trait Typers { + self: Context => + + import universe._ + + /** Contexts that represent macros in-flight, including the current one. Very much like a stack trace, but for macros only. + * Can be useful for interoperating with other macros and for imposing compiler-friendly limits on macro expansion. + * + * Is also priceless for emitting sane error messages for macros that are called by other macros on synthetic (i.e. position-less) trees. + * In that dire case navigate the ``openMacros'' stack, and it will most likely contain at least one macro with a position-ful macro application. + * See ``enclosingPosition'' for a default implementation of this logic. + * + * Unlike `enclosingMacros`, this is a def, which means that it gets recalculated on every invocation, + * so it might change depending on what is going on during macro expansion. + */ + def openMacros: List[Context] + + /** Types along with corresponding trees for which implicit arguments are currently searched. + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * + * Unlike `enclosingImplicits`, this is a def, which means that it gets recalculated on every invocation, + * so it might change depending on what is going on during macro expansion. + */ + def openImplicits: List[(Type, Tree)] + + /** Typechecks the provided tree against the expected type ``pt'' in the macro callsite context. + * + * If ``silent'' is false, ``TypeError'' will be thrown in case of a typecheck error. + * If ``silent'' is true, the typecheck is silent and will return ``EmptyTree'' if an error occurs. + * Such errors don't vanish and can be inspected by turning on -Ymacro-debug-verbose. + * Unlike in ``inferImplicitValue'' and ``inferImplicitView'', ``silent'' is false by default. + * + * Typechecking can be steered with the following optional parameters: + * ``withImplicitViewsDisabled'' recursively prohibits implicit views (though, implicit vals will still be looked up and filled in), default value is false + * ``withMacrosDisabled'' recursively prohibits macro expansions and macro-based implicits, default value is false + */ + def typeCheck(tree: Tree, pt: Type = WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree + + /** Infers an implicit value of the expected type ``pt'' in the macro callsite context. + * Optional ``pos'' parameter provides a position that will be associated with the implicit search. + * + * If ``silent'' is false, ``TypeError'' will be thrown in case of an inference error. + * If ``silent'' is true, the typecheck is silent and will return ``EmptyTree'' if an error occurs. + * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Unlike in ``typeCheck'', ``silent'' is true by default. + */ + def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree + + /** Infers an implicit view from the provided tree ``tree'' from the type ``from'' to the type ``to'' in the macro callsite context. + * + * Optional ``pos'' parameter provides a position that will be associated with the implicit search. + * Another optional parameter, ``reportAmbiguous`` controls whether ambiguous implicit errors should be reported. + * If we search for a view simply to find out whether one type is coercible to another, it might be desirable to set this flag to ``false''. + * + * If ``silent'' is false, ``TypeError'' will be thrown in case of an inference error. + * If ``silent'' is true, the typecheck is silent and will return ``EmptyTree'' if an error occurs. + * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Unlike in ``typeCheck'', ``silent'' is true by default. + */ + def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true, pos: Position = enclosingPosition): Tree + + /** Recursively resets symbols and types in a given tree. + * + * Note that this does not revert the tree to its pre-typer shape. + * For more info, read up https://issues.scala-lang.org/browse/SI-5464. + */ + def resetAllAttrs(tree: Tree): Tree + + /** Recursively resets locally defined symbols and types in a given tree. + * + * Note that this does not revert the tree to its pre-typer shape. + * For more info, read up https://issues.scala-lang.org/browse/SI-5464. + */ + def resetLocalAttrs(tree: Tree): Tree + + /** Represents an error during typechecking + */ + type TypeError <: Throwable + val TypeError: TypeErrorExtractor + abstract class TypeErrorExtractor { + def unapply(error: TypeError): Option[(Position, String)] + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/Universe.scala b/src/reflect/scala/reflect/makro/Universe.scala new file mode 100644 index 0000000000..ffc4042a0a --- /dev/null +++ b/src/reflect/scala/reflect/makro/Universe.scala @@ -0,0 +1,118 @@ +package scala.reflect +package makro + +abstract class Universe extends scala.reflect.api.Universe { + + val treeBuild: TreeBuilder { val global: Universe.this.type } + + // Symbol extensions --------------------------------------------------------------- + + override type Symbol >: Null <: SymbolContextApi + + /** The extended API of symbols that's supported in macro context universes + */ + trait SymbolContextApi extends SymbolApi { this: Symbol => + + // [Eugene++ to Martin] should we also add mutability methods here (similarly to what's done below for trees)? + // I'm talking about `setAnnotations` and friends + + /** Can this symbol be loaded by a reflective mirror? + * + * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. + * Such annotations (also called "pickles") are applied on top-level classes and include information + * about all symbols reachable from the annotee. However, local symbols (e.g. classes or definitions local to a block) + * are typically unreachable and information about them gets lost. + * + * This method is useful for macro writers who wish to save certain ASTs to be used at runtime. + * With `isLocatable' it's possible to check whether a tree can be retained as is, or it needs special treatment. + */ + def isLocatable: Boolean + + /** Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + */ + def isStatic: Boolean + } + + // Tree extensions --------------------------------------------------------------- + + override type Tree >: Null <: TreeContextApi + + /** The extended API of trees that's supported in macro context universes + */ + trait TreeContextApi extends TreeApi { this: Tree => + + /** ... */ + def pos_=(pos: Position): Unit + + /** ... */ + def setPos(newpos: Position): this.type + + /** ... */ + def tpe_=(t: Type): Unit + + /** Set tpe to give `tp` and return this. + */ + def setType(tp: Type): this.type + + /** Like `setType`, but if this is a previously empty TypeTree that + * fact is remembered so that resetAllAttrs will snap back. + * + * @PP: Attempting to elaborate on the above, I find: If defineType + * is called on a TypeTree whose type field is null or NoType, + * this is recorded as "wasEmpty = true". That value is used in + * ResetAttrsTraverser, which nulls out the type field of TypeTrees + * for which wasEmpty is true, leaving the others alone. + * + * resetAllAttrs is used in situations where some speculative + * typing of a tree takes place, fails, and the tree needs to be + * returned to its former state to try again. So according to me: + * using `defineType` instead of `setType` is how you communicate + * that the type being set does not depend on any previous state, + * and therefore should be abandoned if the current line of type + * inquiry doesn't work out. + */ + def defineType(tp: Type): this.type + + /** ... */ + def symbol_=(sym: Symbol): Unit + + /** ... */ + def setSymbol(sym: Symbol): this.type + + /** ... */ + def attachments: base.Attachments { type Pos = Position } + + /** ... */ + def addAttachment(attachment: Any): this.type + + /** ... */ + def removeAttachment[T: ClassTag]: this.type + } + + override type SymTree >: Null <: Tree with SymTreeContextApi + + /** The extended API of sym trees that's supported in macro context universes + */ + trait SymTreeContextApi extends SymTreeApi { this: SymTree => + var symbol: Symbol + } + + override type TypeTree >: Null <: TypTree with TypeTreeContextApi + + /** The extended API of sym trees that's supported in macro context universes + */ + trait TypeTreeContextApi extends TypeTreeApi { this: TypeTree => + def setOriginal(tree: Tree): this.type + } + + override type Ident >: Null <: RefTree with IdentContextApi + + /** The extended API of idents that's supported in macro context universes + */ + trait IdentContextApi extends IdentApi { this: Ident => + def isBackquoted: Boolean + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/makro/package.scala b/src/reflect/scala/reflect/makro/package.scala new file mode 100644 index 0000000000..3c0e51030e --- /dev/null +++ b/src/reflect/scala/reflect/makro/package.scala @@ -0,0 +1,6 @@ +package scala.reflect + +package object makro { + + type MirrorOf[U <: base.Universe with Singleton] = base.MirrorOf[U] +} diff --git a/src/reflect/scala/reflect/runtime/AbstractFile.scala b/src/reflect/scala/reflect/runtime/AbstractFile.scala new file mode 100644 index 0000000000..0f88af1b0a --- /dev/null +++ b/src/reflect/scala/reflect/runtime/AbstractFile.scala @@ -0,0 +1,7 @@ +package scala.reflect +package runtime + +class AbstractFile(val jfile: java.io.File) extends internal.AbstractFileApi { + def path: String = jfile.getPath() + def canonicalPath: String = jfile.getCanonicalPath() +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala new file mode 100644 index 0000000000..a8120d220a --- /dev/null +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -0,0 +1,981 @@ +package scala.reflect +package runtime + +import scala.ref.WeakReference +import scala.collection.mutable.WeakHashMap + +import java.lang.{Class => jClass, Package => jPackage} +import java.lang.reflect.{ + Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, + Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray, + GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement } +import java.io.IOException +import internal.MissingRequirementError +import internal.pickling.ByteCodecs +import internal.ClassfileConstants._ +import internal.pickling.UnPickler +import collection.mutable.{ HashMap, ListBuffer } +import internal.Flags._ +//import scala.tools.nsc.util.ScalaClassLoader +//import scala.tools.nsc.util.ScalaClassLoader._ +import ReflectionUtils.{singletonInstance} +import language.existentials + +trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: SymbolTable => + + private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]() + + private def createMirror(owner: Symbol, cl: ClassLoader): Mirror = { + val jm = new JavaMirror(owner, cl) + mirrors(cl) = new WeakReference(jm) + jm.init() + jm + } + + override type Mirror = JavaMirror + + override lazy val rootMirror: Mirror = createMirror(NoSymbol, rootClassLoader) + + // overriden by ReflectGlobal + def rootClassLoader: ClassLoader = this.getClass.getClassLoader + + def init() = { + definitions.AnyValClass // force it. + + // establish root association to avoid cyclic dependency errors later + rootMirror.classToScala(classOf[java.lang.Object]).initialize + + // println("initializing definitions") + definitions.init() + } + + def runtimeMirror(cl: ClassLoader): Mirror = mirrors get cl match { + case Some(WeakReference(m)) => m + case _ => createMirror(rootMirror.RootClass, cl) + } + + /** The API of a mirror for a reflective universe */ + class JavaMirror(owner: Symbol, + /** Class loader that is a mastermind behind the reflexive mirror */ + val classLoader: ClassLoader + ) extends Roots(owner) with super.JavaMirror { wholemirror => + + val universe: self.type = self + + import definitions._ + + /** The lazy type for root. + */ + override lazy val rootLoader = new LazyType { + override def complete(sym: Symbol) = sym setInfo new LazyPackageType + } + +// ----------- Caching ------------------------------------------------------------------ + + // [Eugene++ to Martin] not weak? why? + private val classCache = new TwoWayCache[jClass[_], ClassSymbol] + private val packageCache = new TwoWayCache[Package, ModuleSymbol] + private val methodCache = new TwoWayCache[jMethod, MethodSymbol] + private val constructorCache = new TwoWayCache[jConstructor[_], MethodSymbol] + private val fieldCache = new TwoWayCache[jField, TermSymbol] + private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] + + def toScala[J: HasJavaClass, S](cache: TwoWayCache[J, S], key: J)(body: (JavaMirror, J) => S): S = + cache.toScala(key){ + val jclazz = implicitly[HasJavaClass[J]] getClazz key + body(mirrorDefining(jclazz), key) + } + + private implicit val classHasJavaClass: HasJavaClass[jClass[_]] = + new HasJavaClass(identity) + private implicit val methHasJavaClass: HasJavaClass[jMethod] + = new HasJavaClass(_.getDeclaringClass) + private implicit val fieldHasJavaClass: HasJavaClass[jField] = + new HasJavaClass(_.getDeclaringClass) + private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] = + new HasJavaClass(_.getDeclaringClass) + private implicit val tparamHasJavaClass: HasJavaClass[jTypeVariable[_ <: GenericDeclaration]] = + new HasJavaClass ( (tparam: jTypeVariable[_ <: GenericDeclaration]) => { + tparam.getGenericDeclaration match { + case jclazz: jClass[_] => jclazz + case jmeth: jMethod => jmeth.getDeclaringClass + case jconstr: jConstructor[_] => jconstr.getDeclaringClass + } + }) + +// ----------- Implementations of mirror operations and classes ------------------- + + def reflect(obj: Any): InstanceMirror = + new JavaInstanceMirror(obj.asInstanceOf[AnyRef]) + + def reflectClass(runtimeClass: RuntimeClass): ClassMirror = + new JavaClassMirror(classToScala(runtimeClass)) + + def reflectClass(fullName: String): ClassMirror = + reflectClass(java.lang.Class.forName(fullName)) + + def reflectModule(runtimeClass: RuntimeClass): ModuleMirror = + new JavaModuleMirror(classToScala(runtimeClass).companionModule.asModuleSymbol) + + def reflectModule(fullName: String): ModuleMirror = + reflectModule(java.lang.Class.forName(fullName)) + + def runtimeClass(tpe: Type): RuntimeClass = typeToJavaClass(tpe) + + def runtimeClass(cls: ClassSymbol): RuntimeClass = classToJava(cls) + + private class JavaInstanceMirror(obj: AnyRef) + extends InstanceMirror { + def instance = obj + def reflectClass = wholemirror.reflectClass(obj.getClass) + def reflectField(field: TermSymbol): FieldMirror = new JavaFieldMirror(obj, field) + def reflectMethod(method: MethodSymbol): MethodMirror = new JavaMethodMirror(obj, method) + } + + private class JavaFieldMirror(val receiver: AnyRef, val field: TermSymbol) + extends FieldMirror { + lazy val jfield = fieldToJava(field) + def get = jfield.get(receiver) + def set(value: Any) = jfield.set(receiver, value) + } + + private class JavaMethodMirror(val receiver: AnyRef, val method: MethodSymbol) + extends MethodMirror { + lazy val jmeth = methodToJava(method) + def apply(args: Any*): Any = + if (method.owner == ArrayClass) + method.name match { + case nme.length => jArray.getLength(receiver) + case nme.apply => jArray.get(receiver, args(0).asInstanceOf[Int]) + case nme.update => jArray.set(receiver, args(0).asInstanceOf[Int], args(1)) + case _ => throw new Error(s"unexpected array method $method") + } + else + jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + } + + private class JavaConstructorMirror(val method: MethodSymbol) + extends MethodMirror { + override val receiver = null + lazy val jconstr = constructorToJava(method) + def apply(args: Any*): Any = jconstr.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*) + } + + + private abstract class JavaTemplateMirror + extends TemplateMirror { + def erasure: ClassSymbol + lazy val runtimeClass = classToJava(erasure) + lazy val signature = typeToScala(runtimeClass) + } + + private class JavaClassMirror(val symbol: ClassSymbol) + extends JavaTemplateMirror with ClassMirror { + def erasure = symbol + def isStatic = false + def reflectConstructor(constructor: MethodSymbol) = new JavaConstructorMirror(constructor) + def companion: Option[ModuleMirror] = symbol.companionModule match { + case module: ModuleSymbol => Some(new JavaModuleMirror(module)) + case _ => None + } + } + + private class JavaModuleMirror(val symbol: ModuleSymbol) + extends JavaTemplateMirror with ModuleMirror { + def erasure = symbol.moduleClass.asClassSymbol + def isStatic = true + def instance = singletonInstance(classLoader, symbol.fullName) + def companion: Option[ClassMirror] = symbol.companionClass match { + case cls: ClassSymbol => Some(new JavaClassMirror(cls)) + case _ => None + } + } + +// -------------------- Java to Scala ----------------------------------- + + /** Does method `meth` erase to Java method `jmeth`? + * This is true if the Java method type is the same as the Scala method type after performing + * all Scala-specific transformations in InfoTransformers. (to be done) + */ + private def erasesTo(meth: Symbol, jmeth: jMethod): Boolean = { + val mtpe = transformedType(meth) + (mtpe.paramTypes map runtimeClass) == jmeth.getParameterTypes.toList && + runtimeClass(mtpe.resultType) == jmeth.getReturnType + } + + private def erasesTo(meth: Symbol, jconstr: jConstructor[_]): Boolean = { + val mtpe = transformedType(meth) + (mtpe.paramTypes map runtimeClass) == jconstr.getParameterTypes.toList && + runtimeClass(mtpe.resultType) == jconstr.getDeclaringClass + } + + def javaClass(path: String): jClass[_] = + Class.forName(path, true, classLoader) + + /** Does `path` correspond to a Java class with that fully qualified name in the current class loader? */ + def tryJavaClass(path: String): Option[jClass[_]] = + try { + Some(javaClass(path)) + } catch { + case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) => + None + } + + /** The mirror that corresponds to the classloader that original defined the given Java class */ + def mirrorDefining(jclazz: jClass[_]): JavaMirror = { + val cl = jclazz.getClassLoader + if (cl == this.classLoader) this else runtimeMirror(cl) + } + + private object unpickler extends UnPickler { + val global: self.type = self + } + + /** how connected???? + * Generate types for top-level Scala root class and root companion object + * from the pickled information stored in a corresponding Java class + * @param clazz The top-level Scala class for which info is unpickled + * @param module The top-level Scala companion object for which info is unpickled + * @param jclazz The Java class which contains the unpickled information in a + * ScalaSignature or ScalaLongSignature annotation. + */ + def unpickleClass(clazz: Symbol, module: Symbol, jclazz: jClass[_]): Unit = { + def markAbsent(tpe: Type) = setAllInfos(clazz, module, tpe) + def handleError(ex: Exception) = { + markAbsent(ErrorType) + if (settings.debug.value) ex.printStackTrace() + val msg = ex.getMessage() + MissingRequirementError.signal( + (if (msg eq null) "reflection error while loading " + clazz.name + else "error while loading " + clazz.name) + ", " + msg) + } + // don't use classOf[scala.reflect.ScalaSignature] here, because it will use getClass.getClassLoader, not mirror's classLoader + // don't use asInstanceOf either because of the same reason (lol, I cannot believe I fell for it) + // don't use structural types to simplify reflective invocations because of the same reason + def loadAnnotation(name: String): Option[java.lang.annotation.Annotation] = + tryJavaClass(name) flatMap { annotClass => + val anns = jclazz.getAnnotations + val result = anns find (_.annotationType == annotClass) + if (result.isEmpty && (anns exists (_.annotationType.getName == name))) + throw new ClassNotFoundException( + s"""Mirror classloader mismatch: $jclazz (loaded by ${ReflectionUtils.show(jclazz.getClassLoader)}) + |is unrelated to the mirror's classloader: (${ReflectionUtils.show(classLoader)})""".stripMargin) + result + } + def loadBytes[T: ClassTag](name: String): Option[T] = + loadAnnotation(name) map { ssig => + val bytesMethod = ssig.annotationType.getMethod("bytes") + bytesMethod.invoke(ssig).asInstanceOf[T] + } + + try { + markAbsent(NoType) + loadBytes[String]("scala.reflect.ScalaSignature") match { + case Some(ssig) => + info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}") + val bytes = ssig.getBytes + val len = ByteCodecs.decode(bytes) + unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) + case None => + loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match { + case Some(slsig) => + info(s"unpickling Scala $clazz and $module with long Scala signature") + val byteSegments = slsig map (_.getBytes) + val lens = byteSegments map ByteCodecs.decode + val bytes = Array.ofDim[Byte](lens.sum) + var len = 0 + for ((bs, l) <- byteSegments zip lens) { + bs.copyToArray(bytes, len, l) + len += l + } + unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName) + case None => + // class does not have a Scala signature; it's a Java class + info("translating reflection info for Java " + jclazz) //debug + initClassModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz)) + } + } + } catch { + case ex: MissingRequirementError => + handleError(ex) + case ex: IOException => + handleError(ex) + } + } + + /** + * A fresh Scala type parameter that corresponds to a Java type variable. + * The association between Scala type parameter and Java type variable is entered in the cache. + * @param jtvar The Java type variable + */ + private def createTypeParameter(jtvar: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = { + val tparam = sOwner(jtvar).newTypeParameter(newTypeName(jtvar.getName)) + .setInfo(new TypeParamCompleter(jtvar)) + tparamCache enter (jtvar, tparam) + tparam + } + + /** + * A completer that fills in the type of a Scala type parameter from the bounds of a Java type variable. + * @param jtvar The Java type variable + */ + private class TypeParamCompleter(jtvar: jTypeVariable[_ <: GenericDeclaration]) extends LazyType { + override def load(sym: Symbol) = complete(sym) + override def complete(sym: Symbol) = { + sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala map objToAny)) + } + } + + /** + * Copy all annotations of Java annotated element `jann` over to Scala symbol `sym`. + * Pre: `sym` is already initialized with a concrete type. + * Note: If `sym` is a method or constructor, its parameter annotations are copied as well. + */ + private def copyAnnotations(sym: Symbol, jann: AnnotatedElement) { + // to do: implement + } + + /** + * A completer that fills in the types of a Scala class and its companion object + * by copying corresponding type info from a Java class. This completer is used + * to reflect classes in Scala that do not have a Scala pickle info, be it + * because they are local classes or have been compiled from Java sources. + * @param clazz The Scala class for which info is copied + * @param module The Scala companion object for which info is copied + * @param jclazz The Java class + */ + private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType { + + /** used to avoid cycles while initializing classes */ + private var parentsLevel = 0 + private var pendingLoadActions: List[() => Unit] = Nil + + override def load(sym: Symbol): Unit = { + debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug + assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym) + val flags = toScalaClassFlags(jclazz.getModifiers) + clazz setFlag (flags | JAVA) + if (module != NoSymbol) { + module setFlag (flags & PRIVATE | JAVA) + module.moduleClass setFlag (flags & PRIVATE | JAVA) + } + + copyAnnotations(clazz, jclazz) + // to do: annotations to set also for module? + + clazz setInfo new LazyPolyType(jclazz.getTypeParameters.toList map createTypeParameter) + if (module != NoSymbol) { + module setInfo module.moduleClass.tpe + module.moduleClass setInfo new LazyPolyType(List()) + } + } + + override def complete(sym: Symbol): Unit = { + load(sym) + completeRest() + } + + def completeRest(): Unit = self.synchronized { + val tparams = clazz.rawInfo.typeParams + + val parents = try { + parentsLevel += 1 + val jsuperclazz = jclazz.getGenericSuperclass + val superclazz = if (jsuperclazz == null) AnyClass.tpe else typeToScala(jsuperclazz) + superclazz :: (jclazz.getGenericInterfaces.toList map typeToScala) + } finally { + parentsLevel -= 1 + } + clazz setInfo GenPolyType(tparams, new ClassInfoType(parents, newScope, clazz)) + if (module != NoSymbol) { + module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) + } + + def enter(sym: Symbol, mods: Int) = + (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym + + for (jinner <- jclazz.getDeclaredClasses) { + enter(jclassAsScala(jinner, clazz), jinner.getModifiers) + } + + pendingLoadActions = { () => + + for (jfield <- jclazz.getDeclaredFields) + enter(jfieldAsScala(jfield), jfield.getModifiers) + + for (jmeth <- jclazz.getDeclaredMethods) + enter(jmethodAsScala(jmeth), jmeth.getModifiers) + + for (jconstr <- jclazz.getConstructors) + enter(jconstrAsScala(jconstr), jconstr.getModifiers) + + } :: pendingLoadActions + + if (parentsLevel == 0) { + while (!pendingLoadActions.isEmpty) { + val item = pendingLoadActions.head + pendingLoadActions = pendingLoadActions.tail + item() + } + } + } + + class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType { + override def complete(sym: Symbol) { + completeRest() + } + } + } + + /** + * If Java modifiers `mods` contain STATIC, return the module class + * of the companion module of `clazz`, otherwise the class `clazz` itself. + */ + private def followStatic(clazz: Symbol, mods: Int) = + if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz + + implicit class RichClass(jclazz: jClass[_]) { + // [Eugene++] `jclazz.isLocalClass` doesn't work because of problems with `getSimpleName` + // java.lang.Error: sOwner(class Test$A$1) has failed + // Caused by: java.lang.InternalError: Malformed class name + // at java.lang.Class.getSimpleName(Class.java:1133) + // at java.lang.Class.isAnonymousClass(Class.java:1188) + // at java.lang.Class.isLocalClass(Class.java:1199) + // (see t5256c.scala for more details) + // hence we have to approximate by removing the `isAnonymousClass` check +// def isLocalClass0: Boolean = jclazz.isLocalClass + def isLocalClass0: Boolean = jclazz.getEnclosingMethod != null || jclazz.getEnclosingConstructor != null + } + + // [Eugene++] overflow from Paul's changes made concurrently with reflection refactoring + // https://github.com/scala/scala/commit/90d2bee45b25844f809f8c5300aefcb1bfe9e336 + // + // /** Methods which need to be wrapped because they either are getSimpleName + // * or call getSimpleName: + // * + // * public String getSimpleName() + // * public boolean isAnonymousClass() + // * public boolean isLocalClass() + // * public boolean isMemberClass() + // * public String getCanonicalName() + // * + // * TODO - find all such calls and wrap them. + // * TODO - create mechanism to avoid the recurrence of unwrapped calls. + // */ + // private def wrapClassCheck[T](alt: T)(body: => T): T = + // try body catch { case x: InternalError if x.getMessage == "Malformed class name" => alt } + + // private def wrapIsLocalClass(clazz: jClass[_]): Boolean = + // wrapClassCheck(false)(clazz.isLocalClass) + + // private def wrapGetSimpleName(clazz: jClass[_]): String = + // wrapClassCheck("")(clazz.getSimpleName) + + /** + * The Scala owner of the Scala class corresponding to the Java class `jclazz` + */ + private def sOwner(jclazz: jClass[_]): Symbol = + if (jclazz.isMemberClass) { + val jEnclosingClass = jclazz.getEnclosingClass + val sEnclosingClass = classToScala(jEnclosingClass) + followStatic(sEnclosingClass, jclazz.getModifiers) + } else if (jclazz.isLocalClass0) { + val jEnclosingMethod = jclazz.getEnclosingMethod + if (jEnclosingMethod != null) { + methodToScala(jEnclosingMethod) + } else { + val jEnclosingConstructor = jclazz.getEnclosingConstructor + constructorToScala(jEnclosingConstructor) + } + } else if (jclazz.isPrimitive || jclazz.isArray) { + ScalaPackageClass + } else if (jclazz.getPackage != null) { + val jPackage = jclazz.getPackage + packageToScala(jPackage).moduleClass + } else { + // @eb: a weird classloader might return a null package for something with a non-empty package name + // for example, http://groups.google.com/group/scala-internals/browse_thread/thread/7be09ff8f67a1e5c + // in that case we could invoke packageNameToScala(jPackageName) and, probably, be okay + // however, I think, it's better to blow up, since weirdness of the class loader might bite us elsewhere + // [martin] I think it's better to be forgiving here. Restoring packageNameToScala. + val jPackageName = jclazz.getName take jclazz.getName.lastIndexOf('.') + packageNameToScala(jPackageName).moduleClass + } + + /** + * The Scala owner of the Scala symbol corresponding to the Java member `jmember` + */ + private def sOwner(jmember: jMember): Symbol = { + followStatic(classToScala(jmember.getDeclaringClass), jmember.getModifiers) + } + + /** + * The Scala owner of the Scala type parameter corresponding to the Java type variable `jtvar` + */ + private def sOwner(jtvar: jTypeVariable[_ <: GenericDeclaration]): Symbol = + genericDeclarationToScala(jtvar.getGenericDeclaration) + + /** + * Find declarations or definition in class `clazz` that maps to a Java + * entity with name `jname`. Because of name-mangling, this is more difficult + * than a simple name-based lookup via `decl`. If `decl` fails, members + * that start with the given name are searched instead. + */ + private def lookup(clazz: Symbol, jname: String): Symbol = { + def approximateMatch(sym: Symbol, jstr: String): Boolean = + (sym.name.toString == jstr) || + sym.isPrivate && nme.expandedName(sym.name.toTermName, sym.owner).toString == jstr + + clazz.info.decl(newTermName(jname)) orElse { + (clazz.info.decls.iterator filter (approximateMatch(_, jname))).toList match { + case List() => NoSymbol + case List(sym) => sym + case alts => clazz.newOverloaded(alts.head.tpe.prefix, alts) + } + } + } + + /** + * The Scala method corresponding to given Java method. + * @param jmeth The Java method + * @return A Scala method object that corresponds to `jmeth`. + */ + def methodToScala(jmeth: jMethod): MethodSymbol = + toScala(methodCache, jmeth)(_ methodToScala1 _) + + private def methodToScala1(jmeth: jMethod): MethodSymbol = { + val jOwner = jmeth.getDeclaringClass + val preOwner = classToScala(jOwner) + val owner = followStatic(preOwner, jmeth.getModifiers) + (lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)) + .asMethodSymbol + } + + /** + * The Scala constructor corresponding to given Java constructor. + * @param jconstr The Java constructor + * @return A Scala method object that corresponds to `jconstr`. + */ + def constructorToScala(jconstr: jConstructor[_]): MethodSymbol = + toScala(constructorCache, jconstr)(_ constructorToScala1 _) + + private def constructorToScala1(jconstr: jConstructor[_]): MethodSymbol = { + val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers) + (lookup(owner, jconstr.getName) suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr)) + .asMethodSymbol + } + + /** + * The Scala field corresponding to given Java field. + * @param jfield The Java field + * @return A Scala field object that corresponds to `jfield`. + * // ??? should we return the getter instead? + */ + def fieldToScala(jfield: jField): TermSymbol = + toScala(fieldCache, jfield)(_ fieldToScala1 _) + + private def fieldToScala1(jfield: jField): TermSymbol = { + val owner = followStatic(classToScala(jfield.getDeclaringClass), jfield.getModifiers) + (lookup(owner, jfield.getName) suchThat (!_.isMethod) orElse jfieldAsScala(jfield)) + .asTermSymbol + } + + /** + * The Scala package corresponding to given Java package + */ + def packageToScala(jpkg: jPackage): ModuleSymbol = packageCache.toScala(jpkg) { + makeScalaPackage(jpkg.getName) + } + + /** + * The Scala package with given fully qualified name. + */ + def packageNameToScala(fullname: String): ModuleSymbol = { + if (fullname == "") EmptyPackage + else { + val jpkg = jPackage.getPackage(fullname) + if (jpkg != null) packageToScala(jpkg) else makeScalaPackage(fullname) + } + } + + /** + * The Scala package with given fully qualified name. Unlike `packageNameToScala`, + * this one bypasses the cache. + */ + private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = { + val split = fullname lastIndexOf '.' + val ownerModule: ModuleSymbol = + if (split > 0) packageNameToScala(fullname take split) else this.RootPackage + val owner = ownerModule.moduleClass + val name = newTermName(fullname drop (split + 1)) + val opkg = owner.info decl name + if (opkg.isPackage) + opkg.asModuleSymbol + else if (opkg == NoSymbol) { + val pkg = owner.newPackage(name) + pkg.moduleClass setInfo new LazyPackageType + pkg setInfoAndEnter pkg.moduleClass.tpe + info("made Scala "+pkg) + pkg + } else + throw new ReflectError(opkg+" is not a package") + } + + private def scalaSimpleName(jclazz: jClass[_]): TypeName = { + val owner = sOwner(jclazz) + val enclosingClass = jclazz.getEnclosingClass + var prefix = if (enclosingClass != null) enclosingClass.getName else "" + val isObject = owner.isModuleClass && !owner.isPackageClass + if (isObject && !prefix.endsWith(nme.MODULE_SUFFIX_STRING)) prefix += nme.MODULE_SUFFIX_STRING + assert(jclazz.getName.startsWith(prefix)) + var name = jclazz.getName.substring(prefix.length) + name = name.substring(name.lastIndexOf(".") + 1) + newTypeName(name) + } + + /** + * The Scala class that corresponds to a given Java class. + * @param jclazz The Java class + * @return A Scala class symbol that reflects all elements of the Java class, + * in the form they appear in the Scala pickling info, or, if that is + * not available, wrapped from the Java reflection info. + */ + def classToScala(jclazz: jClass[_]): ClassSymbol = + toScala(classCache, jclazz)(_ classToScala1 _) + + private def classToScala1(jclazz: jClass[_]): ClassSymbol = { + val jname = newTypeName(jclazz.getName) + if (jname == fulltpnme.RuntimeNothing) NothingClass + else if (jname == fulltpnme.RuntimeNull) NullClass + else { + val owner = sOwner(jclazz) + val simpleName = scalaSimpleName(jclazz) + + def lookupClass = { + def coreLookup(name: Name): Symbol = + owner.info.decl(name) orElse { + if (name.startsWith(nme.NAME_JOIN_STRING)) coreLookup(name drop 1) else NoSymbol + } + if (nme.isModuleName(simpleName)) + coreLookup(nme.stripModuleSuffix(simpleName).toTermName) map (_.moduleClass) + else + coreLookup(simpleName) + } + + val cls = + if (jclazz.isMemberClass && !nme.isImplClassName(jname)) + lookupClass + else if (jclazz.isLocalClass0 || isInvalidClassName(jname)) + // local classes and implementation classes not preserved by unpickling - treat as Java + jclassAsScala(jclazz) + else if (jclazz.isArray) + ArrayClass + else + javaTypeToValueClass(jclazz) orElse lookupClass + + assert (cls.isType, + s"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"} + | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""".stripMargin) + + cls.asClassSymbol + } + } + + /** + * The Scala type parameter that corresponds to a given Java type parameter. + * @param jparam The Java type parameter + * @return A Scala type parameter symbol that has the same owner and name as the Java type parameter + */ + def typeParamToScala(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = + toScala(tparamCache, jparam)(_ typeParamToScala1 _) + + private def typeParamToScala1(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = { + val owner = genericDeclarationToScala(jparam.getGenericDeclaration) + owner.info match { + case PolyType(tparams, _) => tparams.find(_.name.toString == jparam.getName).get.asTypeSymbol + } + } + + /** + * The Scala symbol that corresponds to a given Java generic declaration (class, method, or constructor) + */ + def genericDeclarationToScala(jdecl: GenericDeclaration): Symbol = jdecl match { + case jclazz: jClass[_] => classToScala(jclazz) + case jmeth: jMethod => methodToScala(jmeth) + case jconstr: jConstructor[_] => constructorToScala(jconstr) + } + + /** + * Given some Java type arguments, a corresponding list of Scala types, plus potentially + * some existentially bound type variables that represent wildcard arguments. + */ + private def targsToScala(owner: Symbol, args: List[jType]): (List[Type], List[TypeSymbol]) = { + val tparams = new ListBuffer[TypeSymbol] + def targToScala(arg: jType): Type = arg match { + case jwild: WildcardType => + val tparam = owner.newExistential(newTypeName("T$" + tparams.length)) + .setInfo(TypeBounds( + lub(jwild.getLowerBounds.toList map typeToScala), + glb(jwild.getUpperBounds.toList map typeToScala map objToAny))) + tparams += tparam + typeRef(NoPrefix, tparam, List()) + case _ => + typeToScala(arg) + } + (args map targToScala, tparams.toList) + } + + /** + * The Scala type that corresponds to given Java type + */ + def typeToScala(jtpe: jType): Type = jtpe match { + case jclazz: jClass[_] => + if (jclazz.isArray) + arrayType(typeToScala(jclazz.getComponentType)) + else { + val clazz = classToScala(jclazz) + rawToExistential(typeRef(clazz.owner.thisType, clazz, List())) + } + case japplied: ParameterizedType => + val (pre, sym) = typeToScala(japplied.getRawType) match { + case ExistentialType(tparams, TypeRef(pre, sym, _)) => (pre, sym) + case TypeRef(pre, sym, _) => (pre, sym) + } + val args0 = japplied.getActualTypeArguments + val (args, bounds) = targsToScala(pre.typeSymbol, args0.toList) + ExistentialType(bounds, typeRef(pre, sym, args)) + case jarr: GenericArrayType => + arrayType(typeToScala(jarr.getGenericComponentType)) + case jtvar: jTypeVariable[_] => + val tparam = typeParamToScala(jtvar) + typeRef(NoPrefix, tparam, List()) + } + + /** + * The Scala class that corresponds to given Java class without taking + * Scala pickling info into account. + * @param jclazz The Java class + * @return A Scala class symbol that wraps all reflection info of `jclazz` + */ + private def jclassAsScala(jclazz: jClass[_]): Symbol = jclassAsScala(jclazz, sOwner(jclazz)) + + private def jclassAsScala(jclazz: jClass[_], owner: Symbol): ClassSymbol = { + val name = scalaSimpleName(jclazz) + val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, jclazz) + val (clazz, module) = createClassModule(owner, name, completer) + classCache enter (jclazz, clazz) + clazz + } + + /** + * The Scala field that corresponds to given Java field without taking + * Scala pickling info into account. + * @param jfield The Java field + * @return A Scala value symbol that wraps all reflection info of `jfield` + */ + private def jfieldAsScala(jfield: jField): TermSymbol = + toScala(fieldCache, jfield)(_ jfieldAsScala1 _) + + private def jfieldAsScala1(jfield: jField): TermSymbol = { + val field = sOwner(jfield) + .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers)) + .setInfo(typeToScala(jfield.getGenericType)) + fieldCache enter (jfield, field) + copyAnnotations(field, jfield) + field + } + + private def setMethType(meth: Symbol, tparams: List[Symbol], paramtpes: List[Type], restpe: Type) = { + meth setInfo GenPolyType(tparams, MethodType(meth.owner.newSyntheticValueParams(paramtpes map objToAny), restpe)) + } + + /** + * The Scala method that corresponds to given Java method without taking + * Scala pickling info into account. + * @param jmeth The Java method + * @return A Scala method symbol that wraps all reflection info of `jmethod` + */ + private def jmethodAsScala(jmeth: jMethod): MethodSymbol = + toScala(methodCache, jmeth)(_ jmethodAsScala1 _) + + private def jmethodAsScala1(jmeth: jMethod): MethodSymbol = { + val clazz = sOwner(jmeth) + val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, toScalaMethodFlags(jmeth.getModifiers)) + methodCache enter (jmeth, meth) + val tparams = jmeth.getTypeParameters.toList map createTypeParameter + val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala + val resulttpe = typeToScala(jmeth.getGenericReturnType) + setMethType(meth, tparams, paramtpes, resulttpe) + copyAnnotations(meth, jmeth) + if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info)) + meth + } + + /** + * The Scala constructor that corresponds to given Java constructor without taking + * Scala pickling info into account. + * @param jconstr The Java constructor + * @return A Scala constructor symbol that wraps all reflection info of `jconstr` + */ + private def jconstrAsScala(jconstr: jConstructor[_]): MethodSymbol = + toScala(constructorCache, jconstr)(_ jconstrAsScala1 _) + + private def jconstrAsScala1(jconstr: jConstructor[_]): MethodSymbol = { + // [Martin] Note: I know there's a lot of duplication wrt jmethodAsScala, but don't think it's worth it to factor this out. + val clazz = sOwner(jconstr) + val constr = clazz.newConstructor(NoPosition, toScalaMethodFlags(jconstr.getModifiers)) + constructorCache enter (jconstr, constr) + val tparams = jconstr.getTypeParameters.toList map createTypeParameter + val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala + setMethType(constr, tparams, paramtpes, clazz.tpe) + constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) + copyAnnotations(constr, jconstr) + constr + } + +// -------------------- Scala to Java ----------------------------------- + + /** Optionally, the Java package corresponding to a given Scala package, or None if no such Java package exists. + * @param pkg The Scala package + */ + def packageToJavaOption(pkg: ModuleSymbol): Option[jPackage] = packageCache.toJavaOption(pkg) { + Option(jPackage.getPackage(pkg.fullName.toString)) + } + + /** The Java class corresponding to given Scala class. + * Note: This only works for + * - top-level classes + * - Scala classes that were generated via jclassToScala + * - classes that have a class owner that has a corresponding Java class + * @throws A `ClassNotFoundException` for all Scala classes not in one of these categories. + */ + @throws(classOf[ClassNotFoundException]) + def classToJava(clazz: ClassSymbol): jClass[_] = classCache.toJava(clazz) { + def noClass = throw new ClassNotFoundException("no Java class corresponding to "+clazz+" found") + //println("classToJava "+clazz+" "+clazz.owner+" "+clazz.owner.isPackageClass)//debug + if (clazz.isPrimitiveValueClass) + valueClassToJavaType(clazz) + else if (clazz == ArrayClass) + noClass + else if (clazz.owner.isPackageClass) + javaClass(clazz.javaClassName) + else if (clazz.owner.isClass) + classToJava(clazz.owner.asClassSymbol) + .getDeclaredClasses + .find(_.getSimpleName == clazz.name.toString) + .getOrElse(noClass) + else + noClass + } + + private def expandedName(sym: Symbol): String = + if (sym.isPrivate) nme.expandedName(sym.name.toTermName, sym.owner).toString + else sym.name.toString + + /** The Java field corresponding to a given Scala field. + * @param meth The Scala field. + */ + def fieldToJava(fld: TermSymbol): jField = fieldCache.toJava(fld) { + val jclazz = classToJava(fld.owner.asClassSymbol) + try jclazz getDeclaredField fld.name.toString + catch { + case ex: NoSuchFieldException => jclazz getDeclaredField expandedName(fld) + } + } + + /** The Java method corresponding to a given Scala method. + * @param meth The Scala method + */ + def methodToJava(meth: MethodSymbol): jMethod = methodCache.toJava(meth) { + val jclazz = classToJava(meth.owner.asClassSymbol) + val paramClasses = transformedType(meth).paramTypes map typeToJavaClass + try jclazz getDeclaredMethod (meth.name.toString, paramClasses: _*) + catch { + case ex: NoSuchMethodException => + jclazz getDeclaredMethod (expandedName(meth), paramClasses: _*) + } + } + + /** The Java constructor corresponding to a given Scala constructor. + * @param constr The Scala constructor + */ + def constructorToJava(constr: MethodSymbol): jConstructor[_] = constructorCache.toJava(constr) { + val jclazz = classToJava(constr.owner.asClassSymbol) + val paramClasses = transformedType(constr).paramTypes map typeToJavaClass + jclazz getConstructor (paramClasses: _*) + } + + private def jArrayClass(elemClazz: jClass[_]): jClass[_] = { + jArray.newInstance(elemClazz, 0).getClass + } + + /** The Java class that corresponds to given Scala type. + * Pre: Scala type is already transformed to Java level. + */ + def typeToJavaClass(tpe: Type): jClass[_] = tpe match { + case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) + case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) + case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClassSymbol) + case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") + } + } + + /** Assert that packages have package scopes */ + override def validateClassInfo(tp: ClassInfoType) { + assert(!tp.typeSymbol.isPackageClass || tp.decls.isInstanceOf[PackageScope]) + } + + override def newPackageScope(pkgClass: Symbol) = new PackageScope(pkgClass) + + override def scopeTransform(owner: Symbol)(op: => Scope): Scope = + if (owner.isPackageClass) owner.info.decls else op + + private lazy val rootToLoader = new WeakHashMap[Symbol, ClassLoader] + + override def mirrorThatLoaded(sym: Symbol): Mirror = { + val root = sym.enclosingRootClass + def findLoader = { + val loaders = (mirrors collect { case (cl, ref) if ref.get.get.RootClass == root => cl }) + assert(loaders.nonEmpty, sym) + loaders.head + } + mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get + } + + private def byName(sym: Symbol): (Name, Symbol) = sym.name -> sym + + private lazy val phantomTypes: Map[Name, Symbol] = + Map(byName(definitions.AnyRefClass)) ++ (definitions.isPhantomClass map byName) + + /** 1. If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package + * <owner>.<name>, otherwise return NoSymbol. + * Exception: If owner is root and a java class with given name exists, create symbol in empty package instead + * 2. If `owner` is the scala package and `name` designates a phantom class, return + * the corresponding class symbol and enter it into this mirror's ScalaPackage. + */ + override def missingHook(owner: Symbol, name: Name): Symbol = { + if (owner.hasPackageFlag) { + val mirror = mirrorThatLoaded(owner) + // [Eugene++] this makes toolbox tests pass, but it's a mere workaround for SI-5865 +// assert((owner.info decl name) == NoSymbol, s"already exists: $owner . $name") + if (owner.isRootSymbol && mirror.tryJavaClass(name.toString).isDefined) + return mirror.EmptyPackageClass.info decl name + if (name.isTermName && !owner.isEmptyPackageClass) + return mirror.makeScalaPackage( + if (owner.isRootSymbol) name.toString else owner.fullName+"."+name) + if (owner.name.toTermName == nme.scala_ && owner.owner.isRoot) + phantomTypes get name match { + case Some(tsym) => + owner.info.decls enter tsym + return tsym + case None => + } + } + info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) + super.missingHook(owner, name) + } +} + +class ReflectError(msg: String) extends java.lang.Error(msg) + +class HasJavaClass[J](val getClazz: J => java.lang.Class[_]) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala new file mode 100644 index 0000000000..d4a83b960d --- /dev/null +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -0,0 +1,33 @@ +package scala.reflect +package runtime + +import internal.{SomePhase, NoPhase, Phase, TreeGen} + +/** The universe for standard runtime reflection from Java. + * This type implements all abstract term members in internal.SymbolTable. + */ +class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.SymbolTable { self => + + type AbstractFileType = AbstractFile + + def picklerPhase = SomePhase + + type TreeGen = internal.TreeGen + + override type Position = scala.reflect.internal.util.Position + + override val gen = new TreeGen { val global: self.type = self } + + lazy val settings = new Settings + def forInteractive = false + def forScaladoc = false + + def log(msg: => AnyRef): Unit = println(" [] "+msg) + + type TreeCopier = TreeCopierOps + def newStrictTreeCopier: TreeCopier = new StrictTreeCopier + def newLazyTreeCopier: TreeCopier = new LazyTreeCopier + + init() +} + diff --git a/src/reflect/scala/reflect/runtime/ReflectSetup.scala b/src/reflect/scala/reflect/runtime/ReflectSetup.scala new file mode 100644 index 0000000000..6e28fc8520 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/ReflectSetup.scala @@ -0,0 +1,12 @@ +package scala.reflect +package runtime + +import internal.{SomePhase, NoPhase, Phase, TreeGen} + +/** A helper trait to initialize things that need to be set before JavaMirrors and other + * reflect specific traits are initialized */ +private[runtime] trait ReflectSetup extends internal.SymbolTable { + override val phaseWithId: Array[Phase] = Array(NoPhase, SomePhase) + override val currentRunId = 1 // fake a run id so that it is different from NoRunId + phase = SomePhase // set to a phase different from NoPhase +} diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala new file mode 100644 index 0000000000..4e82fe8ad2 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -0,0 +1,80 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.runtime + +import java.lang.{Class => jClass} +import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException } + +/** A few java-reflection oriented utility functions useful during reflection bootstrapping. + */ +object ReflectionUtils { + // Unwraps some chained exceptions which arise during reflective calls. + def unwrapThrowable(x: Throwable): Throwable = x match { + case _: InvocationTargetException | // thrown by reflectively invoked method or constructor + _: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor) + _: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception + _: ClassNotFoundException | // no definition for a class instantiated by name + _: NoClassDefFoundError // the definition existed when the executing class was compiled, but can no longer be found + if x.getCause != null => + unwrapThrowable(x.getCause) + case _ => x + } + // Transforms an exception handler into one which will only receive the unwrapped + // exceptions (for the values of wrap covered in unwrapThrowable.) + def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] = { + case ex if pf isDefinedAt unwrapThrowable(ex) => pf(unwrapThrowable(ex)) + } + + private def systemProperties: Iterator[(String, String)] = { + import scala.collection.JavaConverters._ + System.getProperties.asScala.iterator + } + + private def inferBootClasspath: String = ( + systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" + ) + + def show(cl: ClassLoader) = { + def inferClasspath(cl: ClassLoader): String = cl match { + case cl: java.net.URLClassLoader => + "[" + (cl.getURLs mkString ",") + "]" + case cl if cl != null && cl.getClass.getName == "scala.tools.nsc.interpreter.AbstractFileClassLoader" => + "[" + cl.asInstanceOf[{val root: scala.reflect.internal.AbstractFileApi}].root + "] and " + inferClasspath(cl.getParent) + case null => + inferBootClasspath + case _ => + "<unknown>" + } + cl match { + case cl if cl != null => + "%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl)) + case null => + "primordial classloader with boot classpath [%s]".format(inferClasspath(cl)) + } + } + + def singletonInstance(cl: ClassLoader, className: String): AnyRef = { + val name = if (className endsWith "$") className else className + "$" + val clazz = java.lang.Class.forName(name, true, cl) + val singleton = clazz getField "MODULE$" get null + singleton + } + + // Retrieves the MODULE$ field for the given class name. + def singletonInstanceOpt(cl: ClassLoader, className: String): Option[AnyRef] = + try Some(singletonInstance(cl, className)) + catch { case _: ClassNotFoundException => None } + + def invokeFactory(cl: ClassLoader, className: String, methodName: String, args: AnyRef*): AnyRef = { + val singleton = singletonInstance(cl, className) + val method = singleton.getClass.getMethod(methodName, classOf[ClassLoader]) + method.invoke(singleton, args: _*) + } + + def invokeFactoryOpt(cl: ClassLoader, className: String, methodName: String, args: AnyRef*): Option[AnyRef] = + try Some(invokeFactory(cl, className, methodName, args: _*)) + catch { case _: ClassNotFoundException => None } +} diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala new file mode 100644 index 0000000000..b247797c6c --- /dev/null +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -0,0 +1,39 @@ +package scala.reflect +package runtime + +/** The Settings class for runtime reflection. + * This should be refined, so that settings are settable via command + * line options or properties. + */ +class Settings extends internal.settings.MutableSettings { + + trait Setting extends SettingValue { } + + class BooleanSetting(x: Boolean) extends Setting { + type T = Boolean + protected var v: Boolean = x + override def value: Boolean = v + } + + class IntSetting(x: Int) extends Setting { + type T = Int + protected var v: Int = x + override def value: Int = v + } + + val overrideObjects = new BooleanSetting(false) + val debug = new BooleanSetting(false) + val Ynotnull = new BooleanSetting(false) + val explaintypes = new BooleanSetting(false) + val verbose = new BooleanSetting(false) + val uniqid = new BooleanSetting(false) + val Yshowsymkinds = new BooleanSetting(false) + val Xprintpos = new BooleanSetting(false) + val printtypes = new BooleanSetting(false) + val Yrecursion = new IntSetting(0) + val maxClassfileName = new IntSetting(255) + val Xexperimental = new BooleanSetting(false) + val deepCloning = new BooleanSetting (false) + val XoldPatmat = new BooleanSetting(false) + val XnoPatmatAnalysis = new BooleanSetting(false) +} diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala new file mode 100644 index 0000000000..c1cd5d2911 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -0,0 +1,152 @@ +package scala.reflect +package runtime + +import internal.Flags +import java.lang.{Class => jClass, Package => jPackage} +import collection.mutable + +trait SymbolLoaders { self: SymbolTable => + + /** The standard completer for top-level classes + * @param clazz The top-level class + * @param module The companion object of `clazz` + * Calling `complete` on this type will assign the infos of `clazz` and `module` + * by unpickling information from the corresponding Java class. If no Java class + * is found, a package is created instead. + */ + class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader { +// def makePackage() { +// println("wrong guess; making package "+clazz) +// val ptpe = newPackageType(module.moduleClass) +// for (sym <- List(clazz, module, module.moduleClass)) { +// sym setFlag Flags.PACKAGE +// sym setInfo ptpe +// } +// } + + override def complete(sym: Symbol) = { + debugInfo("completing "+sym+"/"+clazz.fullName) + assert(sym == clazz || sym == module || sym == module.moduleClass) +// try { + atPhaseNotLaterThan(picklerPhase) { + val loadingMirror = mirrorThatLoaded(sym) + val javaClass = loadingMirror.javaClass(clazz.javaClassName) + loadingMirror.unpickleClass(clazz, module, javaClass) +// } catch { +// case ex: ClassNotFoundException => makePackage() +// case ex: NoClassDefFoundError => makePackage() + // Note: We catch NoClassDefFoundError because there are situations + // where a package and a class have the same name except for capitalization. + // It seems in this case the class is loaded even if capitalization differs + // but then a NoClassDefFound error is issued with a ("wrong name: ...") + // reason. (I guess this is a concession to Windows). + // The present behavior is a bit too forgiving, in that it masks + // all class load errors, not just wrong name errors. We should try + // to be more discriminating. To get on the right track simply delete + // the clause above and load a collection class such as collection.Iterable. + // You'll see an error that class `parallel` has the wrong name. +// } + } + } + override def load(sym: Symbol) = complete(sym) + } + + /** Create a class and a companion object, enter in enclosing scope, + * and initialize with a lazy type completer. + * @param owner The owner of the newly created class and object + * @param name The simple name of the newly created class + * @param completer The completer to be used to set the info of the class and the module + */ + protected def createClassModule(owner: Symbol, name: TypeName, completer: (Symbol, Symbol) => LazyType) = { + assert(!(name.toString endsWith "[]"), name) + val clazz = owner.newClass(name) + val module = owner.newModule(name.toTermName) + // [Eugene++] am I doing this right? + // todo: drop condition, see what goes wrong + // [Eugene++ to Martin] test/files/run/t5256g and test/files/run/t5256h will crash + // reflection meeting verdict: need to enter the symbols into the first symbol in the owner chain that has a non-empty scope + if (owner.info.decls != EmptyScope) { + owner.info.decls enter clazz + owner.info.decls enter module + } + initClassModule(clazz, module, completer(clazz, module)) + (clazz, module) + } + + protected def setAllInfos(clazz: Symbol, module: Symbol, info: Type) = { + List(clazz, module, module.moduleClass) foreach (_ setInfo info) + } + + protected def initClassModule(clazz: Symbol, module: Symbol, completer: LazyType) = + setAllInfos(clazz, module, completer) + + /** The type completer for packages. + */ + class LazyPackageType extends LazyType { + override def complete(sym: Symbol) { + assert(sym.isPackageClass) + sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym) + // override def safeToString = pkgClass.toString + openPackageModule(sym) + } + } + + /** Is the given name valid for a top-level class? We exclude names with embedded $-signs, because + * these are nested classes or anonymous classes, + */ + def isInvalidClassName(name: Name) = { + val dp = name pos '$' + 0 < dp && dp < (name.length - 1) + } + + class PackageScope(pkgClass: Symbol) extends Scope() with SynchronizedScope { + assert(pkgClass.isType) + private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. + override def lookupEntry(name: Name): ScopeEntry = { + val e = super.lookupEntry(name) + if (e != null) + e + else if (isInvalidClassName(name) || (negatives contains name)) + null + else { + val path = + if (pkgClass.isEmptyPackageClass) name.toString + else pkgClass.fullName + "." + name + val currentMirror = mirrorThatLoaded(pkgClass) + currentMirror.tryJavaClass(path) match { + case Some(cls) => + val loadingMirror = currentMirror.mirrorDefining(cls) + val (clazz, module) = + if (loadingMirror eq currentMirror) { + createClassModule(pkgClass, name.toTypeName, new TopClassCompleter(_, _)) + } else { + val origOwner = loadingMirror.packageNameToScala(pkgClass.fullName) + val clazz = origOwner.info decl name.toTypeName + val module = origOwner.info decl name.toTermName + assert(clazz != NoSymbol) + assert(module != NoSymbol) + pkgClass.info.decls enter clazz + pkgClass.info.decls enter module + (clazz, module) + } + debugInfo(s"created $module/${module.moduleClass} in $pkgClass") + lookupEntry(name) + case none => + debugInfo("*** not found : "+path) + negatives += name + null + } + } + } + } + + /** Assert that packages have package scopes */ + override def validateClassInfo(tp: ClassInfoType) { + assert(!tp.typeSymbol.isPackageClass || tp.decls.isInstanceOf[PackageScope]) + } + + override def newPackageScope(pkgClass: Symbol) = new PackageScope(pkgClass) + + override def scopeTransform(owner: Symbol)(op: => Scope): Scope = + if (owner.isPackageClass) owner.info.decls else op +} diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala new file mode 100644 index 0000000000..c90665508b --- /dev/null +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -0,0 +1,17 @@ +package scala.reflect +package runtime + +/** + * This symbol table trait fills in the definitions so that class information is obtained by refection. + * It can be used either from a reflexive universe (class scala.reflect.runtime.JavaUniverse), or else from + * a runtime compiler that uses reflection to get a class information (class scala.tools.nsc.ReflectGlobal) + */ +trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { + + def info(msg: => String) = + if (settings.verbose.value) println("[reflect-compiler] "+msg) + + def debugInfo(msg: => String) = + if (settings.debug.value) info(msg) + +} diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala new file mode 100644 index 0000000000..907c0dd369 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -0,0 +1,51 @@ +package scala.reflect +package runtime + +trait SynchronizedOps extends internal.SymbolTable + with SynchronizedSymbols + with SynchronizedTypes { self: SymbolTable => + +// Names + + private lazy val nameLock = new Object + + override def newTermName(s: String): TermName = nameLock.synchronized { super.newTermName(s) } + override def newTypeName(s: String): TypeName = nameLock.synchronized { super.newTypeName(s) } + +// BaseTypeSeqs + + override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + + trait SynchronizedBaseTypeSeq extends BaseTypeSeq { + override def apply(i: Int): Type = synchronized { super.apply(i) } + override def rawElem(i: Int) = synchronized { super.rawElem(i) } + override def typeSymbol(i: Int): Symbol = synchronized { super.typeSymbol(i) } + override def toList: List[Type] = synchronized { super.toList } + override def copy(head: Type, offset: Int): BaseTypeSeq = synchronized { super.copy(head, offset) } + override def map(f: Type => Type): BaseTypeSeq = synchronized { super.map(f) } + override def exists(p: Type => Boolean): Boolean = synchronized { super.exists(p) } + override lazy val maxDepth = synchronized { maxDepthOfElems } + override def toString = synchronized { super.toString } + + override def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + } + +// Scopes + + override def newScope = new Scope() with SynchronizedScope + override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope + + trait SynchronizedScope extends Scope { + override def isEmpty: Boolean = synchronized { super.isEmpty } + override def size: Int = synchronized { super.size } + override def enter[T <: Symbol](sym: T): T = synchronized { super.enter(sym) } + override def rehash(sym: Symbol, newname: Name) = synchronized { super.rehash(sym, newname) } + override def unlink(e: ScopeEntry) = synchronized { super.unlink(e) } + override def unlink(sym: Symbol) = synchronized { super.unlink(sym) } + override def lookupAll(name: Name) = synchronized { super.lookupAll(name) } + override def lookupEntry(name: Name) = synchronized { super.lookupEntry(name) } + override def lookupNextEntry(entry: ScopeEntry) = synchronized { super.lookupNextEntry(entry) } + override def toList: List[Symbol] = synchronized { super.toList } + } +} diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala new file mode 100644 index 0000000000..3b28ddf42c --- /dev/null +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -0,0 +1,140 @@ +package scala.reflect +package runtime + +import internal.Flags.DEFERRED + +trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => + + override protected def nextId() = synchronized { super.nextId() } + + override protected def freshExistentialName(suffix: String) = + synchronized { super.freshExistentialName(suffix) } + + // Set the fields which point companions at one another. Returns the module. + override def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = + synchronized { super.connectModuleToClass(m, moduleClass) } + + override def newFreeTermSymbol(name: TermName, info: Type, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = + new FreeTermSymbol(name, value, origin) with SynchronizedTermSymbol initFlags flags setInfo info + + override def newFreeTypeSymbol(name: TypeName, info: Type, value: => Any, flags: Long = 0L, origin: String = null): FreeTypeSymbol = + new FreeTypeSymbol(name, value, origin) with SynchronizedTypeSymbol initFlags flags setInfo info + + override protected def makeNoSymbol: NoSymbol = new NoSymbol with SynchronizedSymbol + + trait SynchronizedSymbol extends Symbol { + + override def rawflags = synchronized { super.rawflags } + override def rawflags_=(x: Long) = synchronized { super.rawflags_=(x) } + + override def rawowner = synchronized { super.rawowner } + override def owner_=(owner: Symbol) = synchronized { super.owner_=(owner) } + + override def validTo = synchronized { super.validTo } + override def validTo_=(x: Period) = synchronized { super.validTo_=(x) } + + override def pos = synchronized { super.pos } + override def setPos(pos: Position): this.type = { synchronized { super.setPos(pos) }; this } + + override def privateWithin = synchronized { super.privateWithin } + override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } + + override def info = synchronized { super.info } + override def info_=(info: Type) = synchronized { super.info_=(info) } + override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } + override def rawInfo: Type = synchronized { super.rawInfo } + + override def typeParams: List[Symbol] = synchronized { super.typeParams } + + override def reset(completer: Type): this.type = synchronized { super.reset(completer) } + + override def infosString: String = synchronized { super.infosString } + + override def annotations: List[AnnotationInfo] = synchronized { super.annotations } + override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } + + +// ------ creators ------------------------------------------------------------------- + + override protected def createAbstractTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AbstractTypeSymbol = + new AbstractTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags + + override protected def createAliasTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AliasTypeSymbol = + new AliasTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags + + override protected def createTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position, newFlags: Long): TypeSkolem = + new TypeSkolem(this, pos, name, origin) with SynchronizedTypeSymbol initFlags newFlags + + override protected def createClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = + new ClassSymbol(this, pos, name) with SynchronizedClassSymbol initFlags newFlags + + override protected def createModuleClassSymbol(name: TypeName, pos: Position, newFlags: Long): ModuleClassSymbol = + new ModuleClassSymbol(this, pos, name) with SynchronizedModuleClassSymbol initFlags newFlags + + override protected def createPackageClassSymbol(name: TypeName, pos: Position, newFlags: Long): PackageClassSymbol = + new PackageClassSymbol(this, pos, name) with SynchronizedModuleClassSymbol initFlags newFlags + + override protected def createRefinementClassSymbol(pos: Position, newFlags: Long): RefinementClassSymbol = + new RefinementClassSymbol(this, pos) with SynchronizedClassSymbol initFlags newFlags + + override protected def createImplClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = + new ClassSymbol(this, pos, name) with ImplClassSymbol with SynchronizedClassSymbol initFlags newFlags + + override protected def createPackageObjectClassSymbol(pos: Position, newFlags: Long): PackageObjectClassSymbol = + new PackageObjectClassSymbol(this, pos) with SynchronizedClassSymbol initFlags newFlags + + override protected def createTermSymbol(name: TermName, pos: Position, newFlags: Long): TermSymbol = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags + + override protected def createMethodSymbol(name: TermName, pos: Position, newFlags: Long): MethodSymbol = + new MethodSymbol(this, pos, name) with SynchronizedMethodSymbol initFlags newFlags + + override protected def createModuleSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = + new ModuleSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags + + override protected def createPackageSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = createModuleSymbol(name, pos, newFlags) + + // TODO + // override protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long) + // override protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long) + } + +// ------- subclasses --------------------------------------------------------------------- + + trait SynchronizedTermSymbol extends TermSymbol with SynchronizedSymbol { + override def name_=(x: Name) = synchronized { super.name_=(x) } + override def rawname = synchronized { super.rawname } + override def referenced: Symbol = synchronized { super.referenced } + override def referenced_=(x: Symbol) = synchronized { super.referenced_=(x) } + } + + trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { + override def typeAsMemberOf(pre: Type): Type = synchronized { super.typeAsMemberOf(pre) } + } + + trait SynchronizedTypeSymbol extends TypeSymbol with SynchronizedSymbol { + override def name_=(x: Name) = synchronized { super.name_=(x) } + override def rawname = synchronized { super.rawname } + override def typeConstructor: Type = synchronized { super.typeConstructor } + override def tpe: Type = synchronized { super.tpe } + } + + trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { + override def associatedFile = synchronized { super.associatedFile } + override def associatedFile_=(f: AbstractFileType) = synchronized { super.associatedFile_=(f) } + override def thisSym: Symbol = synchronized { super.thisSym } + override def thisType: Type = synchronized { super.thisType } + override def typeOfThis: Type = synchronized { super.typeOfThis } + override def typeOfThis_=(tp: Type) = synchronized { super.typeOfThis_=(tp) } + override def children = synchronized { super.children } + override def addChild(sym: Symbol) = synchronized { super.addChild(sym) } + } + + trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { + override def sourceModule = synchronized { super.sourceModule } + // [Eugene++ to Martin] doesn't override anything. no longer necessary? + // def sourceModule_=(module: ModuleSymbol) = synchronized { super.sourceModule_=(module) } + override def implicitMembers: List[Symbol] = synchronized { super.implicitMembers } + } +} + diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala new file mode 100644 index 0000000000..e1eb7a57fe --- /dev/null +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -0,0 +1,88 @@ +package scala.reflect +package runtime + +/** This trait overrides methods in reflect.internal, bracketing + * them in synchronized { ... } to make them thread-safe + */ +trait SynchronizedTypes extends internal.Types { self: SymbolTable => + + // No sharing of map objects: + override protected def commonOwnerMap = new CommonOwnerMap + + private object uniqueLock + + override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) } + + class SynchronizedUndoLog extends UndoLog { + + override def clear() = + synchronized { super.clear() } + + override def undo[T](block: => T): T = + synchronized { super.undo(block) } + + override def undoUnless(block: => Boolean): Boolean = + synchronized { super.undoUnless(block) } + } + + override protected def newUndoLog = new SynchronizedUndoLog + + override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = + synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } + + private object subsametypeLock + + override def isSameType(tp1: Type, tp2: Type): Boolean = + subsametypeLock.synchronized { super.isSameType(tp1, tp2) } + + override def isDifferentType(tp1: Type, tp2: Type): Boolean = + subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } + + override def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = + subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } + + private object lubglbLock + + override def glb(ts: List[Type]): Type = + lubglbLock.synchronized { super.glb(ts) } + + override def lub(ts: List[Type]): Type = + lubglbLock.synchronized { super.lub(ts) } + + private object indentLock + + override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { + indentLock.synchronized { super.explain(op, p, tp1, arg2) } + } + + private object toStringLock + + override protected def typeToString(tpe: Type): String = + toStringLock.synchronized(super.typeToString(tpe)) + + /* The idea of caches is as follows. + * When in reflexive mode, a cache is either null, or one sentinal + * value representing undefined or the final defined + * value. Hence, we can ask in non-synchronized ode whether the cache field + * is non null and different from the sentinel (if a sentinel exists). + * If that's true, the cache value is current. + * Otherwise we arrive in one of the defined... methods listed below + * which go through all steps in synchronized mode. + */ + + override protected def defineUnderlyingOfSingleType(tpe: SingleType) = + tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } + + override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = + tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } + + override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = + tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } + + override protected def defineParentsOfTypeRef(tpe: TypeRef) = + tpe.synchronized { super.defineParentsOfTypeRef(tpe) } + + override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = + tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } + +} diff --git a/src/reflect/scala/reflect/runtime/TwoWayCache.scala b/src/reflect/scala/reflect/runtime/TwoWayCache.scala new file mode 100644 index 0000000000..c7bfb3435d --- /dev/null +++ b/src/reflect/scala/reflect/runtime/TwoWayCache.scala @@ -0,0 +1,52 @@ +package scala.reflect +package runtime + +/** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + */ +import collection.mutable.HashMap + +private[runtime] class TwoWayCache[J, S] { + + private val toScalaMap = new HashMap[J, S] + private val toJavaMap = new HashMap[S, J] + + def enter(j: J, s: S) = synchronized { + // debugInfo("cached: "+j+"/"+s) + toScalaMap(j) = s + toJavaMap(s) = j + } + + def toScala(key: J)(body: => S): S = synchronized { + toScalaMap get key match { + case Some(v) => + v + case none => + val result = body + enter(key, result) + result + } + } + + def toJava(key: S)(body: => J): J = synchronized { + toJavaMap get key match { + case Some(v) => + v + case none => + val result = body + enter(result, key) + result + } + } + + def toJavaOption(key: S)(body: => Option[J]): Option[J] = synchronized { + toJavaMap get key match { + case None => + val result = body + for (value <- result) enter(value, key) + result + case some => some + } + } +} + diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala new file mode 100644 index 0000000000..a5809a2629 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/package.scala @@ -0,0 +1,26 @@ +package scala.reflect + +import language.experimental.macros + +package object runtime { + + // type is api.JavaUniverse because we only want to expose the `scala.reflect.api.*` subset of reflection + lazy val universe: api.JavaUniverse = new runtime.JavaUniverse + + // [Eugene++ to Martin] removed `mirrorOfLoader`, because one can use `universe.runtimeMirror` instead + + def currentMirror: universe.Mirror = macro Macros.currentMirror +} + +package runtime { + object Macros { + def currentMirror(c: scala.reflect.makro.Context): c.Expr[universe.Mirror] = { + import c.universe._ + val runtimeClass = c.reifyEnclosingRuntimeClass + if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class") + val runtimeUniverse = Select(Select(Select(Ident(newTermName("scala")), newTermName("reflect")), newTermName("runtime")), newTermName("universe")) + val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader")))) + c.Expr[Nothing](currentMirror)(c.TypeTag.Nothing) + } + } +} diff --git a/src/reflect/scala/tools/nsc/io/AbstractFile.scala b/src/reflect/scala/tools/nsc/io/AbstractFile.scala new file mode 100644 index 0000000000..fd56608fab --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/AbstractFile.scala @@ -0,0 +1,258 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + + +package scala.tools.nsc +package io + +import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream } +import java.io.{ File => JFile } +import java.net.URL +import scala.collection.mutable.ArrayBuffer + +/** + * @author Philippe Altherr + * @version 1.0, 23/03/2004 + */ +object AbstractFile { + /** Returns "getFile(new File(path))". */ + def getFile(path: String): AbstractFile = getFile(File(path)) + def getFile(path: Path): AbstractFile = getFile(path.toFile) + + /** + * If the specified File exists and is a regular file, returns an + * abstract regular file backed by it. Otherwise, returns <code>null</code>. + */ + def getFile(file: File): AbstractFile = + if (file.isFile) new PlainFile(file) else null + + /** Returns "getDirectory(new File(path))". */ + def getDirectory(path: Path): AbstractFile = getDirectory(path.toFile) + + /** + * If the specified File exists and is either a directory or a + * readable zip or jar archive, returns an abstract directory + * backed by it. Otherwise, returns <code>null</code>. + * + * @param file ... + * @return ... + */ + def getDirectory(file: File): AbstractFile = + if (file.isDirectory) new PlainFile(file) + else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file + else null + + /** + * If the specified URL exists and is a readable zip or jar archive, + * returns an abstract directory backed by it. Otherwise, returns + * <code>null</code>. + * + * @param file ... + * @return ... + */ + def getURL(url: URL): AbstractFile = { + if (url == null || !Path.isExtensionJarOrZip(url.getPath)) null + else ZipArchive fromURL url + } +} + +/** + * <p> + * This class and its children serve to unify handling of files and + * directories. These files and directories may or may not have some + * real counter part within the file system. For example, some file + * handles reference files within a zip archive or virtual ones + * that exist only in memory. + * </p> + * <p> + * Every abstract file has a path (i.e. a full name) and a name + * (i.e. a short name) and may be backed by some real File. There are + * two different kinds of abstract files: regular files and + * directories. Regular files may be read and have a last modification + * time. Directories may list their content and look for subfiles with + * a specified name or path and of a specified kind. + * </p> + * <p> + * The interface does <b>not</b> allow to access the content. + * The class <code>symtab.classfile.AbstractFileReader</code> accesses + * bytes, knowing that the character set of classfiles is UTF-8. For + * all other cases, the class <code>SourceFile</code> is used, which honors + * <code>global.settings.encoding.value</code>. + * </p> + */ +abstract class AbstractFile extends reflect.internal.AbstractFileApi with Iterable[AbstractFile] { + + /** Returns the name of this abstract file. */ + def name: String + + /** Returns the path of this abstract file. */ + def path: String + + /** Returns the path of this abstract file in a canonical form. */ + def canonicalPath: String = if (file == null) path else file.getCanonicalPath + + /** Checks extension case insensitively. */ + def hasExtension(other: String) = extension == other.toLowerCase + private lazy val extension: String = Path.extension(name) + + /** The absolute file, if this is a relative file. */ + def absolute: AbstractFile + + /** Returns the containing directory of this abstract file */ + def container : AbstractFile + + /** Returns the underlying File if any and null otherwise. */ + def file: JFile + + /** An underlying source, if known. Mostly, a zip/jar file. */ + def underlyingSource: Option[AbstractFile] = None + + /** Does this abstract file denote an existing file? */ + def exists: Boolean = (file eq null) || file.exists + + /** Does this abstract file represent something which can contain classfiles? */ + def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip")) + + /** Create a file on disk, if one does not exist already. */ + def create(): Unit + + /** Delete the underlying file or directory (recursively). */ + def delete(): Unit + + /** Is this abstract file a directory? */ + def isDirectory: Boolean + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long + + /** returns an input stream so the file can be read */ + def input: InputStream + + /** Returns an output stream for writing the file */ + def output: OutputStream + + /** Returns a buffered output stream for writing the file - defaults to out */ + def bufferedOutput: BufferedOutputStream = new BufferedOutputStream(output) + + /** size of this file if it is a concrete file. */ + def sizeOption: Option[Int] = None + + def toURL: URL = if (file == null) null else file.toURI.toURL + + /** Returns contents of file (if applicable) in a Char array. + * warning: use <code>Global.getSourceFile()</code> to use the proper + * encoding when converting to the char array. + */ + @throws(classOf[IOException]) + def toCharArray = new String(toByteArray).toCharArray + + /** Returns contents of file (if applicable) in a byte array. + */ + @throws(classOf[IOException]) + def toByteArray: Array[Byte] = { + val in = input + var rest = sizeOption.getOrElse(0) + val arr = new Array[Byte](rest) + while (rest > 0) { + val res = in.read(arr, arr.length - rest, rest) + if (res == -1) + throw new IOException("read error") + rest -= res + } + in.close() + arr + } + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] + + /** Returns the abstract file in this abstract directory with the specified + * name. If there is no such file, returns <code>null</code>. The argument + * <code>directory</code> tells whether to look for a directory or + * a regular file. + */ + def lookupName(name: String, directory: Boolean): AbstractFile + + /** Returns an abstract file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile + + /** Returns the abstract file in this abstract directory with the specified + * path relative to it, If there is no such file, returns null. The argument + * <code>directory</code> tells whether to look for a directory or a regular + * file. + * + * @param path ... + * @param directory ... + * @return ... + */ + def lookupPath(path: String, directory: Boolean): AbstractFile = { + lookup((f, p, dir) => f.lookupName(p, dir), path, directory) + } + + /** Return an abstract file that does not check that `path` denotes + * an existing file. + */ + def lookupPathUnchecked(path: String, directory: Boolean): AbstractFile = { + lookup((f, p, dir) => f.lookupNameUnchecked(p, dir), path, directory) + } + + private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile, + path0: String, + directory: Boolean): AbstractFile = { + val separator = java.io.File.separatorChar + // trim trailing '/'s + val path: String = if (path0.last == separator) path0 dropRight 1 else path0 + val length = path.length() + assert(length > 0 && !(path.last == separator), path) + var file = this + var start = 0 + while (true) { + val index = path.indexOf(separator, start) + assert(index < 0 || start < index, ((path, directory, start, index))) + val name = path.substring(start, if (index < 0) length else index) + file = getFile(file, name, if (index < 0) directory else true) + if ((file eq null) || index < 0) return file + start = index + 1 + } + file + } + + private def fileOrSubdirectoryNamed(name: String, isDir: Boolean): AbstractFile = { + val lookup = lookupName(name, isDir) + if (lookup != null) lookup + else { + val jfile = new JFile(file, name) + if (isDir) jfile.mkdirs() else jfile.createNewFile() + new PlainFile(jfile) + } + } + + /** + * Get the file in this directory with the given name, + * creating an empty file if it does not already existing. + */ + def fileNamed(name: String): AbstractFile = { + assert(isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path)) + fileOrSubdirectoryNamed(name, false) + } + + /** + * Get the subdirectory with a given name, creating it if it + * does not already exist. + */ + def subdirectoryNamed(name: String): AbstractFile = { + assert (isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path)) + fileOrSubdirectoryNamed(name, true) + } + + protected def unsupported(): Nothing = unsupported(null) + protected def unsupported(msg: String): Nothing = throw new UnsupportedOperationException(msg) + + /** Returns the path of this abstract file. */ + override def toString() = path + +} diff --git a/src/reflect/scala/tools/nsc/io/Directory.scala b/src/reflect/scala/tools/nsc/io/Directory.scala new file mode 100644 index 0000000000..ebd6edc8d8 --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/Directory.scala @@ -0,0 +1,75 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.tools.nsc +package io + +import java.io.{ File => JFile } + +object Directory { + import scala.util.Properties.{ tmpDir, userHome, userDir } + + private def normalizePath(s: String) = Some(apply(Path(s).normalize)) + def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir) + def Home: Option[Directory] = if (userHome == "") None else normalizePath(userHome) + def TmpDir: Option[Directory] = if (tmpDir == "") None else normalizePath(tmpDir) + + def apply(path: Path): Directory = path.toDirectory + + // Like File.makeTemp but creates a directory instead + def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = { + val path = File.makeTemp(prefix, suffix, dir) + path.delete() + path.createDirectory() + } +} +import Path._ + +/** An abstraction for directories. + * + * @author Paul Phillips + * @since 2.8 + */ +class Directory(jfile: JFile) extends Path(jfile) { + override def toAbsolute: Directory = if (isAbsolute) this else super.toAbsolute.toDirectory + override def toDirectory: Directory = this + override def toFile: File = new File(jfile) + override def isValid = jfile.isDirectory() || !jfile.exists() + override def normalize: Directory = super.normalize.toDirectory + + /** An iterator over the contents of this directory. + */ + def list: Iterator[Path] = + jfile.listFiles match { + case null => Iterator.empty + case xs => xs.iterator map Path.apply + } + + def dirs: Iterator[Directory] = list collect { case x: Directory => x } + def files: Iterator[File] = list collect { case x: File => x } + + override def walkFilter(cond: Path => Boolean): Iterator[Path] = + list filter cond flatMap (_ walkFilter cond) + + def deepDirs: Iterator[Directory] = Path.onlyDirs(deepList()) + def deepFiles: Iterator[File] = Path.onlyFiles(deepList()) + + /** If optional depth argument is not given, will recurse + * until it runs out of contents. + */ + def deepList(depth: Int = -1): Iterator[Path] = + if (depth < 0) list ++ (dirs flatMap (_ deepList (depth))) + else if (depth == 0) Iterator.empty + else list ++ (dirs flatMap (_ deepList (depth - 1))) + + /** An iterator over the directories underneath this directory, + * to the (optionally) given depth. + */ + def subdirs(depth: Int = 1): Iterator[Directory] = + deepList(depth) collect { case x: Directory => x } +} diff --git a/src/reflect/scala/tools/nsc/io/File.scala b/src/reflect/scala/tools/nsc/io/File.scala new file mode 100644 index 0000000000..eedf92ef98 --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/File.scala @@ -0,0 +1,193 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala.tools.nsc +package io + +import java.io.{ + FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter, + BufferedInputStream, BufferedOutputStream, IOException, PrintStream, PrintWriter, Closeable => JCloseable } +import java.io.{ File => JFile } +import java.nio.channels.{ Channel, FileChannel } +import scala.io.Codec +import language.{reflectiveCalls, implicitConversions} + +object File { + def pathSeparator = java.io.File.pathSeparator + def separator = java.io.File.separator + + def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec) + + // Create a temporary file, which will be deleted upon jvm exit. + def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = { + val jfile = java.io.File.createTempFile(prefix, suffix, dir) + jfile.deleteOnExit() + apply(jfile) + } + + type HasClose = { def close(): Unit } + + def closeQuietly(target: HasClose) { + try target.close() catch { case e: IOException => } + } + def closeQuietly(target: JCloseable) { + try target.close() catch { case e: IOException => } + } + + // this is a workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6503430 + // we are using a static initializer to statically initialize a java class so we don't + // trigger java.lang.InternalErrors later when using it concurrently. We ignore all + // the exceptions so as not to cause spurious failures when no write access is available, + // e.g. google app engine. + // + // XXX need to put this behind a setting. + // + // try { + // import Streamable.closing + // val tmp = java.io.File.createTempFile("bug6503430", null, null) + // try closing(new FileInputStream(tmp)) { in => + // val inc = in.getChannel() + // closing(new FileOutputStream(tmp, true)) { out => + // out.getChannel().transferFrom(inc, 0, 0) + // } + // } + // finally tmp.delete() + // } + // catch { + // case _: IllegalArgumentException | _: IllegalStateException | _: IOException | _: SecurityException => () + // } +} +import File._ +import Path._ + +/** An abstraction for files. For character data, a Codec + * can be supplied at either creation time or when a method + * involving character data is called (with the latter taking + * precedence if supplied.) If neither is available, the value + * of scala.io.Codec.default is used. + * + * @author Paul Phillips + * @since 2.8 + */ +class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) with Streamable.Chars { + override val creationCodec = constructorCodec + def withCodec(codec: Codec): File = new File(jfile)(codec) + + override def addExtension(ext: String): File = super.addExtension(ext).toFile + override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile + override def toDirectory: Directory = new Directory(jfile) + override def toFile: File = this + override def normalize: File = super.normalize.toFile + override def isValid = jfile.isFile() || !jfile.exists() + override def length = super[Path].length + override def walkFilter(cond: Path => Boolean): Iterator[Path] = + if (cond(this)) Iterator.single(this) else Iterator.empty + + /** Obtains an InputStream. */ + def inputStream() = new FileInputStream(jfile) + + /** Obtains a OutputStream. */ + def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append) + def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append)) + def printStream(append: Boolean = false) = new PrintStream(outputStream(append), true) + + /** Obtains an OutputStreamWriter wrapped around a FileOutputStream. + * This should behave like a less broken version of java.io.FileWriter, + * in that unlike the java version you can specify the encoding. + */ + def writer(): OutputStreamWriter = writer(false) + def writer(append: Boolean): OutputStreamWriter = writer(append, creationCodec) + def writer(append: Boolean, codec: Codec): OutputStreamWriter = + new OutputStreamWriter(outputStream(append), codec.charSet) + + /** Wraps a BufferedWriter around the result of writer(). + */ + def bufferedWriter(): BufferedWriter = bufferedWriter(false) + def bufferedWriter(append: Boolean): BufferedWriter = bufferedWriter(append, creationCodec) + def bufferedWriter(append: Boolean, codec: Codec): BufferedWriter = + new BufferedWriter(writer(append, codec)) + + def printWriter(): PrintWriter = new PrintWriter(bufferedWriter(), true) + def printWriter(append: Boolean): PrintWriter = new PrintWriter(bufferedWriter(append), true) + + /** Creates a new file and writes all the Strings to it. */ + def writeAll(strings: String*): Unit = { + val out = bufferedWriter() + try strings foreach (out write _) + finally out close + } + + def writeBytes(bytes: Array[Byte]): Unit = { + val out = bufferedOutput() + try out write bytes + finally out close + } + + def appendAll(strings: String*): Unit = { + val out = bufferedWriter(append = true) + try strings foreach (out write _) + finally out.close() + } + + /** Calls println on each string (so it adds a newline in the PrintWriter fashion.) */ + def printlnAll(strings: String*): Unit = { + val out = printWriter() + try strings foreach (out println _) + finally out close + } + + def safeSlurp(): Option[String] = + try Some(slurp()) + catch { case _: IOException => None } + + def copyTo(destPath: Path, preserveFileDate: Boolean = false): Boolean = { + val CHUNK = 1024 * 1024 * 16 // 16 MB + val dest = destPath.toFile + if (!isValid) fail("Source %s is not a valid file." format name) + if (this.normalize == dest.normalize) fail("Source and destination are the same.") + if (!dest.parent.exists) fail("Destination cannot be created.") + if (dest.exists && !dest.canWrite) fail("Destination exists but is not writable.") + if (dest.isDirectory) fail("Destination exists but is a directory.") + + lazy val in_s = inputStream() + lazy val out_s = dest.outputStream() + lazy val in = in_s.getChannel() + lazy val out = out_s.getChannel() + + try { + val size = in.size() + var pos, count = 0L + while (pos < size) { + count = (size - pos) min CHUNK + pos += out.transferFrom(in, pos, count) + } + } + finally List[HasClose](out, out_s, in, in_s) foreach closeQuietly + + if (this.length != dest.length) + fail("Failed to completely copy %s to %s".format(name, dest.name)) + + if (preserveFileDate) + dest.lastModified = this.lastModified + + true + } + + /** Reflection since we're into the java 6+ API. + */ + def setExecutable(executable: Boolean, ownerOnly: Boolean = true): Boolean = { + type JBoolean = java.lang.Boolean + val method = + try classOf[JFile].getMethod("setExecutable", classOf[Boolean], classOf[Boolean]) + catch { case _: NoSuchMethodException => return false } + + try method.invoke(jfile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue + catch { case _: Exception => false } + } +} diff --git a/src/reflect/scala/tools/nsc/io/FileOperationException.scala b/src/reflect/scala/tools/nsc/io/FileOperationException.scala new file mode 100644 index 0000000000..f23658efbc --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/FileOperationException.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package scala.tools.nsc +package io + +case class FileOperationException(msg: String) extends RuntimeException(msg) diff --git a/src/reflect/scala/tools/nsc/io/NoAbstractFile.scala b/src/reflect/scala/tools/nsc/io/NoAbstractFile.scala new file mode 100644 index 0000000000..04568b0e2e --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/NoAbstractFile.scala @@ -0,0 +1,31 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package io + +import java.io.InputStream +import java.io.{ File => JFile } + +/** A distinguished object so you can avoid both null + * and Option. + */ +object NoAbstractFile extends AbstractFile { + def absolute: AbstractFile = this + def container: AbstractFile = this + def create(): Unit = ??? + def delete(): Unit = ??? + def file: JFile = null + def input: InputStream = null + def isDirectory: Boolean = false + def iterator: Iterator[AbstractFile] = Iterator.empty + def lastModified: Long = 0L + def lookupName(name: String, directory: Boolean): AbstractFile = null + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = null + def name: String = "" + def output: java.io.OutputStream = null + def path: String = "" + override def toByteArray = Array[Byte]() +} diff --git a/src/reflect/scala/tools/nsc/io/Path.scala b/src/reflect/scala/tools/nsc/io/Path.scala new file mode 100644 index 0000000000..984c96dfbb --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/Path.scala @@ -0,0 +1,288 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package io + +import java.io.{ + FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter, + BufferedInputStream, BufferedOutputStream, RandomAccessFile } +import java.io.{ File => JFile } +import java.net.{ URI, URL } +import scala.util.Random.alphanumeric +import language.implicitConversions + +/** An abstraction for filesystem paths. The differences between + * Path, File, and Directory are primarily to communicate intent. + * Since the filesystem can change at any time, there is no way to + * reliably associate Files only with files and so on. Any Path + * can be converted to a File or Directory (and thus gain access to + * the additional entity specific methods) by calling toFile or + * toDirectory, which has no effect on the filesystem. + * + * Also available are createFile and createDirectory, which attempt + * to create the path in question. + * + * @author Paul Phillips + * @since 2.8 + */ + +object Path { + def isExtensionJarOrZip(jfile: JFile): Boolean = isExtensionJarOrZip(jfile.getName) + def isExtensionJarOrZip(name: String): Boolean = { + val ext = extension(name) + ext == "jar" || ext == "zip" + } + def extension(name: String): String = { + var i = name.length - 1 + while (i >= 0 && name.charAt(i) != '.') + i -= 1 + + if (i < 0) "" + else name.substring(i + 1).toLowerCase + } + // [Eugene++] I hope that noone relied on this method +// def isJarOrZip(f: Path, examineFile: Boolean = true) = Jar.isJarOrZip(f, examineFile) + + // not certain these won't be problematic, but looks good so far + implicit def string2path(s: String): Path = apply(s) + implicit def jfile2path(jfile: JFile): Path = apply(jfile) + + // java 7 style, we don't use it yet + // object AccessMode extends Enumeration { + // val EXECUTE, READ, WRITE = Value + // } + // def checkAccess(modes: AccessMode*): Boolean = { + // modes foreach { + // case EXECUTE => throw new Exception("Unsupported") // can't check in java 5 + // case READ => if (!jfile.canRead()) return false + // case WRITE => if (!jfile.canWrite()) return false + // } + // true + // } + + def onlyDirs(xs: Iterator[Path]): Iterator[Directory] = xs filter (_.isDirectory) map (_.toDirectory) + def onlyDirs(xs: List[Path]): List[Directory] = xs filter (_.isDirectory) map (_.toDirectory) + def onlyFiles(xs: Iterator[Path]): Iterator[File] = xs filter (_.isFile) map (_.toFile) + def onlyFiles(xs: List[Path]): List[File] = xs filter (_.isFile) map (_.toFile) + + def roots: List[Path] = java.io.File.listRoots().toList map Path.apply + + def apply(segments: Seq[String]): Path = apply(segments mkString java.io.File.separator) + def apply(path: String): Path = apply(new JFile(path)) + def apply(jfile: JFile): Path = + if (jfile.isFile) new File(jfile) + else if (jfile.isDirectory) new Directory(jfile) + else new Path(jfile) + + /** Avoiding any shell/path issues by only using alphanumerics. */ + private[io] def randomPrefix = alphanumeric take 6 mkString + private[io] def fail(msg: String) = throw FileOperationException(msg) +} +import Path._ + +/** The Path constructor is private so we can enforce some + * semantics regarding how a Path might relate to the world. + */ +class Path private[io] (val jfile: JFile) { + val separator = java.io.File.separatorChar + val separatorStr = java.io.File.separator + + // Validation: this verifies that the type of this object and the + // contents of the filesystem are in agreement. All objects are + // valid except File objects whose path points to a directory and + // Directory objects whose path points to a file. + def isValid: Boolean = true + + // conversions + def toFile: File = new File(jfile) + def toDirectory: Directory = new Directory(jfile) + def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath()) + def toCanonical: Path = Path(jfile.getCanonicalPath()) + def toURI: URI = jfile.toURI() + def toURL: URL = toURI.toURL() + /** If this path is absolute, returns it: otherwise, returns an absolute + * path made up of root / this. + */ + def toAbsoluteWithRoot(root: Path) = if (isAbsolute) this else root.toAbsolute / this + + /** Creates a new Path with the specified path appended. Assumes + * the type of the new component implies the type of the result. + */ + def /(child: Path): Path = if (isEmpty) child else new Path(new JFile(jfile, child.path)) + def /(child: Directory): Directory = /(child: Path).toDirectory + def /(child: File): File = /(child: Path).toFile + + /** If this path is a container, recursively iterate over its contents. + * The supplied condition is a filter which is applied to each element, + * with that branch of the tree being closed off if it is true. So for + * example if the condition is true for some subdirectory, nothing + * under that directory will be in the Iterator; but otherwise each + * file and subdirectory underneath it will appear. + */ + def walkFilter(cond: Path => Boolean): Iterator[Path] = + if (isFile) toFile walkFilter cond + else if (isDirectory) toDirectory walkFilter cond + else Iterator.empty + + /** Equivalent to walkFilter(_ => false). + */ + def walk: Iterator[Path] = walkFilter(_ => true) + + // identity + def name: String = jfile.getName() + def path: String = jfile.getPath() + def normalize: Path = Path(jfile.getAbsolutePath()) + def isRootPath: Boolean = roots exists (_ isSame this) + + def resolve(other: Path) = if (other.isAbsolute || isEmpty) other else /(other) + def relativize(other: Path) = { + assert(isAbsolute == other.isAbsolute, "Paths not of same type: "+this+", "+other) + + def createRelativePath(baseSegs: List[String], otherSegs: List[String]) : String = { + (baseSegs, otherSegs) match { + case (b :: bs, o :: os) if b == o => createRelativePath(bs, os) + case (bs, os) => ((".."+separator)*bs.length)+os.mkString(separatorStr) + } + } + + Path(createRelativePath(segments, other.segments)) + } + + // derived from identity + def root: Option[Path] = roots find (this startsWith _) + def segments: List[String] = (path split separator).toList filterNot (_.length == 0) + /** + * @return The path of the parent directory, or root if path is already root + */ + def parent: Directory = path match { + case "" | "." => Directory("..") + case _ => + // the only solution <-- a comment which could have used elaboration + if (segments.nonEmpty && segments.last == "..") + (path / "..").toDirectory + else jfile.getParent match { + case null => + if (isAbsolute) toDirectory // it should be a root. BTW, don't need to worry about relative pathed root + else Directory(".") // a dir under pwd + case x => + Directory(x) + } + } + def parents: List[Directory] = { + val p = parent + if (p isSame this) Nil else p :: p.parents + } + // if name ends with an extension (e.g. "foo.jpg") returns the extension ("jpg"), otherwise "" + def extension: String = { + var i = name.length - 1 + while (i >= 0 && name.charAt(i) != '.') + i -= 1 + + if (i < 0) "" + else name.substring(i + 1) + } + // def extension: String = (name lastIndexOf '.') match { + // case -1 => "" + // case idx => name drop (idx + 1) + // } + // compares against extensions in a CASE INSENSITIVE way. + def hasExtension(ext: String, exts: String*) = { + val lower = extension.toLowerCase + ext.toLowerCase == lower || exts.exists(_.toLowerCase == lower) + } + // returns the filename without the extension. + def stripExtension: String = name stripSuffix ("." + extension) + // returns the Path with the extension. + def addExtension(ext: String): Path = Path(path + "." + ext) + // changes the existing extension out for a new one, or adds it + // if the current path has none. + def changeExtension(ext: String): Path = ( + if (extension == "") addExtension(ext) + else Path(path.stripSuffix(extension) + ext) + ) + + // conditionally execute + def ifFile[T](f: File => T): Option[T] = if (isFile) Some(f(toFile)) else None + def ifDirectory[T](f: Directory => T): Option[T] = if (isDirectory) Some(f(toDirectory)) else None + + // Boolean tests + def canRead = jfile.canRead() + def canWrite = jfile.canWrite() + def exists = jfile.exists() + def notExists = try !jfile.exists() catch { case ex: SecurityException => false } + + def isFile = jfile.isFile() + def isDirectory = jfile.isDirectory() + def isAbsolute = jfile.isAbsolute() + def isHidden = jfile.isHidden() + def isEmpty = path.length == 0 + + // Information + def lastModified = jfile.lastModified() + def lastModified_=(time: Long) = jfile setLastModified time // should use setXXX function? + def length = jfile.length() + + // Boolean path comparisons + def endsWith(other: Path) = segments endsWith other.segments + def startsWith(other: Path) = segments startsWith other.segments + def isSame(other: Path) = toCanonical == other.toCanonical + def isFresher(other: Path) = lastModified > other.lastModified + + // creations + def createDirectory(force: Boolean = true, failIfExists: Boolean = false): Directory = { + val res = if (force) jfile.mkdirs() else jfile.mkdir() + if (!res && failIfExists && exists) fail("Directory '%s' already exists." format name) + else if (isDirectory) toDirectory + else new Directory(jfile) + } + def createFile(failIfExists: Boolean = false): File = { + val res = jfile.createNewFile() + if (!res && failIfExists && exists) fail("File '%s' already exists." format name) + else if (isFile) toFile + else new File(jfile) + } + + // deletions + def delete() = jfile.delete() + def deleteIfExists() = if (jfile.exists()) delete() else false + + /** Deletes the path recursively. Returns false on failure. + * Use with caution! + */ + def deleteRecursively(): Boolean = deleteRecursively(jfile) + private def deleteRecursively(f: JFile): Boolean = { + if (f.isDirectory) f.listFiles match { + case null => + case xs => xs foreach deleteRecursively + } + f.delete() + } + + def truncate() = + isFile && { + val raf = new RandomAccessFile(jfile, "rw") + raf setLength 0 + raf.close() + length == 0 + } + + def touch(modTime: Long = System.currentTimeMillis) = { + createFile() + if (isFile) + lastModified = modTime + } + + // todo + // def copyTo(target: Path, options ...): Boolean + // def moveTo(target: Path, options ...): Boolean + + override def toString() = path + override def equals(other: Any) = other match { + case x: Path => path == x.path + case _ => false + } + override def hashCode() = path.hashCode() +} diff --git a/src/reflect/scala/tools/nsc/io/PlainFile.scala b/src/reflect/scala/tools/nsc/io/PlainFile.scala new file mode 100644 index 0000000000..21276e8740 --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/PlainFile.scala @@ -0,0 +1,102 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + + +package scala.tools.nsc +package io + +import java.io.{ FileInputStream, FileOutputStream, IOException } +import PartialFunction._ + +object PlainFile { + /** + * If the specified File exists, returns an abstract file backed + * by it. Otherwise, returns null. + */ + def fromPath(file: Path): PlainFile = + if (file.isDirectory) new PlainDirectory(file.toDirectory) + else if (file.isFile) new PlainFile(file) + else null +} + +class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { + override def isDirectory = true + override def iterator = givenPath.list filter (_.exists) map (x => new PlainFile(x)) + override def delete(): Unit = givenPath.deleteRecursively() +} + +/** This class implements an abstract file backed by a File. + */ +class PlainFile(val givenPath: Path) extends AbstractFile { + assert(path ne null) + + val file = givenPath.jfile + override def underlyingSource = Some(this) + + private val fpath = givenPath.toAbsolute + + /** Returns the name of this abstract file. */ + def name = givenPath.name + + /** Returns the path of this abstract file. */ + def path = givenPath.path + + /** The absolute file. */ + def absolute = new PlainFile(givenPath.toAbsolute) + + override def container: AbstractFile = new PlainFile(givenPath.parent) + override def input = givenPath.toFile.inputStream() + override def output = givenPath.toFile.outputStream() + override def sizeOption = Some(givenPath.length.toInt) + + override def toString = path + override def hashCode(): Int = fpath.hashCode + override def equals(that: Any): Boolean = that match { + case x: PlainFile => fpath == x.fpath + case _ => false + } + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = givenPath.isDirectory + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long = givenPath.lastModified + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] = { + if (!isDirectory) Iterator.empty + else givenPath.toDirectory.list filter (_.exists) map (new PlainFile(_)) + } + + /** + * Returns the abstract file in this abstract directory with the + * specified name. If there is no such file, returns null. The + * argument "directory" tells whether to look for a directory or + * or a regular file. + * + * @param name ... + * @param directory ... + * @return ... + */ + def lookupName(name: String, directory: Boolean): AbstractFile = { + val child = givenPath / name + if ((child.isDirectory && directory) || (child.isFile && !directory)) new PlainFile(child) + else null + } + + /** Does this abstract file denote an existing file? */ + def create(): Unit = if (!exists) givenPath.createFile() + + /** Delete the underlying file or directory (recursively). */ + def delete(): Unit = + if (givenPath.isFile) givenPath.delete() + else if (givenPath.isDirectory) givenPath.toDirectory.deleteRecursively() + + /** Returns a plain file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = + new PlainFile(givenPath / name) +} diff --git a/src/reflect/scala/tools/nsc/io/Streamable.scala b/src/reflect/scala/tools/nsc/io/Streamable.scala new file mode 100644 index 0000000000..03318674ee --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/Streamable.scala @@ -0,0 +1,122 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package io + +import java.net.{ URI, URL } +import java.io.{ BufferedInputStream, InputStream, PrintStream } +import java.io.{ BufferedReader, InputStreamReader, Closeable => JCloseable } +import scala.io.{ Codec, BufferedSource, Source } +import collection.mutable.ArrayBuffer +import Path.fail + +/** Traits for objects which can be represented as Streams. + * + * @author Paul Phillips + * @since 2.8 + */ + +object Streamable { + /** Traits which can be viewed as a sequence of bytes. Source types + * which know their length should override def length: Long for more + * efficient method implementations. + */ + trait Bytes { + def inputStream(): InputStream + def length: Long = -1 + + def bufferedInput() = new BufferedInputStream(inputStream()) + def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte) + def bytesAsInts(): Iterator[Int] = { + val in = bufferedInput() + Iterator continually in.read() takeWhile (_ != -1) + } + + /** This method aspires to be the fastest way to read + * a stream of known length into memory. + */ + def toByteArray(): Array[Byte] = { + // if we don't know the length, fall back on relative inefficiency + if (length == -1L) + return (new ArrayBuffer[Byte]() ++= bytes()).toArray + + val arr = new Array[Byte](length.toInt) + val len = arr.length + lazy val in = bufferedInput() + var offset = 0 + + def loop() { + if (offset < len) { + val read = in.read(arr, offset, len - offset) + if (read >= 0) { + offset += read + loop() + } + } + } + try loop() + finally in.close() + + if (offset == arr.length) arr + else fail("Could not read entire source (%d of %d bytes)".format(offset, len)) + } + } + + /** For objects which can be viewed as Chars. + */ + trait Chars extends Bytes { + /** Calls to methods requiring byte<->char transformations should be offered + * in a form which allows specifying the codec. When it is not specified, + * the one discovered at creation time will be used, which will always find the + * one in scala.io.Codec if no other is available. This can be overridden + * to use a different default. + */ + def creationCodec: Codec = implicitly[Codec] + + def chars(): BufferedSource = chars(creationCodec) + def chars(codec: Codec): BufferedSource = Source.fromInputStream(inputStream())(codec) + + def lines(): Iterator[String] = lines(creationCodec) + def lines(codec: Codec): Iterator[String] = chars(codec).getLines() + + /** Obtains an InputStreamReader wrapped around a FileInputStream. + */ + def reader(): InputStreamReader = reader(creationCodec) + def reader(codec: Codec): InputStreamReader = new InputStreamReader(inputStream, codec.charSet) + + /** Wraps a BufferedReader around the result of reader(). + */ + def bufferedReader(): BufferedReader = bufferedReader(creationCodec) + def bufferedReader(codec: Codec) = new BufferedReader(reader(codec)) + + /** Creates a BufferedReader and applies the closure, automatically closing it on completion. + */ + def applyReader[T](f: BufferedReader => T): T = { + val in = bufferedReader() + try f(in) + finally in.close() + } + + /** Convenience function to import entire file into a String. + */ + def slurp(): String = slurp(creationCodec) + def slurp(codec: Codec) = chars(codec).mkString + } + + /** Call a function on something Closeable, finally closing it. */ + def closing[T <: JCloseable, U](stream: T)(f: T => U): U = + try f(stream) + finally stream.close() + + def bytes(is: => InputStream): Array[Byte] = + new Bytes { def inputStream() = is } toByteArray + + def slurp(is: => InputStream)(implicit codec: Codec): String = + new Chars { def inputStream() = is } slurp codec + + def slurp(url: URL)(implicit codec: Codec): String = + slurp(url.openStream()) +} diff --git a/src/reflect/scala/tools/nsc/io/VirtualDirectory.scala b/src/reflect/scala/tools/nsc/io/VirtualDirectory.scala new file mode 100644 index 0000000000..0bcb2de43f --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/VirtualDirectory.scala @@ -0,0 +1,70 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + */ + +package scala.tools.nsc +package io + +import scala.collection.mutable + +/** + * An in-memory directory. + * + * @author Lex Spoon + */ +class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory]) +extends AbstractFile { + def path: String = + maybeContainer match { + case None => name + case Some(parent) => parent.path+'/'+ name + } + + def absolute = this + + def container = maybeContainer.get + def isDirectory = true + var lastModified: Long = System.currentTimeMillis + + override def file = null + override def input = sys.error("directories cannot be read") + override def output = sys.error("directories cannot be written") + + /** Does this abstract file denote an existing file? */ + def create() { unsupported } + + /** Delete the underlying file or directory (recursively). */ + def delete() { unsupported } + + /** Returns an abstract file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = unsupported + + private val files = mutable.Map.empty[String, AbstractFile] + + // the toList is so that the directory may continue to be + // modified while its elements are iterated + def iterator = files.values.toList.iterator + + override def lookupName(name: String, directory: Boolean): AbstractFile = + files get name filter (_.isDirectory == directory) orNull + + override def fileNamed(name: String): AbstractFile = + Option(lookupName(name, false)) getOrElse { + val newFile = new VirtualFile(name, path+'/'+name) + files(name) = newFile + newFile + } + + override def subdirectoryNamed(name: String): AbstractFile = + Option(lookupName(name, true)) getOrElse { + val dir = new VirtualDirectory(name, Some(this)) + files(name) = dir + dir + } + + def clear() { + files.clear(); + } +} diff --git a/src/reflect/scala/tools/nsc/io/VirtualFile.scala b/src/reflect/scala/tools/nsc/io/VirtualFile.scala new file mode 100644 index 0000000000..b9a946598c --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/VirtualFile.scala @@ -0,0 +1,101 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + + +package scala.tools.nsc +package io + +import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, InputStream, OutputStream } +import java.io.{ File => JFile } + +/** This class implements an in-memory file. + * + * @author Philippe Altherr + * @version 1.0, 23/03/2004 + */ +class VirtualFile(val name: String, override val path: String) extends AbstractFile { + /** + * Initializes this instance with the specified name and an + * identical path. + * + * @param name the name of the virtual file to be created + * @return the created virtual file + */ + def this(name: String) = this(name, name) + + override def hashCode = path.hashCode + override def equals(that: Any) = that match { + case x: VirtualFile => x.path == path + case _ => false + } + + //######################################################################## + // Private data + private var content = new Array[Byte](0) + + //######################################################################## + // Public Methods + def absolute = this + + /** Returns null. */ + final def file: JFile = null + + override def sizeOption: Option[Int] = Some(content.size) + + def input : InputStream = new ByteArrayInputStream(content); + + override def output: OutputStream = { + new ByteArrayOutputStream() { + override def close() { + super.close() + content = toByteArray() + } + } + } + + def container: AbstractFile = unsupported + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = false + + /** Returns the time that this abstract file was last modified. */ + private var _lastModified: Long = 0 + def lastModified: Long = _lastModified + def lastModified_=(x: Long) = _lastModified = x + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] = { + assert(isDirectory, "not a directory '" + this + "'") + Iterator.empty + } + + /** Does this abstract file denote an existing file? */ + def create() { unsupported } + + /** Delete the underlying file or directory (recursively). */ + def delete() { unsupported } + + /** + * Returns the abstract file in this abstract directory with the + * specified name. If there is no such file, returns null. The + * argument "directory" tells whether to look for a directory or + * or a regular file. + * + * @param name ... + * @param directory ... + * @return ... + */ + def lookupName(name: String, directory: Boolean): AbstractFile = { + assert(isDirectory, "not a directory '" + this + "'") + null + } + + /** Returns an abstract file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean) = unsupported + + //######################################################################## +} diff --git a/src/reflect/scala/tools/nsc/io/ZipArchive.scala b/src/reflect/scala/tools/nsc/io/ZipArchive.scala new file mode 100644 index 0000000000..766b1fd093 --- /dev/null +++ b/src/reflect/scala/tools/nsc/io/ZipArchive.scala @@ -0,0 +1,220 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package io + +import java.net.URL +import java.io.{ IOException, InputStream, ByteArrayInputStream } +import java.io.{ File => JFile } +import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream } +import scala.collection.{ immutable, mutable } +import annotation.tailrec + +/** An abstraction for zip files and streams. Everything is written the way + * it is for performance: we come through here a lot on every run. Be careful + * about changing it. + * + * @author Philippe Altherr (original version) + * @author Paul Phillips (this one) + * @version 2.0, + */ +object ZipArchive { + def fromPath(path: String): FileZipArchive = fromFile(new JFile(path)) + def fromPath(path: Path): FileZipArchive = fromFile(path.toFile) + + /** + * @param file a File + * @return A ZipArchive if `file` is a readable zip file, otherwise null. + */ + def fromFile(file: File): FileZipArchive = fromFile(file.jfile) + def fromFile(file: JFile): FileZipArchive = + try { new FileZipArchive(file) } + catch { case _: IOException => null } + + /** + * @param url the url of a zip file + * @return A ZipArchive backed by the given url. + */ + def fromURL(url: URL): URLZipArchive = new URLZipArchive(url) + def fromURL(url: String): URLZipArchive = fromURL(new URL(url)) + + private def dirName(path: String) = splitPath(path, true) + private def baseName(path: String) = splitPath(path, false) + private def splitPath(path0: String, front: Boolean): String = { + val isDir = path0.charAt(path0.length - 1) == '/' + val path = if (isDir) path0.substring(0, path0.length - 1) else path0 + val idx = path.lastIndexOf('/') + + if (idx < 0) + if (front) "/" + else path + else + if (front) path.substring(0, idx + 1) + else path.substring(idx + 1) + } +} +import ZipArchive._ + +abstract class ZipArchive(override val file: JFile) extends AbstractFile with Equals { + self => + + override def underlyingSource = Some(this) + def isDirectory = true + def lookupName(name: String, directory: Boolean) = unsupported + def lookupNameUnchecked(name: String, directory: Boolean) = unsupported + def create() = unsupported + def delete() = unsupported + def output = unsupported + def container = unsupported + def absolute = unsupported + + private def walkIterator(its: Iterator[AbstractFile]): Iterator[AbstractFile] = { + its flatMap { f => + if (f.isDirectory) walkIterator(f.iterator) + else Iterator(f) + } + } + def deepIterator = walkIterator(iterator) + + sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) { + // have to keep this name for compat with sbt's compiler-interface + def getArchive: ZipFile = null + override def underlyingSource = Some(self) + override def toString = self.path + "(" + path + ")" + } + class DirEntry(path: String) extends Entry(path) { + val entries = mutable.HashMap[String, Entry]() + + override def isDirectory = true + override def iterator: Iterator[Entry] = entries.valuesIterator + override def lookupName(name: String, directory: Boolean): Entry = { + if (directory) entries(name + "/") + else entries(name) + } + } + + private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = { + dirs.getOrElseUpdate(path, { + val parent = ensureDir(dirs, dirName(path), null) + val dir = new DirEntry(path) + parent.entries(baseName(path)) = dir + dir + }) + } + protected def getDir(dirs: mutable.Map[String, DirEntry], entry: ZipEntry): DirEntry = { + if (entry.isDirectory) ensureDir(dirs, entry.getName, entry) + else ensureDir(dirs, dirName(entry.getName), null) + } +} + +final class FileZipArchive(file: JFile) extends ZipArchive(file) { + def iterator: Iterator[Entry] = { + val zipFile = new ZipFile(file) + val root = new DirEntry("/") + val dirs = mutable.HashMap[String, DirEntry]("/" -> root) + val enum = zipFile.entries() + + while (enum.hasMoreElements) { + val zipEntry = enum.nextElement + val dir = getDir(dirs, zipEntry) + if (zipEntry.isDirectory) dir + else { + class FileEntry() extends Entry(zipEntry.getName) { + override def getArchive = zipFile + override def lastModified = zipEntry.getTime() + override def input = getArchive getInputStream zipEntry + override def sizeOption = Some(zipEntry.getSize().toInt) + } + val f = new FileEntry() + dir.entries(f.name) = f + } + } + + try root.iterator + finally dirs.clear() + } + + def name = file.getName + def path = file.getPath + def input = File(file).inputStream() + def lastModified = file.lastModified + + override def sizeOption = Some(file.length.toInt) + override def canEqual(other: Any) = other.isInstanceOf[FileZipArchive] + override def hashCode() = file.hashCode + override def equals(that: Any) = that match { + case x: FileZipArchive => file.getAbsoluteFile == x.file.getAbsoluteFile + case _ => false + } +} + +final class URLZipArchive(val url: URL) extends ZipArchive(null) { + def iterator: Iterator[Entry] = { + val root = new DirEntry("/") + val dirs = mutable.HashMap[String, DirEntry]("/" -> root) + val in = new ZipInputStream(new ByteArrayInputStream(Streamable.bytes(input))) + + @tailrec def loop() { + val zipEntry = in.getNextEntry() + class EmptyFileEntry() extends Entry(zipEntry.getName) { + override def toByteArray: Array[Byte] = null + override def sizeOption = Some(0) + } + class FileEntry() extends Entry(zipEntry.getName) { + override val toByteArray: Array[Byte] = { + val len = zipEntry.getSize().toInt + val arr = new Array[Byte](len) + var offset = 0 + + def loop() { + if (offset < len) { + val read = in.read(arr, offset, len - offset) + if (read >= 0) { + offset += read + loop() + } + } + } + loop() + + if (offset == arr.length) arr + else throw new IOException("Input stream truncated: read %d of %d bytes".format(offset, len)) + } + override def sizeOption = Some(zipEntry.getSize().toInt) + } + + if (zipEntry != null) { + val dir = getDir(dirs, zipEntry) + if (zipEntry.isDirectory) + dir + else { + val f = if (zipEntry.getSize() == 0) new EmptyFileEntry() else new FileEntry() + dir.entries(f.name) = f + } + in.closeEntry() + loop() + } + } + + loop() + try root.iterator + finally dirs.clear() + } + + def name = url.getFile() + def path = url.getPath() + def input = url.openStream() + def lastModified = + try url.openConnection().getLastModified() + catch { case _: IOException => 0 } + + override def canEqual(other: Any) = other.isInstanceOf[URLZipArchive] + override def hashCode() = url.hashCode + override def equals(that: Any) = that match { + case x: URLZipArchive => url == x.url + case _ => false + } +} |