diff options
Diffstat (limited to 'src/reflect')
162 files changed, 18560 insertions, 10219 deletions
diff --git a/src/reflect/scala/reflect/api/Annotations.scala b/src/reflect/scala/reflect/api/Annotations.scala index 09eaf7afb4..b880fad756 100644 --- a/src/reflect/scala/reflect/api/Annotations.scala +++ b/src/reflect/scala/reflect/api/Annotations.scala @@ -1,14 +1,15 @@ -package scala.reflect +package scala +package reflect package api import scala.collection.immutable.ListMap /** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * This trait provides annotation support for the reflection API. + * This trait provides annotation support for the reflection API. * - * The API distinguishes between two kinds of annotations: + * In Scala, annotations belong to one of the two categories: * * <ul> * <li>''Java annotations'': annotations on definitions produced by the Java compiler, i.e., subtypes of [[java.lang.annotation.Annotation]] @@ -21,16 +22,13 @@ import scala.collection.immutable.ListMap * it is stored as special attributes in the corresponding classfile, and not as a Java annotation. Note that subclassing * just [[scala.annotation.Annotation]] is not enough to have the corresponding metadata persisted for runtime reflection. * - * The distinction between Java and Scala annotations is manifested in the contract of [[scala.reflect.api.Annotations#Annotation]], which exposes - * both `scalaArgs` and `javaArgs`. For Scala or Java annotations extending [[scala.annotation.ClassfileAnnotation]] `scalaArgs` is empty - * and arguments are stored in `javaArgs`. For all other Scala annotations, arguments are stored in `scalaArgs` and `javaArgs` is empty. + * Both Java and Scala annotations are represented as typed trees carrying constructor invocations corresponding + * to the annotation. For instance, the annotation in `@ann(1, 2) class C` is represented as `q"@new ann(1, 2)"`. * - * Arguments in `scalaArgs` are represented as typed trees. Note that these trees are not transformed by any phases - * following the type-checker. Arguments in `javaArgs` are repesented as a map from [[scala.reflect.api.Names#Name]] to - * [[scala.reflect.api.Annotations#JavaArgument]]. Instances of `JavaArgument` represent different kinds of Java annotation arguments: - * - literals (primitive and string constants), - * - arrays and - * - nested annotations. + * Unlike Java reflection, Scala reflection does not support evaluation of constructor invocations stored in annotations + * into underlying objects. For instance it's impossible to go from `@ann(1, 2) class C` to `ann(1, 2)`, so one + * has to analyze trees representing annotation arguments to manually extract corresponding values. Towards that end, + * arguments of an annotation can be obtained via `annotation.tree.children.tail`. * * For more information about `Annotation`s, see the [[http://docs.scala-lang.org/overviews/reflection/annotations-names-scopes.html Reflection Guide: Annotations, Names, Scopes, and More]] * @@ -45,23 +43,22 @@ trait Annotations { self: Universe => */ type Annotation >: Null <: AnyRef with AnnotationApi - /** A tag that preserves the identity of the `Annotation` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** The constructor/extractor for `Annotation` instances. + * @group Extractors */ - implicit val AnnotationTag: ClassTag[Annotation] - - /** The constructor/extractor for `Annotation` instances. - * @group Extractors - */ - val Annotation: AnnotationExtractor + val Annotation: AnnotationExtractor /** An extractor class to create and pattern match with syntax `Annotation(tpe, scalaArgs, javaArgs)`. * Here, `tpe` is the annotation type, `scalaArgs` the payload of Scala annotations, and `javaArgs` the payload of Java annotations. * @group Extractors */ abstract class AnnotationExtractor { + def apply(tree: Tree): Annotation = treeToAnnotation(tree) + + @deprecated("Use `apply(tree: Tree): Annotation` instead", "2.11.0") def apply(tpe: Type, scalaArgs: List[Tree], javaArgs: ListMap[Name, JavaArgument]): Annotation + + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def unapply(ann: Annotation): Option[(Type, List[Tree], ListMap[Name, JavaArgument])] } @@ -70,55 +67,64 @@ trait Annotations { self: Universe => * @group API */ trait AnnotationApi { + /** The tree underlying the annotation. */ + def tree: Tree = annotationToTree(this.asInstanceOf[Annotation]) + /** The type of the annotation. */ + @deprecated("Use `tree.tpe` instead", "2.11.0") def tpe: Type /** Payload of the Scala annotation: a list of abstract syntax trees that represent the argument. * Empty for Java annotations. */ + @deprecated("Use `tree.children.tail` instead", "2.11.0") def scalaArgs: List[Tree] /** Payload of the Java annotation: a list of name-value pairs. * Empty for Scala annotations. */ + @deprecated("Use `tree.children.tail` instead", "2.11.0") def javaArgs: ListMap[Name, JavaArgument] } + protected[scala] def annotationToTree(ann: Annotation): Tree + protected[scala] def treeToAnnotation(tree: Tree): Annotation + /** A Java annotation argument * @template * @group Annotations */ - type JavaArgument >: Null <: AnyRef + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") + type JavaArgument >: Null <: AnyRef with JavaArgumentApi - /** A tag that preserves the identity of the `JavaArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** Has no special methods. Is here to provides erased identity for `CompoundType`. + * @group API */ - implicit val JavaArgumentTag: ClassTag[JavaArgument] + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") + trait JavaArgumentApi /** A literal argument to a Java annotation as `"Use X instead"` in `@Deprecated("Use X instead")` * @template * @group Annotations */ - type LiteralArgument >: Null <: AnyRef with JavaArgument with LiteralArgumentApi - - /** A tag that preserves the identity of the `LiteralArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LiteralArgumentTag: ClassTag[LiteralArgument] + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") + type LiteralArgument >: Null <: LiteralArgumentApi with JavaArgument /** The constructor/extractor for `LiteralArgument` instances. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") val LiteralArgument: LiteralArgumentExtractor /** An extractor class to create and pattern match with syntax `LiteralArgument(value)` * where `value` is the constant argument. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") abstract class LiteralArgumentExtractor { + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def apply(value: Constant): LiteralArgument + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def unapply(arg: LiteralArgument): Option[Constant] } @@ -126,8 +132,10 @@ trait Annotations { self: Universe => * The main source of information about annotations is the [[scala.reflect.api.Annotations]] page. * @group API */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") trait LiteralArgumentApi { /** The underlying compile-time constant value. */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def value: Constant } @@ -135,25 +143,24 @@ trait Annotations { self: Universe => * @template * @group Annotations */ - type ArrayArgument >: Null <: AnyRef with JavaArgument with ArrayArgumentApi - - /** A tag that preserves the identity of the `ArrayArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ArrayArgumentTag: ClassTag[ArrayArgument] + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") + type ArrayArgument >: Null <: ArrayArgumentApi with JavaArgument /** The constructor/extractor for `ArrayArgument` instances. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") val ArrayArgument: ArrayArgumentExtractor /** An extractor class to create and pattern match with syntax `ArrayArgument(args)` * where `args` is the argument array. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") abstract class ArrayArgumentExtractor { + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def apply(args: Array[JavaArgument]): ArrayArgument + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def unapply(arg: ArrayArgument): Option[Array[JavaArgument]] } @@ -161,8 +168,10 @@ trait Annotations { self: Universe => * The main source of information about annotations is the [[scala.reflect.api.Annotations]] page. * @group API */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") trait ArrayArgumentApi { /** The underlying array of Java annotation arguments. */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def args: Array[JavaArgument] } @@ -170,25 +179,24 @@ trait Annotations { self: Universe => * @template * @group Annotations */ - type NestedArgument >: Null <: AnyRef with JavaArgument with NestedArgumentApi - - /** A tag that preserves the identity of the `NestedArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NestedArgumentTag: ClassTag[NestedArgument] + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") + type NestedArgument >: Null <: NestedArgumentApi with JavaArgument /** The constructor/extractor for `NestedArgument` instances. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") val NestedArgument: NestedArgumentExtractor /** An extractor class to create and pattern match with syntax `NestedArgument(annotation)` * where `annotation` is the nested annotation. * @group Extractors */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") abstract class NestedArgumentExtractor { + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def apply(annotation: Annotation): NestedArgument + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def unapply(arg: NestedArgument): Option[Annotation] } @@ -196,8 +204,10 @@ trait Annotations { self: Universe => * The main source of information about annotations is the [[scala.reflect.api.Annotations]] page. * @group API */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") trait NestedArgumentApi { /** The underlying nested annotation. */ + @deprecated("Use `Annotation.tree` to inspect annotation arguments", "2.11.0") def annotation: Annotation } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala deleted file mode 100644 index 0c8e81a220..0000000000 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ /dev/null @@ -1,78 +0,0 @@ -package scala.reflect -package api - -/** - * This is an internal implementation class. - * @groupname TreeBuilders Tree Building - */ -private[reflect] trait BuildUtils { self: Universe => - - /** @group TreeBuilders */ - val build: BuildApi - - // this API abstracts away the functionality necessary for reification - // it's too gimmicky and unstructured to be exposed directly in the universe - // but we need it in a publicly available place for reification to work - - /** @group TreeBuilders */ - abstract class BuildApi { - /** Selects type symbol with given simple name `name` from the defined members of `owner`. - */ - def selectType(owner: Symbol, name: String): TypeSymbol - - /** Selects term symbol with given name and type from the defined members of prefix type - */ - def selectTerm(owner: Symbol, name: String): TermSymbol - - /** Selects overloaded method symbol with given name and index - */ - def selectOverloadedMethod(owner: Symbol, name: String, index: Int): MethodSymbol - - /** A fresh symbol with given name `name`, position `pos` and flags `flags` that has - * the current symbol as its owner. - */ - def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: FlagSet, isClass: Boolean): Symbol - - /** Create a fresh free term symbol. - * @param name the name of the free variable - * @param value the value of the free variable at runtime - * @param flags (optional) flags of the free variable - * @param origin debug information that tells where this symbol comes from - */ - def newFreeTerm(name: String, value: => Any, flags: FlagSet = NoFlags, origin: String = null): FreeTermSymbol - - /** Create a fresh free type symbol. - * @param name the name of the free variable - * @param flags (optional) flags of the free variable - * @param origin debug information that tells where this symbol comes from - */ - def newFreeType(name: String, flags: FlagSet = NoFlags, origin: String = null): FreeTypeSymbol - - /** Set symbol's type signature to given type. - * @return the symbol itself - */ - def setTypeSignature[S <: Symbol](sym: S, tpe: Type): S - - /** Set symbol's annotations to given annotations `annots`. - */ - def setAnnotations[S <: Symbol](sym: S, annots: List[Annotation]): S - - def flagsFromBits(bits: Long): FlagSet - - def emptyValDef: ValDef - - def This(sym: Symbol): Tree - - def Select(qualifier: Tree, sym: Symbol): Select - - def Ident(sym: Symbol): Ident - - def TypeTree(tp: Type): TypeTree - - def thisPrefix(sym: Symbol): Type - - def setType[T <: Tree](tree: T, tpe: Type): T - - def setSymbol[T <: Tree](tree: T, sym: Symbol): T - } -} diff --git a/src/reflect/scala/reflect/api/Constants.scala b/src/reflect/scala/reflect/api/Constants.scala index f3d75c3c00..e73c5ffa91 100644 --- a/src/reflect/scala/reflect/api/Constants.scala +++ b/src/reflect/scala/reflect/api/Constants.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api /** @@ -68,7 +69,7 @@ package api * val enumRef = jarg("enumRef").symbolValue * println(enumRef) // value BAR * - * val siblings = enumRef.owner.typeSignature.declarations + * val siblings = enumRef.owner.info.decls * val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic) * println(enumValues) // Scope{ * // final val FOO: JavaSimpleEnumeration; @@ -164,7 +165,7 @@ trait Constants { * // ideally one should match instead of casting * println(enumRef) // value BAR * - * val siblings = enumRef.owner.typeSignature.declarations + * val siblings = enumRef.owner.info.decls * val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic) * println(enumValues) // Scope{ * // final val FOO: JavaSimpleEnumeration; @@ -183,12 +184,6 @@ trait Constants { */ type Constant >: Null <: AnyRef with ConstantApi - /** A tag that preserves the identity of the `Constant` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ConstantTag: ClassTag[Constant] - /** The constructor/extractor for `Constant` instances. * @group Extractors */ diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 2ba18a8207..5b6ff2325c 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -3,10 +3,12 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api import scala.reflect.runtime.{universe => ru} +import scala.annotation.compileTimeOnly /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> @@ -90,7 +92,7 @@ trait Exprs { self: Universe => * }}} * because expr of type Expr[T] itself does not have a method foo. */ - // @compileTimeOnly("Cannot use splice outside reify") + @compileTimeOnly("splice must be enclosed within a reify {} block") def splice: T /** @@ -107,7 +109,7 @@ trait Exprs { self: Universe => * object Impls { def foo_impl(c: Context)(x: c.Expr[X]): c.Expr[x.value.T] = ... } * }}} */ - // @compileTimeOnly("Cannot use value except for signatures of macro implementations") + @compileTimeOnly("cannot use value except for signatures of macro implementations") val value: T override def canEqual(x: Any) = x.isInstanceOf[Expr[_]] @@ -174,4 +176,4 @@ private[scala] class SerializedExpr(var treec: TreeCreator, var tag: ru.WeakType import ru._ Expr(rootMirror, treec)(tag) } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala index 4357aec9c9..bf4d6353df 100644 --- a/src/reflect/scala/reflect/api/FlagSets.scala +++ b/src/reflect/scala/reflect/api/FlagSets.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api import scala.language.implicitConversions @@ -61,12 +62,6 @@ trait FlagSets { self: Universe => */ type FlagSet - /** A tag that preserves the identity of the `FlagSet` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FlagSetTag: ClassTag[FlagSet] - /** The API of `FlagSet` instances. * The main source of information about flag sets is the [[scala.reflect.api.FlagSets]] page. * @group Flags @@ -136,8 +131,8 @@ trait FlagSets { self: Universe => /** Flag indicating that tree has `protected` modifier set */ val PROTECTED: FlagSet - /** Flag indicating that tree represents a member local to current class - * (i.e. private[this] or protected[this]. + /** Flag indicating that tree represents a member local to current class, + * i.e. private[this] or protected[this]. * This requires having either PRIVATE or PROTECTED set as well. */ val LOCAL: FlagSet @@ -171,6 +166,90 @@ trait FlagSets { self: Universe => /** Flag indicating that tree represents a variable or a member initialized to the default value */ val DEFAULTINIT: FlagSet + + /** Flag indicating that tree represents an enum. + * + * It can only appear at + * - the enum's class + * - enum constants + **/ + val ENUM: FlagSet + + /** Flag indicating that tree represents a parameter of the primary constructor of some class + * or a synthetic member underlying thereof. E.g. here's how 'class C(val x: Int)' is represented: + * + * [[syntax trees at end of parser]]// Scala source: tmposDU52 + * class C extends scala.AnyRef { + * <paramaccessor> val x: Int = _; + * def <init>(x: Int) = { + * super.<init>(); + * () + * } + * } + * ClassDef( + * Modifiers(), TypeName("C"), List(), + * Template( + * List(Select(Ident(scala), TypeName("AnyRef"))), + * noSelfType, + * List( + * ValDef(Modifiers(PARAMACCESSOR), TermName("x"), Ident(TypeName("Int")), EmptyTree), + * DefDef( + * Modifiers(), nme.CONSTRUCTOR, List(), + * List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("x"), Ident(TypeName("Int")), EmptyTree))), TypeTree(), + * Block(List(pendingSuperCall), Literal(Constant(()))))))))) + */ + val PARAMACCESSOR: FlagSet + + /** Flag indicating that tree represents a parameter of the primary constructor of some case class + * or a synthetic member underlying thereof. E.g. here's how 'case class C(val x: Int)' is represented: + * + * [[syntax trees at end of parser]]// Scala source: tmpnHkJ3y + * case class C extends scala.Product with scala.Serializable { + * <caseaccessor> <paramaccessor> val x: Int = _; + * def <init>(x: Int) = { + * super.<init>(); + * () + * } + * } + * ClassDef( + * Modifiers(CASE), TypeName("C"), List(), + * Template( + * List(Select(Ident(scala), TypeName("Product")), Select(Ident(scala), TypeName("Serializable"))), + * noSelfType, + * List( + * ValDef(Modifiers(CASEACCESSOR | PARAMACCESSOR), TermName("x"), Ident(TypeName("Int")), EmptyTree), + * DefDef( + * Modifiers(), nme.CONSTRUCTOR, List(), + * List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("x"), Ident(TypeName("Int")), EmptyTree))), TypeTree(), + * Block(List(pendingSuperCall), Literal(Constant(()))))))))) + */ + val CASEACCESSOR: FlagSet + + /** Flag used to distinguish programmatically generated definitions from user-written ones. + * @see ARTIFACT + */ + val SYNTHETIC: FlagSet + + /** Flag used to distinguish platform-specific implementation details. + * Trees and symbols which are currently marked ARTIFACT by scalac: + * * $outer fields and accessors + * * super accessors + * * protected accessors + * * lazy local accessors + * * bridge methods + * * default argument getters + * * evaluation-order preserving locals for right-associative and out-of-order named arguments + * * catch-expression storing vals + * * anything else which feels a setFlag(ARTIFACT) + * + * @see SYNTHETIC + */ + val ARTIFACT: FlagSet + + /** Flag that indicates methods that are supposed to be stable + * (e.g. synthetic getters of valdefs). + */ + val STABLE: FlagSet } /** The empty set of flags diff --git a/src/reflect/scala/reflect/api/ImplicitTags.scala b/src/reflect/scala/reflect/api/ImplicitTags.scala new file mode 100644 index 0000000000..aca0692d0d --- /dev/null +++ b/src/reflect/scala/reflect/api/ImplicitTags.scala @@ -0,0 +1,119 @@ +package scala +package reflect +package api + +/** Tags which preserve the identity of abstract types in the face of erasure. + * Can be used for pattern matching, instance tests, serialization and the like. + * @group Tags + */ +trait ImplicitTags { + self: Universe => + + // Tags for Types. + implicit val AnnotatedTypeTag: ClassTag[AnnotatedType] + implicit val BoundedWildcardTypeTag: ClassTag[BoundedWildcardType] + implicit val ClassInfoTypeTag: ClassTag[ClassInfoType] + implicit val CompoundTypeTag: ClassTag[CompoundType] + implicit val ConstantTypeTag: ClassTag[ConstantType] + implicit val ExistentialTypeTag: ClassTag[ExistentialType] + implicit val MethodTypeTag: ClassTag[MethodType] + implicit val NullaryMethodTypeTag: ClassTag[NullaryMethodType] + implicit val PolyTypeTag: ClassTag[PolyType] + implicit val RefinedTypeTag: ClassTag[RefinedType] + implicit val SingleTypeTag: ClassTag[SingleType] + implicit val SingletonTypeTag: ClassTag[SingletonType] + implicit val SuperTypeTag: ClassTag[SuperType] + implicit val ThisTypeTag: ClassTag[ThisType] + implicit val TypeBoundsTag: ClassTag[TypeBounds] + implicit val TypeRefTag: ClassTag[TypeRef] + implicit val TypeTagg: ClassTag[Type] + + // Tags for Names. + implicit val NameTag: ClassTag[Name] + implicit val TermNameTag: ClassTag[TermName] + implicit val TypeNameTag: ClassTag[TypeName] + + // Tags for Scopes. + implicit val ScopeTag: ClassTag[Scope] + implicit val MemberScopeTag: ClassTag[MemberScope] + + // Tags for Annotations. + implicit val AnnotationTag: ClassTag[Annotation] + implicit val JavaArgumentTag: ClassTag[JavaArgument] + implicit val LiteralArgumentTag: ClassTag[LiteralArgument] + implicit val ArrayArgumentTag: ClassTag[ArrayArgument] + implicit val NestedArgumentTag: ClassTag[NestedArgument] + + // Tags for Symbols. + implicit val TermSymbolTag: ClassTag[TermSymbol] + implicit val MethodSymbolTag: ClassTag[MethodSymbol] + implicit val SymbolTag: ClassTag[Symbol] + implicit val TypeSymbolTag: ClassTag[TypeSymbol] + implicit val ModuleSymbolTag: ClassTag[ModuleSymbol] + implicit val ClassSymbolTag: ClassTag[ClassSymbol] + + // Tags for misc Tree relatives. + implicit val PositionTag: ClassTag[Position] + implicit val ConstantTag: ClassTag[Constant] + implicit val FlagSetTag: ClassTag[FlagSet] + implicit val ModifiersTag: ClassTag[Modifiers] + + // Tags for Trees. WTF. + implicit val AlternativeTag: ClassTag[Alternative] + implicit val AnnotatedTag: ClassTag[Annotated] + implicit val AppliedTypeTreeTag: ClassTag[AppliedTypeTree] + implicit val ApplyTag: ClassTag[Apply] + implicit val AssignOrNamedArgTag: ClassTag[AssignOrNamedArg] + implicit val AssignTag: ClassTag[Assign] + implicit val BindTag: ClassTag[Bind] + implicit val BlockTag: ClassTag[Block] + implicit val CaseDefTag: ClassTag[CaseDef] + implicit val ClassDefTag: ClassTag[ClassDef] + implicit val CompoundTypeTreeTag: ClassTag[CompoundTypeTree] + implicit val DefDefTag: ClassTag[DefDef] + implicit val DefTreeTag: ClassTag[DefTree] + implicit val ExistentialTypeTreeTag: ClassTag[ExistentialTypeTree] + implicit val FunctionTag: ClassTag[Function] + implicit val GenericApplyTag: ClassTag[GenericApply] + implicit val IdentTag: ClassTag[Ident] + implicit val IfTag: ClassTag[If] + implicit val ImplDefTag: ClassTag[ImplDef] + implicit val ImportSelectorTag: ClassTag[ImportSelector] + implicit val ImportTag: ClassTag[Import] + implicit val LabelDefTag: ClassTag[LabelDef] + implicit val LiteralTag: ClassTag[Literal] + implicit val MatchTag: ClassTag[Match] + implicit val MemberDefTag: ClassTag[MemberDef] + implicit val ModuleDefTag: ClassTag[ModuleDef] + implicit val NameTreeTag: ClassTag[NameTree] + implicit val NewTag: ClassTag[New] + implicit val PackageDefTag: ClassTag[PackageDef] + implicit val RefTreeTag: ClassTag[RefTree] + implicit val ReturnTag: ClassTag[Return] + implicit val SelectFromTypeTreeTag: ClassTag[SelectFromTypeTree] + implicit val SelectTag: ClassTag[Select] + implicit val SingletonTypeTreeTag: ClassTag[SingletonTypeTree] + implicit val StarTag: ClassTag[Star] + implicit val SuperTag: ClassTag[Super] + implicit val SymTreeTag: ClassTag[SymTree] + implicit val TemplateTag: ClassTag[Template] + implicit val TermTreeTag: ClassTag[TermTree] + implicit val ThisTag: ClassTag[This] + implicit val ThrowTag: ClassTag[Throw] + implicit val TreeTag: ClassTag[Tree] + implicit val TryTag: ClassTag[Try] + implicit val TypTreeTag: ClassTag[TypTree] + implicit val TypeApplyTag: ClassTag[TypeApply] + implicit val TypeBoundsTreeTag: ClassTag[TypeBoundsTree] + implicit val TypeDefTag: ClassTag[TypeDef] + implicit val TypeTreeTag: ClassTag[TypeTree] + implicit val TypedTag: ClassTag[Typed] + implicit val UnApplyTag: ClassTag[UnApply] + implicit val ValDefTag: ClassTag[ValDef] + implicit val ValOrDefDefTag: ClassTag[ValOrDefDef] + + // Miscellaneous + implicit val TreeCopierTag: ClassTag[TreeCopier] + implicit val RuntimeClassTag: ClassTag[RuntimeClass] + implicit val MirrorTag: ClassTag[Mirror] +} diff --git a/src/reflect/scala/reflect/api/Importers.scala b/src/reflect/scala/reflect/api/Importers.scala deleted file mode 100644 index afc4f2f25d..0000000000 --- a/src/reflect/scala/reflect/api/Importers.scala +++ /dev/null @@ -1,103 +0,0 @@ -package scala.reflect -package api - -/** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> - * - * This trait provides support for importers, a facility to migrate reflection artifacts between universes. - * ''Note: this trait should typically be used only rarely.'' - * - * Reflection artifacts, such as [[scala.reflect.api.Symbols Symbols]] and [[scala.reflect.api.Types Types]], - * are contained in [[scala.reflect.api.Universes Universe]]s. Typically all processing happens - * within a single `Universe` (e.g. a compile-time macro `Universe` or a runtime reflection `Universe`), but sometimes - * there is a need to migrate artifacts from one `Universe` to another. For example, runtime compilation works by - * importing runtime reflection trees into a runtime compiler universe, compiling the importees and exporting the - * result back. - * - * Reflection artifacts are firmly grounded in their `Universe`s, which is reflected by the fact that types of artifacts - * from different universes are not compatible. By using `Importer`s, however, they be imported from one universe - * into another. For example, to import `foo.bar.Baz` from the source `Universe` to the target `Universe`, - * an importer will first check whether the entire owner chain exists in the target `Universe`. - * If it does, then nothing else will be done. Otherwise, the importer will recreate the entire owner chain - * and will import the corresponding type signatures into the target `Universe`. - * - * Since importers match `Symbol` tables of the source and the target `Universe`s using plain string names, - * it is programmer's responsibility to make sure that imports don't distort semantics, e.g., that - * `foo.bar.Baz` in the source `Universe` means the same that `foo.bar.Baz` does in the target `Universe`. - * - * === Example === - * - * Here's how one might implement a macro that performs compile-time evaluation of its argument - * by using a runtime compiler to compile and evaluate a tree that belongs to a compile-time compiler: - * - * {{{ - * def staticEval[T](x: T) = macro staticEval[T] - * - * def staticEval[T](c: scala.reflect.macros.Context)(x: c.Expr[T]) = { - * // creates a runtime reflection universe to host runtime compilation - * import scala.reflect.runtime.{universe => ru} - * val mirror = ru.runtimeMirror(c.libraryClassLoader) - * import scala.tools.reflect.ToolBox - * val toolBox = mirror.mkToolBox() - * - * // runtime reflection universe and compile-time macro universe are different - * // therefore an importer is needed to bridge them - * // currently mkImporter requires a cast to correctly assign the path-dependent types - * val importer0 = ru.mkImporter(c.universe) - * val importer = importer0.asInstanceOf[ru.Importer { val from: c.universe.type }] - * - * // the created importer is used to turn a compiler tree into a runtime compiler tree - * // both compilers use the same classpath, so semantics remains intact - * val imported = importer.importTree(tree) - * - * // after the tree is imported, it can be evaluated as usual - * val tree = toolBox.resetAllAttrs(imported.duplicate) - * val valueOfX = toolBox.eval(imported).asInstanceOf[T] - * ... - * } - * }}} - * - * @group ReflectionAPI - */ -trait Importers { self: Universe => - - /** Creates an importer that moves reflection artifacts between universes. - * @group Importers - */ - def mkImporter(from0: Universe): Importer { val from: from0.type } - - /** The API of importers. - * The main source of information about importers is the [[scala.reflect.api.Importers]] page. - * @group Importers - */ - trait Importer { - /** The source universe of reflection artifacts that will be processed. - * The target universe is universe that created this importer with `mkImporter`. - */ - val from: Universe - - /** An importer that works in reverse direction, namely: - * imports reflection artifacts from the current universe to the universe specified in `from`. - */ - val reverse: from.Importer { val from: self.type } - - /** In the current universe, locates or creates a symbol that corresponds to the provided symbol in the source universe. - * If necessary imports the owner chain, companions, type signature, annotations and attachments. - */ - def importSymbol(sym: from.Symbol): Symbol - - /** In the current universe, locates or creates a type that corresponds to the provided type in the source universe. - * If necessary imports the underlying symbols, annotations, scopes and trees. - */ - def importType(tpe: from.Type): Type - - /** In the current universe, creates a tree that corresponds to the provided tree in the source universe. - * If necessary imports the underlying symbols, types and attachments. - */ - def importTree(tree: from.Tree): Tree - - /** In the current universe, creates a position that corresponds to the provided position in the source universe. - */ - def importPosition(pos: from.Position): Position - } -}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala new file mode 100644 index 0000000000..577cd09295 --- /dev/null +++ b/src/reflect/scala/reflect/api/Internals.scala @@ -0,0 +1,1238 @@ +package scala +package reflect +package api + +import scala.language.implicitConversions +import scala.language.higherKinds + +/** + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * + * This trait assembles APIs occasionally necessary for performing low-level operations on reflection artifacts. + * See [[Internals#InternalApi]] for more information about nature, usefulness and compatibility guarantees of these APIs. + * + * @group ReflectionAPI + */ +trait Internals { self: Universe => + + /** @see [[InternalApi]] + * @group Internal + */ + val internal: Internal + + /** @see [[InternalApi]] + * @group Internal + */ + type Internal <: InternalApi + + /** Reflection API exhibits a tension inherent to experimental things: + * on the one hand we want it to grow into a beautiful and robust API, + * but on the other hand we have to deal with immaturity of underlying mechanisms + * by providing not very pretty solutions to enable important use cases. + * + * In Scala 2.10, which was our first stab at reflection API, we didn't have a systematic + * approach to dealing with this tension, sometimes exposing too much of internals (e.g. Symbol.deSkolemize) + * and sometimes exposing too little (e.g. there's still no facility to change owners, to do typing + * transformations, etc). This resulted in certain confusion with some internal APIs + * living among public ones, scaring the newcomers, and some internal APIs only available via casting, + * which requires intimate knowledge of the compiler and breaks compatibility guarantees. + * + * This led to creation of the `internal` API module for the reflection API, which + * provides advanced APIs necessary for macros that push boundaries of the state of the art, + * clearly demarcating them from the more or less straightforward rest and + * providing compatibility guarantees on par with the rest of the reflection API + * (full compatibility within minor releases, best effort towards backward compatibility within major releases, + * clear replacement path in case of rare incompatible changes in major releases). + * + * The `internal` module itself (the value that implements [[InternalApi]]) isn't defined here, + * in [[scala.reflect.api.Universe]], but is provided on per-implementation basis. Runtime API endpoint + * ([[scala.reflect.runtime.universe]]) provides `universe.compat: InternalApi`, whereas compile-time API endpoints + * (instances of [[scala.reflect.macros.Context]]) provide `c.compat: ContextInternalApi`, which extends `InternalApi` + * with additional universe-specific and context-specific functionality. + * + * @group Internal + */ + trait InternalApi { internal => + /** This is an internal implementation module. + */ + val reificationSupport: ReificationSupportApi + + /** Creates an importer that moves reflection artifacts between universes. + * @see [[Importer]] + */ + // SI-6241: move importers to a mirror + def createImporter(from0: Universe): Importer { val from: from0.type } + + /** + * Convert a [[scala.reflect.api.TypeTags#TypeTag]] to a [[scala.reflect.Manifest]]. + * + * Compiler usually generates these conversions automatically, when a type tag for a type `T` is in scope, + * and an implicit of type `Manifest[T]` is requested, but this method can also be called manually. + * For example: + * {{{ + * typeTagToManifest(scala.reflect.runtime.currentMirror, implicitly[TypeTag[String]]) + * }}} + * @group TagInterop + */ + def typeTagToManifest[T: ClassTag](mirror: Any, tag: Universe#TypeTag[T]): Manifest[T] = + throw new UnsupportedOperationException("This universe does not support tag -> manifest conversions. Use a JavaUniverse, e.g. the scala.reflect.runtime.universe.") + + /** + * Convert a [[scala.reflect.Manifest]] to a [[scala.reflect.api.TypeTags#TypeTag]]. + * + * Compiler usually generates these conversions automatically, when a manifest for a type `T` is in scope, + * and an implicit of type `TypeTag[T]` is requested, but this method can also be called manually. + * For example: + * {{{ + * manifestToTypeTag(scala.reflect.runtime.currentMirror, implicitly[Manifest[String]]) + * }}} + * @group TagInterop + */ + def manifestToTypeTag[T](mirror: Any, manifest: Manifest[T]): Universe#TypeTag[T] = + throw new UnsupportedOperationException("This universe does not support manifest -> tag conversions. Use a JavaUniverse, e.g. the scala.reflect.runtime.universe.") + + /** Create a new scope with the given initial elements. + */ + def newScopeWith(elems: Symbol*): Scope + + /** Extracts free term symbols from a tree that is reified or contains reified subtrees. + */ + def freeTerms(tree: Tree): List[FreeTermSymbol] + + /** Extracts free type symbols from a tree that is reified or contains reified subtrees. + */ + def freeTypes(tree: Tree): List[FreeTypeSymbol] + + /** Substitute symbols in `to` for corresponding occurrences of references to + * symbols `from` in this type. + */ + def substituteSymbols(tree: Tree, from: List[Symbol], to: List[Symbol]): Tree + + /** Substitute types in `to` for corresponding occurrences of references to + * symbols `from` in this tree. + */ + def substituteTypes(tree: Tree, 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(tree: Tree, clazz: Symbol, to: Tree): Tree + + /** A factory method for `ClassDef` nodes. + */ + def classDef(sym: Symbol, impl: Template): ClassDef + + /** A factory method for `ModuleDef` nodes. + */ + def moduleDef(sym: Symbol, impl: Template): ModuleDef + + /** A factory method for `ValDef` nodes. + */ + def valDef(sym: Symbol, rhs: Tree): ValDef + + /** A factory method for `ValDef` nodes. + */ + def valDef(sym: Symbol): ValDef + + /** A factory method for `DefDef` nodes. + */ + def defDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef + + /** A factory method for `DefDef` nodes. + */ + def defDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef + + /** A factory method for `DefDef` nodes. + */ + def defDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef + + /** A factory method for `DefDef` nodes. + */ + def defDef(sym: Symbol, rhs: Tree): DefDef + + /** A factory method for `DefDef` nodes. + */ + def defDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef + + /** A factory method for `TypeDef` nodes. + */ + def typeDef(sym: Symbol, rhs: Tree): TypeDef + + /** A factory method for `TypeDef` nodes. + */ + def typeDef(sym: Symbol): TypeDef + + /** A factory method for `LabelDef` nodes. + */ + def labelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef + + /** Does this symbol represent a free term captured by reification? + * If yes, `isTerm` is also guaranteed to be true. + */ + def isFreeTerm(symbol: Symbol): Boolean + + /** This symbol cast to a free term symbol. + * @throws ScalaReflectionException if `isFreeTerm` is false. + */ + def asFreeTerm(symbol: Symbol): FreeTermSymbol + + /** Does this symbol represent a free type captured by reification? + * If yes, `isType` is also guaranteed to be true. + */ + def isFreeType(symbol: Symbol): Boolean + + /** This symbol cast to a free type symbol. + * @throws ScalaReflectionException if `isFreeType` is false. + */ + def asFreeType(symbol: Symbol): FreeTypeSymbol + + def newTermSymbol(owner: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol + + def newModuleAndClassSymbol(owner: Symbol, name: Name, pos: Position = NoPosition, flags: FlagSet = NoFlags): (ModuleSymbol, ClassSymbol) + + def newMethodSymbol(owner: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): MethodSymbol + + def newTypeSymbol(owner: Symbol, name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TypeSymbol + + def newClassSymbol(owner: Symbol, name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): ClassSymbol + + def newFreeTerm(name: String, value: => Any, flags: FlagSet = NoFlags, origin: String = null): FreeTermSymbol + + def newFreeType(name: String, flags: FlagSet = NoFlags, origin: String = null): FreeTypeSymbol + + /** Does this symbol or its underlying type represent a typechecking error? + */ + def isErroneous(symbol: Symbol): Boolean + + /** Does this symbol represent the definition of a skolem? + * Skolems are used during typechecking to represent type parameters viewed from inside their scopes. + */ + def isSkolem(symbol: Symbol): Boolean + + /** If this symbol is a skolem, its corresponding type parameter, otherwise the symbol itself. + * + * [[https://groups.google.com/forum/#!msg/scala-internals/0j8laVNTQsI/kRXMF_c8bGsJ To quote Martin Odersky]], + * skolems are synthetic type "constants" that are copies of existentially bound or universally + * bound type variables. E.g. if one is inside the right-hand side of a method: + * + * {{{ + * def foo[T](x: T) = ... foo[List[T]].... + * }}} + * + * the skolem named `T` refers to the unknown type instance of `T` when `foo` is called. It needs to be different + * from the type parameter because in a recursive call as in the `foo[List[T]]` above the type parameter gets + * substituted with `List[T]`, but the ''type skolem'' stays what it is. + * + * The other form of skolem is an ''existential skolem''. Say one has a function + * + * {{{ + * def bar(xs: List[T] forSome { type T }) = xs.head + * }}} + * + * then each occurrence of `xs` on the right will have type `List[T']` where `T'` is a fresh copy of `T`. + */ + def deSkolemize(symbol: Symbol): Symbol + + /** Forces all outstanding completers associated with this symbol. + * After this call returns, the symbol becomes immutable and thread-safe. + */ + def initialize(symbol: Symbol): symbol.type + + /** Calls [[initialize]] on the owner and all the value and type parameters of the symbol. + */ + def fullyInitialize(symbol: Symbol): symbol.type + + /** Calls [[initialize]] on all the value and type parameters of the type. + */ + def fullyInitialize(tp: Type): tp.type + + /** Calls [[initialize]] on all the symbols that the scope consists of. + */ + def fullyInitialize(scope: Scope): scope.type + + /** Returns internal flags associated with the symbol. + */ + def flags(symbol: Symbol): FlagSet + + /** A creator for `ThisType` types. + */ + def thisType(sym: Symbol): Type + + /** A creator for `SingleType` types. + */ + def singleType(pre: Type, sym: Symbol): Type + + /** A creator for `SuperType` types. + */ + def superType(thistpe: Type, supertpe: Type): Type + + /** A creator for `ConstantType` types. + */ + def constantType(value: Constant): ConstantType + + /** A creator for `TypeRef` types. + */ + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type + + /** A creator for `RefinedType` types. + */ + def refinedType(parents: List[Type], decls: Scope): RefinedType + + /** A creator for `RefinedType` types. + */ + def refinedType(parents: List[Type], decls: Scope, clazz: Symbol): RefinedType + + /** A creator for `RefinedType` types. + */ + def refinedType(parents: List[Type], owner: Symbol): Type + + /** A creator for `RefinedType` types. + */ + def refinedType(parents: List[Type], owner: Symbol, decls: Scope): Type + + /** A creator for `RefinedType` types. + */ + def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): 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 `ClassInfoType` types. + */ + def classInfoType(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType + + /** A creator for `MethodType` types. + */ + def methodType(params: List[Symbol], resultType: Type): MethodType + + /** A creator for `NullaryMethodType` types. + */ + def nullaryMethodType(resultType: Type): NullaryMethodType + + /** 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): PolyType + + /** A creator for `ExistentialType` types. + */ + def existentialType(quantified: List[Symbol], underlying: Type): ExistentialType + + /** 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`. + * @group TypeCreators + */ + def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type + + /** A creator for `AnnotatedType` types. + */ + def annotatedType(annotations: List[Annotation], underlying: Type): AnnotatedType + + /** A creator for `TypeBounds` types. + */ + def typeBounds(lo: Type, hi: Type): TypeBounds + + /** A creator for `BoundedWildcardType` types. + */ + def boundedWildcardType(bounds: TypeBounds): BoundedWildcardType + + /** Syntactic conveniences for additional internal APIs for trees, symbols and types */ + type Decorators <: DecoratorApi + + /** @see [[Decorators]] */ + val decorators: Decorators + + /** @see [[Decorators]] */ + trait DecoratorApi { + /** Extension methods for trees */ + type TreeDecorator[T <: Tree] <: TreeDecoratorApi[T] + + /** @see [[TreeDecorator]] */ + implicit def treeDecorator[T <: Tree](tree: T): TreeDecorator[T] + + /** @see [[TreeDecorator]] */ + class TreeDecoratorApi[T <: Tree](val tree: T) { + /** @see [[internal.freeTerms]] */ + def freeTerms: List[FreeTermSymbol] = internal.freeTerms(tree) + + /** @see [[internal.freeTypes]] */ + def freeTypes: List[FreeTypeSymbol] = internal.freeTypes(tree) + + /** @see [[internal.substituteSymbols]] */ + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree = internal.substituteSymbols(tree, from, to) + + /** @see [[internal.substituteTypes]] */ + def substituteTypes(from: List[Symbol], to: List[Type]): Tree = internal.substituteTypes(tree, from, to) + + /** @see [[internal.substituteThis]] */ + def substituteThis(clazz: Symbol, to: Tree): Tree = internal.substituteThis(tree, clazz, to) + } + + /** Extension methods for symbols */ + type SymbolDecorator[T <: Symbol] <: SymbolDecoratorApi[T] + + /** @see [[SymbolDecorator]] */ + implicit def symbolDecorator[T <: Symbol](symbol: T): SymbolDecorator[T] + + /** @see [[SymbolDecorator]] */ + class SymbolDecoratorApi[T <: Symbol](val symbol: T) { + /** @see [[internal.isFreeTerm]] */ + def isFreeTerm: Boolean = internal.isFreeTerm(symbol) + + /** @see [[internal.asFreeTerm]] */ + def asFreeTerm: FreeTermSymbol = internal.asFreeTerm(symbol) + + /** @see [[internal.isFreeType]] */ + def isFreeType: Boolean = internal.isFreeType(symbol) + + /** @see [[internal.asFreeType]] */ + def asFreeType: FreeTypeSymbol = internal.asFreeType(symbol) + + /** @see [[internal.newTermSymbol]] */ + def newTermSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol = internal.newTermSymbol(symbol, name, pos, flags) + + /** @see [[internal.newModuleAndClassSymbol]] */ + def newModuleAndClassSymbol(name: Name, pos: Position = NoPosition, flags: FlagSet = NoFlags): (ModuleSymbol, ClassSymbol) = internal.newModuleAndClassSymbol(symbol, name, pos, flags) + + /** @see [[internal.newMethodSymbol]] */ + def newMethodSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): MethodSymbol = internal.newMethodSymbol(symbol, name, pos, flags) + + /** @see [[internal.newTypeSymbol]] */ + def newTypeSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TypeSymbol = internal.newTypeSymbol(symbol, name, pos, flags) + + /** @see [[internal.newClassSymbol]] */ + def newClassSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): ClassSymbol = internal.newClassSymbol(symbol, name, pos, flags) + + /** @see [[internal.isErroneous]] */ + def isErroneous: Boolean = internal.isErroneous(symbol) + + /** @see [[internal.isSkolem]] */ + def isSkolem: Boolean = internal.isSkolem(symbol) + + /** @see [[internal.deSkolemize]] */ + def deSkolemize: Symbol = internal.deSkolemize(symbol) + + /** @see [[internal.initialize]] */ + def initialize: T = internal.initialize(symbol) + + /** @see [[internal.fullyInitialize]] */ + def fullyInitialize: T = internal.fullyInitialize(symbol) + + /** @see [[internal.flags]] */ + def flags: FlagSet = internal.flags(symbol) + } + + /** Extension methods for types */ + type TypeDecorator[T <: Type] <: TypeDecoratorApi[T] + + /** @see [[TypeDecorator]] */ + implicit def typeDecorator[T <: Type](tp: T): TypeDecorator[T] + + /** @see [[TypeDecorator]] */ + implicit class TypeDecoratorApi[T <: Type](val tp: T) { + /** @see [[internal.fullyInitialize]] */ + def fullyInitialize: T = internal.fullyInitialize(tp) + } + } + } + + /** This is an internal implementation class. + * @group Internal + */ + // this API abstracts away the functionality necessary for reification and quasiquotes + // it's too gimmicky and unstructured to be exposed directly in the universe + // but we need it in a publicly available place for reification to work + trait ReificationSupportApi { + /** Selects type symbol with given simple name `name` from the defined members of `owner`. + */ + def selectType(owner: Symbol, name: String): TypeSymbol + + /** Selects term symbol with given name and type from the defined members of prefix type + */ + def selectTerm(owner: Symbol, name: String): TermSymbol + + /** Selects overloaded method symbol with given name and index + */ + def selectOverloadedMethod(owner: Symbol, name: String, index: Int): MethodSymbol + + /** A fresh symbol with given name `name`, position `pos` and flags `flags` that has + * the current symbol as its owner. + */ + def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: FlagSet, isClass: Boolean): Symbol + + def newScopeWith(elems: Symbol*): Scope + + /** Create a fresh free term symbol. + * @param name the name of the free variable + * @param value the value of the free variable at runtime + * @param flags (optional) flags of the free variable + * @param origin debug information that tells where this symbol comes from + */ + def newFreeTerm(name: String, value: => Any, flags: FlagSet = NoFlags, origin: String = null): FreeTermSymbol + + /** Create a fresh free type symbol. + * @param name the name of the free variable + * @param flags (optional) flags of the free variable + * @param origin debug information that tells where this symbol comes from + */ + def newFreeType(name: String, flags: FlagSet = NoFlags, origin: String = null): FreeTypeSymbol + + /** Set symbol's type signature to given type. + * @return the symbol itself + */ + def setInfo[S <: Symbol](sym: S, tpe: Type): S + + /** Set symbol's annotations to given annotations `annots`. + */ + def setAnnotations[S <: Symbol](sym: S, annots: List[Annotation]): S + + def mkThis(sym: Symbol): Tree + + def mkSelect(qualifier: Tree, sym: Symbol): Select + + def mkIdent(sym: Symbol): Ident + + def mkTypeTree(tp: Type): TypeTree + + def ThisType(sym: Symbol): Type + + def SingleType(pre: Type, sym: Symbol): Type + + def SuperType(thistpe: Type, supertpe: Type): Type + + def ConstantType(value: Constant): ConstantType + + def TypeRef(pre: Type, sym: Symbol, args: List[Type]): Type + + def RefinedType(parents: List[Type], decls: Scope, typeSymbol: Symbol): RefinedType + + def ClassInfoType(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType + + def MethodType(params: List[Symbol], resultType: Type): MethodType + + def NullaryMethodType(resultType: Type): NullaryMethodType + + def PolyType(typeParams: List[Symbol], resultType: Type): PolyType + + def ExistentialType(quantified: List[Symbol], underlying: Type): ExistentialType + + def AnnotatedType(annotations: List[Annotation], underlying: Type): AnnotatedType + + def TypeBounds(lo: Type, hi: Type): TypeBounds + + def BoundedWildcardType(bounds: TypeBounds): BoundedWildcardType + + def thisPrefix(sym: Symbol): Type + + def setType[T <: Tree](tree: T, tpe: Type): T + + def setSymbol[T <: Tree](tree: T, sym: Symbol): T + + def toStats(tree: Tree): List[Tree] + + def mkAnnotation(tree: Tree): Tree + + def mkAnnotation(trees: List[Tree]): List[Tree] + + def mkRefineStat(stat: Tree): Tree + + def mkRefineStat(stats: List[Tree]): List[Tree] + + def mkPackageStat(stat: Tree): Tree + + def mkPackageStat(stats: List[Tree]): List[Tree] + + def mkEarlyDef(defn: Tree): Tree + + def mkEarlyDef(defns: List[Tree]): List[Tree] + + def mkRefTree(qual: Tree, sym: Symbol): Tree + + def freshTermName(prefix: String): TermName + + def freshTypeName(prefix: String): TypeName + + val ImplicitParams: ImplicitParamsExtractor + + trait ImplicitParamsExtractor { + def apply(paramss: List[List[Tree]], implparams: List[Tree]): List[List[Tree]] + def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] + } + + val ScalaDot: ScalaDotExtractor + + trait ScalaDotExtractor { + def apply(name: Name): Tree + def unapply(tree: Tree): Option[Name] + } + + val FlagsRepr: FlagsReprExtractor + + trait FlagsReprExtractor { + def apply(value: Long): FlagSet + def unapply(flags: Long): Some[Long] + } + + val SyntacticTypeApplied: SyntacticTypeAppliedExtractor + val SyntacticAppliedType: SyntacticTypeAppliedExtractor + + trait SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree + def unapply(tree: Tree): Option[(Tree, List[Tree])] + } + + val SyntacticApplied: SyntacticAppliedExtractor + + trait SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree + def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] + } + + val SyntacticClassDef: SyntacticClassDefExtractor + + trait SyntacticClassDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], + constrMods: Modifiers, vparamss: List[List[Tree]], + earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]], + List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticTraitDef: SyntacticTraitDefExtractor + + trait SyntacticTraitDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], + earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], + List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticObjectDef: SyntacticObjectDefExtractor + + trait SyntacticObjectDefExtractor { + def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfType: Tree, body: List[Tree]): ModuleDef + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticPackageObjectDef: SyntacticPackageObjectDefExtractor + + trait SyntacticPackageObjectDefExtractor { + def apply(name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfType: Tree, body: List[Tree]): PackageDef + def unapply(tree: Tree): Option[(TermName, List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticTuple: SyntacticTupleExtractor + val SyntacticTupleType: SyntacticTupleExtractor + + trait SyntacticTupleExtractor { + def apply(args: List[Tree]): Tree + def unapply(tree: Tree): Option[List[Tree]] + } + + val SyntacticBlock: SyntacticBlockExtractor + + trait SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree + def unapply(tree: Tree): Option[List[Tree]] + } + + val SyntacticNew: SyntacticNewExtractor + + trait SyntacticNewExtractor { + def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): Tree + def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticFunctionType: SyntacticFunctionTypeExtractor + + trait SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree + def unapply(tree: Tree): Option[(List[Tree], Tree)] + } + + val SyntacticFunction: SyntacticFunctionExtractor + + trait SyntacticFunctionExtractor { + def apply(params: List[Tree], body: Tree): Function + + def unapply(tree: Function): Option[(List[ValDef], Tree)] + } + + val SyntacticDefDef: SyntacticDefDefExtractor + + trait SyntacticDefDefExtractor { + def apply(mods: Modifiers, name: TermName, tparams: List[Tree], + vparamss: List[List[Tree]], tpt: Tree, rhs: Tree): DefDef + + def unapply(tree: Tree): Option[(Modifiers, TermName, List[TypeDef], List[List[ValDef]], Tree, Tree)] + } + + val SyntacticValDef: SyntacticValDefExtractor + val SyntacticVarDef: SyntacticValDefExtractor + + trait SyntacticValDefExtractor { + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef + def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] + } + + val SyntacticPatDef: SyntacticPatDefExtractor + + trait SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] + } + + val SyntacticAssign: SyntacticAssignExtractor + + trait SyntacticAssignExtractor { + def apply(lhs: Tree, rhs: Tree): Tree + def unapply(tree: Tree): Option[(Tree, Tree)] + } + + val SyntacticValFrom: SyntacticValFromExtractor + + trait SyntacticValFromExtractor { + def apply(pat: Tree, rhs: Tree): Tree + def unapply(tree: Tree): Option[(Tree, Tree)] + } + + val SyntacticValEq: SyntacticValEqExtractor + + trait SyntacticValEqExtractor { + def apply(pat: Tree, rhs: Tree): Tree + def unapply(tree: Tree): Option[(Tree, Tree)] + } + + val SyntacticFilter: SyntacticFilterExtractor + + trait SyntacticFilterExtractor { + def apply(test: Tree): Tree + def unapply(tree: Tree): Option[(Tree)] + } + + val SyntacticEmptyTypeTree: SyntacticEmptyTypeTreeExtractor + + trait SyntacticEmptyTypeTreeExtractor { + def apply(): TypeTree + def unapply(tt: TypeTree): Boolean + } + + val SyntacticFor: SyntacticForExtractor + val SyntacticForYield: SyntacticForExtractor + + trait SyntacticForExtractor { + def apply(enums: List[Tree], body: Tree): Tree + def unapply(tree: Tree): Option[(List[Tree], Tree)] + } + + def UnliftListElementwise[T](unliftable: Unliftable[T]): UnliftListElementwise[T] + trait UnliftListElementwise[T] { + def unapply(lst: List[Tree]): Option[List[T]] + } + + def UnliftListOfListsElementwise[T](unliftable: Unliftable[T]): UnliftListOfListsElementwise[T] + trait UnliftListOfListsElementwise[T] { + def unapply(lst: List[List[Tree]]): Option[List[List[T]]] + } + + val SyntacticPartialFunction: SyntacticPartialFunctionExtractor + trait SyntacticPartialFunctionExtractor { + def apply(cases: List[Tree]): Match + def unapply(tree: Tree): Option[List[CaseDef]] + } + + val SyntacticMatch: SyntacticMatchExtractor + trait SyntacticMatchExtractor { + def apply(scrutinee: Tree, cases: List[Tree]): Match + def unapply(tree: Match): Option[(Tree, List[CaseDef])] + } + + val SyntacticTry: SyntacticTryExtractor + trait SyntacticTryExtractor { + def apply(block: Tree, catches: List[Tree], finalizer: Tree): Try + def unapply(tree: Try): Option[(Tree, List[CaseDef], Tree)] + } + + val SyntacticTermIdent: SyntacticTermIdentExtractor + trait SyntacticTermIdentExtractor { + def apply(name: TermName, isBackquoted: Boolean = false): Ident + def unapply(id: Ident): Option[(TermName, Boolean)] + } + + val SyntacticTypeIdent: SyntacticTypeIdentExtractor + trait SyntacticTypeIdentExtractor { + def apply(name: TypeName): Ident + def unapply(tree: Tree): Option[TypeName] + } + + val SyntacticImport: SyntacticImportExtractor + trait SyntacticImportExtractor { + def apply(expr: Tree, selectors: List[Tree]): Import + def unapply(imp: Import): Some[(Tree, List[Tree])] + } + + val SyntacticSelectType: SyntacticSelectTypeExtractor + trait SyntacticSelectTypeExtractor { + def apply(qual: Tree, name: TypeName): Select + def unapply(tree: Tree): Option[(Tree, TypeName)] + } + + val SyntacticSelectTerm: SyntacticSelectTermExtractor + trait SyntacticSelectTermExtractor { + def apply(qual: Tree, name: TermName): Select + def unapply(tree: Tree): Option[(Tree, TermName)] + } + + val SyntacticCompoundType: SyntacticCompoundTypeExtractor + trait SyntacticCompoundTypeExtractor { + def apply(parents: List[Tree], defns: List[Tree]): CompoundTypeTree + def unapply(tree: Tree): Option[(List[Tree], List[Tree])] + } + + val SyntacticSingletonType: SyntacitcSingletonTypeExtractor + trait SyntacitcSingletonTypeExtractor { + def apply(tree: Tree): SingletonTypeTree + def unapply(tree: Tree): Option[Tree] + } + + val SyntacticTypeProjection: SyntacticTypeProjectionExtractor + trait SyntacticTypeProjectionExtractor { + def apply(qual: Tree, name: TypeName): SelectFromTypeTree + def unapply(tree: Tree): Option[(Tree, TypeName)] + } + + val SyntacticAnnotatedType: SyntacticAnnotatedTypeExtractor + trait SyntacticAnnotatedTypeExtractor { + def apply(tpt: Tree, annot: Tree): Annotated + def unapply(tree: Tree): Option[(Tree, Tree)] + } + + val SyntacticExistentialType: SyntacticExistentialTypeExtractor + trait SyntacticExistentialTypeExtractor { + def apply(tpt: Tree, where: List[Tree]): ExistentialTypeTree + def unapply(tree: Tree): Option[(Tree, List[MemberDef])] + } + } + + @deprecated("Use `internal.reificationSupport` instead", "2.11.0") + val build: ReificationSupportApi + + @deprecated("Use `internal.ReificationSupportApi` instead", "2.11.0") + type BuildApi = ReificationSupportApi + + /** This trait provides support for importers, a facility to migrate reflection artifacts between universes. + * ''Note: this trait should typically be used only rarely.'' + * + * Reflection artifacts, such as [[scala.reflect.api.Symbols Symbols]] and [[scala.reflect.api.Types Types]], + * are contained in [[scala.reflect.api.Universe Universe]]s. Typically all processing happens + * within a single `Universe` (e.g. a compile-time macro `Universe` or a runtime reflection `Universe`), but sometimes + * there is a need to migrate artifacts from one `Universe` to another. For example, runtime compilation works by + * importing runtime reflection trees into a runtime compiler universe, compiling the importees and exporting the + * result back. + * + * Reflection artifacts are firmly grounded in their `Universe`s, which is reflected by the fact that types of artifacts + * from different universes are not compatible. By using `Importer`s, however, they be imported from one universe + * into another. For example, to import `foo.bar.Baz` from the source `Universe` to the target `Universe`, + * an importer will first check whether the entire owner chain exists in the target `Universe`. + * If it does, then nothing else will be done. Otherwise, the importer will recreate the entire owner chain + * and will import the corresponding type signatures into the target `Universe`. + * + * Since importers match `Symbol` tables of the source and the target `Universe`s using plain string names, + * it is programmer's responsibility to make sure that imports don't distort semantics, e.g., that + * `foo.bar.Baz` in the source `Universe` means the same that `foo.bar.Baz` does in the target `Universe`. + * + * === Example === + * + * Here's how one might implement a macro that performs compile-time evaluation of its argument + * by using a runtime compiler to compile and evaluate a tree that belongs to a compile-time compiler: + * + * {{{ + * def staticEval[T](x: T) = macro staticEval[T] + * + * def staticEval[T](c: scala.reflect.macros.blackbox.Context)(x: c.Expr[T]) = { + * // creates a runtime reflection universe to host runtime compilation + * import scala.reflect.runtime.{universe => ru} + * val mirror = ru.runtimeMirror(c.libraryClassLoader) + * import scala.tools.reflect.ToolBox + * val toolBox = mirror.mkToolBox() + * + * // runtime reflection universe and compile-time macro universe are different + * // therefore an importer is needed to bridge them + * // currently mkImporter requires a cast to correctly assign the path-dependent types + * val importer0 = ru.internal.mkImporter(c.universe) + * val importer = importer0.asInstanceOf[ru.internal.Importer { val from: c.universe.type }] + * + * // the created importer is used to turn a compiler tree into a runtime compiler tree + * // both compilers use the same classpath, so semantics remains intact + * val imported = importer.importTree(tree) + * + * // after the tree is imported, it can be evaluated as usual + * val tree = toolBox.untypecheck(imported.duplicate) + * val valueOfX = toolBox.eval(imported).asInstanceOf[T] + * ... + * } + * }}} + * + * @group Internal + */ + // SI-6241: move importers to a mirror + trait Importer { + /** The source universe of reflection artifacts that will be processed. + * The target universe is universe that created this importer with `mkImporter`. + */ + val from: Universe + + /** An importer that works in reverse direction, namely: + * imports reflection artifacts from the current universe to the universe specified in `from`. + */ + val reverse: from.Importer { val from: self.type } + + /** In the current universe, locates or creates a symbol that corresponds to the provided symbol in the source universe. + * If necessary imports the owner chain, companions, type signature, annotations and attachments. + */ + def importSymbol(sym: from.Symbol): Symbol + + /** In the current universe, locates or creates a type that corresponds to the provided type in the source universe. + * If necessary imports the underlying symbols, annotations, scopes and trees. + */ + def importType(tpe: from.Type): Type + + /** In the current universe, creates a tree that corresponds to the provided tree in the source universe. + * If necessary imports the underlying symbols, types and attachments. + */ + def importTree(tree: from.Tree): Tree + + /** In the current universe, creates a position that corresponds to the provided position in the source universe. + */ + def importPosition(pos: from.Position): Position + } + + @deprecated("Use `internal.createImporter` instead", "2.11.0") + def mkImporter(from0: Universe): Importer { val from: from0.type } = internal.createImporter(from0) + + /** Marks underlying reference to id as boxed. + * + * <b>Precondition:<\b> id must refer to a captured variable + * A reference such marked will refer to the boxed entity, no dereferencing + * with `.elem` is done on it. + * This tree node can be emitted by macros such as reify that call referenceCapturedVariable. + * It is eliminated in LambdaLift, where the boxing conversion takes place. + * @group Internal + * @template + */ + type ReferenceToBoxed >: Null <: ReferenceToBoxedApi with TermTree + + /** The constructor/extractor for `ReferenceToBoxed` instances. + * @group Internal + */ + val ReferenceToBoxed: ReferenceToBoxedExtractor + + /** An extractor class to create and pattern match with syntax `ReferenceToBoxed(ident)`. + * This AST node does not have direct correspondence to Scala code, + * and is emitted by macros to reference capture vars directly without going through `elem`. + * + * For example: + * + * var x = ... + * fun { x } + * + * Will emit: + * + * Ident(x) + * + * Which gets transformed to: + * + * Select(Ident(x), "elem") + * + * If `ReferenceToBoxed` were used instead of Ident, no transformation would be performed. + * @group Internal + */ + abstract class ReferenceToBoxedExtractor { + def apply(ident: Ident): ReferenceToBoxed + def unapply(referenceToBoxed: ReferenceToBoxed): Option[Ident] + } + + /** The API that all references support + * @group Internal + */ + trait ReferenceToBoxedApi extends TermTreeApi { this: ReferenceToBoxed => + /** The underlying reference. */ + def ident: Tree + } + + /** Tag that preserves the identity of `ReferenceToBoxed` in the face of erasure. + * Can be used for pattern matching, instance tests, serialization and the like. + * @group Internal + */ + implicit val ReferenceToBoxedTag: ClassTag[ReferenceToBoxed] + + /** The type of free terms introduced by reification. + * @group Internal + * @template + */ + type FreeTermSymbol >: Null <: FreeTermSymbolApi with TermSymbol + + /** The API of free term symbols. + * The main source of information about symbols is the [[Symbols]] page. + * + * $SYMACCESSORS + * @group Internal + */ + trait FreeTermSymbolApi extends TermSymbolApi { this: FreeTermSymbol => + /** The place where this symbol has been spawned + * + * @group FreeTerm + */ + def origin: String + + /** The valus this symbol refers to + * + * @group FreeTerm + */ + def value: Any + } + + /** Tag that preserves the identity of `FreeTermSymbol` in the face of erasure. + * Can be used for pattern matching, instance tests, serialization and the like. + * @group Internal + */ + implicit val FreeTermSymbolTag: ClassTag[FreeTermSymbol] + + /** The type of free types introduced by reification. + * @group Internal + * @template + */ + type FreeTypeSymbol >: Null <: FreeTypeSymbolApi with TypeSymbol + + /** The API of free type symbols. + * The main source of information about symbols is the [[Symbols]] page. + * + * $SYMACCESSORS + * @group Internal + */ + trait FreeTypeSymbolApi extends TypeSymbolApi { this: FreeTypeSymbol => + /** The place where this symbol has been spawned + * + * @group FreeType + */ + def origin: String + } + + /** Tag that preserves the identity of `FreeTermSymbol` in the face of erasure. + * Can be used for pattern matching, instance tests, serialization and the like. + * @group Internal + */ + implicit val FreeTypeSymbolTag: ClassTag[FreeTypeSymbol] + + /** Provides enrichments to ensure source compatibility between Scala 2.10 and Scala 2.11. + * If in your reflective program for Scala 2.10 you've used something that's now become an internal API, + * a single `compat._` import will fix things for you. + * @group Internal + */ + val compat: Compat + + /** @see [[compat]] + * @group Internal + */ + type Compat <: CompatApi + + /** Presence of an implicit value of this type in scope + * indicates that source compatibility with Scala 2.10 has been enabled. + * @group Internal + */ + @scala.annotation.implicitNotFound("This method has been removed from the public API. Import compat._ or migrate away.") + class CompatToken + + /** @see [[compat]] + * @group Internal + */ + trait CompatApi { + /** @see [[CompatToken]] */ + implicit val token = new CompatToken + + /** @see [[InternalApi.typeTagToManifest]] */ + @deprecated("Use `internal.typeTagToManifest` instead", "2.11.0") + def typeTagToManifest[T: ClassTag](mirror: Any, tag: Universe#TypeTag[T]): Manifest[T] = + internal.typeTagToManifest(mirror, tag) + + /** @see [[InternalApi.manifestToTypeTag]] */ + @deprecated("Use `internal.manifestToTypeTag` instead", "2.11.0") + def manifestToTypeTag[T](mirror: Any, manifest: Manifest[T]): Universe#TypeTag[T] = + internal.manifestToTypeTag(mirror, manifest) + + /** @see [[InternalApi.newScopeWith]] */ + @deprecated("Use `internal.newScopeWith` instead", "2.11.0") + def newScopeWith(elems: Symbol*): Scope = + internal.newScopeWith(elems: _*) + + /** Scala 2.10 compatibility enrichments for BuildApi. */ + implicit class CompatibleBuildApi(api: BuildApi) { + /** @see [[BuildApi.setInfo]] */ + @deprecated("Use `internal.reificationSupport.setInfo` instead", "2.11.0") + def setTypeSignature[S <: Symbol](sym: S, tpe: Type): S = internal.reificationSupport.setInfo(sym, tpe) + + /** @see [[BuildApi.FlagsRepr]] */ + @deprecated("Use `internal.reificationSupport.FlagsRepr` instead", "2.11.0") + def flagsFromBits(bits: Long): FlagSet = internal.reificationSupport.FlagsRepr(bits) + + /** @see [[BuildApi.noSelfType]] */ + @deprecated("Use `noSelfType` instead", "2.11.0") + def emptyValDef: ValDef = noSelfType + + /** @see [[BuildApi.mkThis]] */ + @deprecated("Use `internal.reificationSupport.mkThis` instead", "2.11.0") + def This(sym: Symbol): Tree = internal.reificationSupport.mkThis(sym) + + /** @see [[BuildApi.mkSelect]] */ + @deprecated("Use `internal.reificationSupport.mkSelect` instead", "2.11.0") + def Select(qualifier: Tree, sym: Symbol): Select = internal.reificationSupport.mkSelect(qualifier, sym) + + /** @see [[BuildApi.mkIdent]] */ + @deprecated("Use `internal.reificationSupport.mkIdent` instead", "2.11.0") + def Ident(sym: Symbol): Ident = internal.reificationSupport.mkIdent(sym) + + /** @see [[BuildApi.mkTypeTree]] */ + @deprecated("Use `internal.reificationSupport.mkTypeTree` instead", "2.11.0") + def TypeTree(tp: Type): TypeTree = internal.reificationSupport.mkTypeTree(tp) + } + + /** Scala 2.10 compatibility enrichments for Tree. */ + implicit class CompatibleTree(tree: Tree) { + /** @see [[InternalApi.freeTerms]] */ + @deprecated("Use `internal.freeTerms` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def freeTerms: List[FreeTermSymbol] = internal.freeTerms(tree) + + /** @see [[InternalApi.freeTypes]] */ + @deprecated("Use `internal.freeTerms` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def freeTypes: List[FreeTypeSymbol] = internal.freeTypes(tree) + + /** @see [[InternalApi.substituteSymbols]] */ + @deprecated("Use `internal.substituteSymbols` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree = internal.substituteSymbols(tree, from, to) + + /** @see [[InternalApi.substituteTypes]] */ + @deprecated("Use `internal.substituteTypes` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def substituteTypes(from: List[Symbol], to: List[Type]): Tree = internal.substituteTypes(tree, from, to) + + /** @see [[InternalApi.substituteThis]] */ + @deprecated("Use `internal.substituteThis` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def substituteThis(clazz: Symbol, to: Tree): Tree = internal.substituteThis(tree, clazz, to) + } + + /** Scala 2.10 compatibility enrichments for Tree. */ + implicit class CompatibleSymbol(symbol: Symbol) { + @deprecated("This API is unreliable. Use `isPrivateThis` or `isProtectedThis` instead", "2.11.0") + def isLocal: Boolean = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].isLocal + + @deprecated("This API is unreliable. Use `overrides.nonEmpty` instead", "2.11.0") + def isOverride: Boolean = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].isOverride + + /** @see [[InternalApi.isFreeTerm]] */ + @deprecated("Use `internal.isFreeTerm` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def isFreeTerm: Boolean = internal.isFreeTerm(symbol) + + /** @see [[InternalApi.asFreeTerm]] */ + @deprecated("Use `internal.asFreeTerm` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def asFreeTerm: FreeTermSymbol = internal.asFreeTerm(symbol) + + /** @see [[InternalApi.isFreeType]] */ + @deprecated("Use `internal.isFreeType` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def isFreeType: Boolean = internal.isFreeType(symbol) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.asFreeType` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def asFreeType: FreeTypeSymbol = internal.asFreeType(symbol) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.newTermSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def newTermSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol = internal.newTermSymbol(symbol, name, pos, flags) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.newModuleAndClassSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def newModuleAndClassSymbol(name: Name, pos: Position = NoPosition, flags: FlagSet = NoFlags): (ModuleSymbol, ClassSymbol) = internal.newModuleAndClassSymbol(symbol, name, pos, flags) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.newMethodSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def newMethodSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): MethodSymbol = internal.newMethodSymbol(symbol, name, pos, flags) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.newTypeSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def newTypeSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TypeSymbol = internal.newTypeSymbol(symbol, name, pos, flags) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.newClassSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def newClassSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): ClassSymbol = internal.newClassSymbol(symbol, name, pos, flags) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.isErroneous` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def isErroneous: Boolean = internal.isErroneous(symbol) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.isSkolem` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def isSkolem: Boolean = internal.isSkolem(symbol) + + /** @see [[InternalApi.asFreeType]] */ + @deprecated("Use `internal.deSkolemize` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def deSkolemize: Symbol = internal.deSkolemize(symbol) + } + + /** @see [[InternalApi.singleType]] */ + @deprecated("Use `internal.singleType` instead", "2.11.0") + def singleType(pre: Type, sym: Symbol): Type = internal.singleType(pre, sym) + + /** @see [[InternalApi.refinedType]] */ + @deprecated("Use `internal.refinedType` instead", "2.11.0") + def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): Type = internal.refinedType(parents, owner, decls, pos) + + /** @see [[InternalApi.refinedType]] */ + @deprecated("Use `internal.refinedType` instead", "2.11.0") + def refinedType(parents: List[Type], owner: Symbol): Type = internal.refinedType(parents, owner) + + /** @see [[InternalApi.typeRef]] */ + @deprecated("Use `internal.typeRef` instead", "2.11.0") + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type = internal.typeRef(pre, sym, args) + + /** @see [[InternalApi.intersectionType]] */ + @deprecated("Use `internal.intersectionType` instead", "2.11.0") + def intersectionType(tps: List[Type]): Type = internal.intersectionType(tps) + + /** @see [[InternalApi.intersectionType]] */ + @deprecated("Use `internal.intersectionType` instead", "2.11.0") + def intersectionType(tps: List[Type], owner: Symbol): Type = internal.intersectionType(tps, owner) + + /** @see [[InternalApi.polyType]] */ + @deprecated("Use `internal.polyType` instead", "2.11.0") + def polyType(tparams: List[Symbol], tpe: Type): Type = internal.polyType(tparams, tpe) + + /** @see [[InternalApi.existentialAbstraction]] */ + @deprecated("Use `internal.existentialAbstraction` instead", "2.11.0") + def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type = internal.existentialAbstraction(tparams, tpe0) + } +} diff --git a/src/reflect/scala/reflect/api/JavaMirrors.scala b/src/reflect/scala/reflect/api/JavaMirrors.scala deleted file mode 100644 index b678033e1a..0000000000 --- a/src/reflect/scala/reflect/api/JavaMirrors.scala +++ /dev/null @@ -1,56 +0,0 @@ -package scala.reflect -package api - -/** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> - * - * A refinement of [[scala.reflect.api.Mirror]] for runtime reflection using JVM classloaders. - * - * This refinement equips mirrors with reflection capabilities for the JVM. `JavaMirror` can - * convert Scala reflection artifacts (symbols and types) into Java reflection artifacts (classes) - * and vice versa. It can also perform reflective invocations (getting/setting field values, - * calling methods, etc). - * - * For more information about `Mirrors`s, see [[scala.reflect.api.Mirrors]] or the - * [[http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html Reflection Guide: Mirrors]] - * - * @groupname JavaMirrors Java Mirrors - * @group ReflectionAPI - */ -trait JavaMirrors { self: JavaUniverse => - - /** In runtime reflection universes, runtime representation of a class is `java.lang.Class`. - * @group JavaMirrors - */ - type RuntimeClass = java.lang.Class[_] - - /** In runtime reflection universes, mirrors are `JavaMirrors`. - * @group JavaMirrors - */ - override type Mirror >: Null <: JavaMirror - - /** A refinement of [[scala.reflect.api.Mirror]] for runtime reflection using JVM classloaders. - * - * With this upgrade, mirrors become capable of converting Scala reflection artifacts (symbols and types) - * into Java reflection artifacts (classes) and vice versa. Consequently, refined mirrors - * become capable of performing reflective invocations (getting/setting field values, calling methods, etc). - * - * For more information about `Mirrors`s, see [[scala.reflect.api.Mirrors]] or the - * [[http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html Reflection Guide: Mirrors]] - * - * @group JavaMirrors - */ - trait JavaMirror extends scala.reflect.api.Mirror[self.type] with RuntimeMirror { - val classLoader: ClassLoader - override def toString = s"JavaMirror with ${runtime.ReflectionUtils.show(classLoader)}" - } - - /** Creates a runtime reflection mirror from a JVM classloader. - * - * For more information about `Mirrors`s, see [[scala.reflect.api.Mirrors]] or the - * [[http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html Reflection Guide: Mirrors]] - * - * @group JavaMirrors - */ - def runtimeMirror(cl: ClassLoader): Mirror -} diff --git a/src/reflect/scala/reflect/api/JavaUniverse.scala b/src/reflect/scala/reflect/api/JavaUniverse.scala index 04d091ee9d..88107ea117 100644 --- a/src/reflect/scala/reflect/api/JavaUniverse.scala +++ b/src/reflect/scala/reflect/api/JavaUniverse.scala @@ -1,13 +1,16 @@ -package scala.reflect +package scala +package reflect package api /** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A refinement of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders. + * A refinement of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders. * - * The refinement consists of an upgrade to the mirror API, which gets extended from [[scala.reflect.api.Mirror]] - * to [[scala.reflect.api.JavaMirrors#JavaMirror]]. + * This refinement equips mirrors with reflection capabilities for the JVM. `JavaMirror` can + * convert Scala reflection artifacts (symbols and types) into Java reflection artifacts (classes) + * and vice versa. It can also perform reflective invocations (getting/setting field values, + * calling methods, etc). * * See the [[http://docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide]] for details on how to use runtime reflection. * @@ -16,31 +19,41 @@ package api * * @contentDiagram hideNodes "*Api" */ -trait JavaUniverse extends Universe with JavaMirrors { self => +trait JavaUniverse extends Universe { self => - /* @group JavaUniverse */ - override def typeTagToManifest[T: ClassTag](mirror0: Any, tag: Universe # TypeTag[T]): Manifest[T] = { - // SI-6239: make this conversion more precise - val mirror = mirror0.asInstanceOf[Mirror] - val runtimeClass = mirror.runtimeClass(tag.in(mirror).tpe) - Manifest.classType(runtimeClass).asInstanceOf[Manifest[T]] + /** In runtime reflection universes, runtime representation of a class is `java.lang.Class`. + * @group JavaMirrors + */ + type RuntimeClass = java.lang.Class[_] + implicit val RuntimeClassTag: ClassTag[RuntimeClass] = ClassTag[RuntimeClass](classOf[RuntimeClass]) + + /** In runtime reflection universes, mirrors are `JavaMirrors`. + * @group JavaMirrors + */ + override type Mirror >: Null <: JavaMirror + + /** A refinement of [[scala.reflect.api.Mirror]] for runtime reflection using JVM classloaders. + * + * With this upgrade, mirrors become capable of converting Scala reflection artifacts (symbols and types) + * into Java reflection artifacts (classes) and vice versa. Consequently, refined mirrors + * become capable of performing reflective invocations (getting/setting field values, calling methods, etc). + * + * For more information about `Mirrors`s, see [[scala.reflect.api.Mirrors]] or the + * [[http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html Reflection Guide: Mirrors]] + * + * @group JavaMirrors + */ + trait JavaMirror extends scala.reflect.api.Mirror[self.type] with RuntimeMirror { + val classLoader: ClassLoader + override def toString = s"JavaMirror with ${runtime.ReflectionUtils.show(classLoader)}" } - /* @group JavaUniverse */ - override def manifestToTypeTag[T](mirror0: Any, manifest: Manifest[T]): Universe # TypeTag[T] = - TypeTag(mirror0.asInstanceOf[Mirror], new TypeCreator { - def apply[U <: Universe with Singleton](mirror: scala.reflect.api.Mirror[U]): U # Type = { - mirror.universe match { - case ju: JavaUniverse => - val jm = mirror.asInstanceOf[ju.Mirror] - val sym = jm.classSymbol(manifest.erasure) - val tpe = - if (manifest.typeArguments.isEmpty) sym.toType - else ju.appliedType(sym.toTypeConstructor, manifest.typeArguments map (targ => ju.manifestToTypeTag(jm, targ)) map (_.in(jm).tpe)) - tpe.asInstanceOf[U # Type] - case u => - u.manifestToTypeTag(mirror.asInstanceOf[u.Mirror], manifest).in(mirror).tpe - } - } - }) -} + /** Creates a runtime reflection mirror from a JVM classloader. + * + * For more information about `Mirrors`s, see [[scala.reflect.api.Mirrors]] or the + * [[http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html Reflection Guide: Mirrors]] + * + * @group JavaMirrors + */ + def runtimeMirror(cl: ClassLoader): Mirror +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/api/Liftables.scala b/src/reflect/scala/reflect/api/Liftables.scala new file mode 100644 index 0000000000..673dbce6f5 --- /dev/null +++ b/src/reflect/scala/reflect/api/Liftables.scala @@ -0,0 +1,75 @@ +package scala +package reflect +package api + +trait Liftables { self: Universe => + + /** A type class that defines a representation of `T` as a `Tree`. + * + * @see [[http://docs.scala-lang.org/overviews/quasiquotes/lifting.html]] + */ + trait Liftable[T] { + def apply(value: T): Tree + } + + /** Companion to `Liftable` type class that contains standard instances + * and provides a helper `apply` method to simplify creation of new ones. + */ + object Liftable extends StandardLiftableInstances { + /** A helper method that simplifies creation of `Liftable` instances. + * Takes a type and a function that maps that type to a tree representation. + * + * For example to write Liftable for object one might use it like: + * + * {{{ + * scala> object O + * + * scala> val Oref = symbolOf[O.type].asClass.module + * + * scala> implicit val liftO = Liftable[O.type] { _ => q"$Oref" } + * + * scala> val lifted = q"$O" + * lifted: universe.Tree = O + * }}} + * + * @see [[http://docs.scala-lang.org/overviews/quasiquotes/lifting.html]] + */ + def apply[T](f: T => Tree): Liftable[T] = + new Liftable[T] { def apply(value: T): Tree = f(value) } + } + + /** A type class that defines a way to extract instance of `T` from a `Tree`. + * + * @see [[http://docs.scala-lang.org/overviews/quasiquotes/unlifting.html]] + */ + trait Unliftable[T] { + def unapply(tree: Tree): Option[T] + } + + /** Companion to `Unliftable` type class that contains standard instances + * and provides a helper `apply` method to simplify creation of new ones. + */ + object Unliftable extends StandardUnliftableInstances { + /** A helper method that simplifies creation of `Unliftable` instances. + * Takes a partial function which is defined on correct representations of `T` + * and returns corresponing instances. + * + * For example to extract a reference to an object as object itself: + * + * {{{ + * scala> object O + * + * scala> val Oref = symbolOf[O.type].asClass.module + * + * scala> implicit val unliftO = Unliftable[O.type] { case t if t.symbol == Oref => O } + * + * scala> val q"${_: O.type}" = q"$Oref" + * }}} + * + * @see [[http://docs.scala-lang.org/overviews/quasiquotes/unlifting.html]] + */ + def apply[T](pf: PartialFunction[Tree, T]): Unliftable[T] = new Unliftable[T] { + def unapply(value: Tree): Option[T] = pf.lift(value) + } + } +} diff --git a/src/reflect/scala/reflect/api/Mirror.scala b/src/reflect/scala/reflect/api/Mirror.scala index 1223326d7c..318fdb369a 100644 --- a/src/reflect/scala/reflect/api/Mirror.scala +++ b/src/reflect/scala/reflect/api/Mirror.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -49,16 +50,16 @@ abstract class Mirror[U <: Universe with Singleton] { * If you need a symbol that corresponds to the type alias itself, load it directly from the package class: * * scala> cm.staticClass("scala.List") - * res0: reflect.runtime.universe.ClassSymbol = class List + * res0: scala.reflect.runtime.universe.ClassSymbol = class List * * scala> res0.fullName * res1: String = scala.collection.immutable.List * * scala> cm.staticPackage("scala") - * res2: reflect.runtime.universe.ModuleSymbol = package scala + * res2: scala.reflect.runtime.universe.ModuleSymbol = package scala * - * scala> res2.moduleClass.typeSignature member newTypeName("List") - * res3: reflect.runtime.universe.Symbol = type List + * scala> res2.moduleClass.info member newTypeName("List") + * res3: scala.reflect.runtime.universe.Symbol = type List * * scala> res3.fullName * res4: String = scala.List @@ -78,11 +79,10 @@ abstract class Mirror[U <: Universe with Singleton] { * } * * staticClass("foo.B") will resolve to the symbol corresponding to the class B declared in the package foo, and - * staticClass("foo.A") will throw a MissingRequirementException (which is exactly what scalac would do if this - * fully qualified class name is written inside any package in a Scala program). + * staticClass("foo.A") will throw a ScalaReflectionException. * * In the example above, to load a symbol that corresponds to the class B declared in the object foo, - * use staticModule("foo") to load the module symbol and then navigate typeSignature.members of its moduleClass. + * use staticModule("foo") to load the module symbol and then navigate info.members of its moduleClass. * @group Mirror */ def staticClass(fullName: String): U#ClassSymbol @@ -105,11 +105,10 @@ abstract class Mirror[U <: Universe with Singleton] { * } * * staticModule("foo.B") will resolve to the symbol corresponding to the object B declared in the package foo, and - * staticModule("foo.A") will throw a MissingRequirementException (which is exactly what scalac would do if this - * fully qualified class name is written inside any package in a Scala program). + * staticModule("foo.A") will throw a ScalaReflectionException * * In the example above, to load a symbol that corresponds to the object B declared in the object foo, - * use staticModule("foo") to load the module symbol and then navigate typeSignature.members of its moduleClass. + * use staticModule("foo") to load the module symbol and then navigate info.members of its moduleClass. * @group Mirror */ def staticModule(fullName: String): U#ModuleSymbol @@ -119,4 +118,22 @@ abstract class Mirror[U <: Universe with Singleton] { * @group Mirror */ def staticPackage(fullName: String): U#ModuleSymbol + + /** + * Shortcut for `implicitly[WeakTypeTag[T]].tpe` + * @group TypeTags + */ + def weakTypeOf[T: universe.WeakTypeTag]: U#Type = universe.weakTypeTag[T].in(this).tpe + + /** + * Shortcut for `implicitly[TypeTag[T]].tpe` + * @group TypeTags + */ + def typeOf[T: universe.TypeTag]: U#Type = universe.typeTag[T].in(this).tpe + + /** + * Type symbol of `x` as derived from a type tag. + * @group TypeTags + */ + def symbolOf[T: universe.WeakTypeTag]: U#TypeSymbol } diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index 76a75940ff..ec420d184c 100644 --- a/src/reflect/scala/reflect/api/Mirrors.scala +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -28,12 +29,12 @@ package api * Compile-time `Mirror`s make use of only classloader `Mirror`s to load `Symbol`s * by name. * - * The entry point to classloader `Mirror`s is via [[scala.reflect.macros.Context#mirror]]. + * The entry point to classloader `Mirror`s is via [[scala.reflect.macros.blackbox.Context#mirror]] or [[scala.reflect.macros.whitebox.Context#mirror]]. * Typical methods which use classloader `Mirror`s include [[scala.reflect.api.Mirror#staticClass]], * [[scala.reflect.api.Mirror#staticModule]], and [[scala.reflect.api.Mirror#staticPackage]]. For * example: * {{{ - * import scala.reflect.macros.Context + * import scala.reflect.macros.blackbox.Context * * case class Location(filename: String, line: Int, column: Int) * @@ -61,7 +62,7 @@ package api * The entry point to `Mirror`s for use at runtime is via `ru.runtimeMirror(<classloader>)`, where * `ru` is [[scala.reflect.runtime.universe]]. * - * The result of a [[scala.reflect.api.JavaMirrors#runtimeMirror]] call is a classloader mirror, + * The result of a [[scala.reflect.api.JavaUniverse#runtimeMirror]] call is a classloader mirror, * of type [[scala.reflect.api.Mirrors#ReflectiveMirror]], which can load symbols by names as * discussed above (in the “Compile-time” section). * @@ -100,7 +101,7 @@ package api * via `ModuleMirror.instance`). Entry point: `val mm = im.reflectMethod(<method symbol>)`. * Example: * {{{ - * scala> val methodX = typeOf[C].declaration(newTermName("x")).asMethod + * scala> val methodX = typeOf[C].declaration(TermName("x")).asMethod * methodX: reflect.runtime.universe.MethodSymbol = method x * * scala> val mm = im.reflectMethod(methodX) @@ -125,7 +126,7 @@ package api * scala> val im = m.reflect(new C) * im: reflect.runtime.universe.InstanceMirror = instance mirror for C@5f0c8ac1 * - * scala> val fieldX = typeOf[C].declaration(newTermName("x")).asTerm.accessed.asTerm + * scala> val fieldX = typeOf[C].declaration(TermName("x")).asTerm.accessed.asTerm * fieldX: reflect.runtime.universe.TermSymbol = value x * scala> val fmX = im.reflectField(fieldX) * fmX: reflect.runtime.universe.FieldMirror = field mirror for C.x (bound to C@5f0c8ac1) @@ -135,7 +136,7 @@ package api * * scala> fmX.set(3) // NOTE: can set an underlying value of an immutable field! * - * scala> val fieldY = typeOf[C].declaration(newTermName("y")).asTerm.accessed.asTerm + * scala> val fieldY = typeOf[C].declaration(TermName("y")).asTerm.accessed.asTerm * fieldY: reflect.runtime.universe.TermSymbol = variable y * * scala> val fmY = im.reflectField(fieldY) @@ -152,7 +153,7 @@ package api * * '''[[scala.reflect.api.Mirrors#ClassMirror]]'''. Used for creating invoker mirrors for constructors. * Entry points: for ''static classes'' `val cm1 = m.reflectClass(<class symbol>)`, - * for ''inner classes'' `val mm2 = im.reflectClass(<module symbol>)`. + * for ''inner classes'' `val mm2 = im.reflectClass(<class symbol>)`. * Example: * {{{ * scala> case class C(x: Int) @@ -224,7 +225,12 @@ trait Mirrors { self: Universe => /** Abstracts the runtime representation of a class on the underlying platform. * @group Mirrors */ - type RuntimeClass >: Null + type RuntimeClass >: Null <: AnyRef + + /** Has no special methods. Is here to provides erased identity for `RuntimeClass`. + * @group API + */ + trait RuntimeClassApi // todo. an improvement might be having mirrors reproduce the structure of the reflection domain // e.g. a ClassMirror could also have a list of fields, methods, constructors and so on @@ -254,8 +260,8 @@ trait Mirrors { self: Universe => * Note also that only accessor MethodMirrors, but not FieldMirrors will accurately reflect overriding behavior. * * To get a field symbol by the name of the field you would like to reflect, - * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the field>)).asTerm.accessed`. - * For further information about member lookup refer to `Symbol.typeSignature`. + * use `<this mirror>.symbol.info.member(TermName(<name of the field>)).asTerm.accessed`. + * For further information about member lookup refer to `Symbol.info`. * * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). * It must be a member (declared or inherited) of the class of the instance underlying this mirror. @@ -274,8 +280,8 @@ trait Mirrors { self: Universe => * that can be used to invoke the method provided. * * To get a method symbol by the name of the method you would like to reflect, - * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the method>)).asMethod`. - * For further information about member lookup refer to `Symbol.typeSignature`. + * use `<this mirror>.symbol.info.member(TermName(<name of the method>)).asMethod`. + * For further information about member lookup refer to `Symbol.info`. * * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). * It must be a member (declared or inherited) of the instance underlying this mirror. @@ -286,8 +292,8 @@ trait Mirrors { self: Universe => * that can be used to create instances of the class, inspect its companion object or perform further reflections. * * To get a class symbol by the name of the class you would like to reflect, - * use `<this mirror>.symbol.typeSignature.member(newTypeName(<name of the class>)).asClass`. - * For further information about member lookup refer to `Symbol.typeSignature`. + * use `<this mirror>.symbol.info.member(newTypeName(<name of the class>)).asClass`. + * For further information about member lookup refer to `Symbol.info`. * * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). * It must be a member (declared or inherited) of the instance underlying this mirror. @@ -298,8 +304,8 @@ trait Mirrors { self: Universe => * that can be used to get the instance of the object or inspect its companion class. * * To get a module symbol by the name of the object you would like to reflect, - * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the object>)).asModule`. - * For further information about member lookup refer to `Symbol.typeSignature`. + * use `<this mirror>.symbol.info.member(TermName(<name of the object>)).asModule`. + * For further information about member lookup refer to `Symbol.info`. * * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). * It must be a member (declared or inherited) of the instance underlying this mirror. @@ -350,6 +356,11 @@ trait Mirrors { self: Universe => * the value of the base field. To achieve overriding behavior, use reflectMethod on an accessor. */ def set(value: Any): Unit + + /** Creates a new mirror which uses the same symbol, but is bound to a different receiver. + * This is significantly faster than recreating the mirror from scratch. + */ + def bind(newReceiver: Any): FieldMirror } /** A mirror that reflects a method. @@ -371,6 +382,11 @@ trait Mirrors { self: Universe => * with invoking the corresponding method or constructor. */ def apply(args: Any*): Any + + /** Creates a new mirror which uses the same symbol, but is bound to a different receiver. + * This is significantly faster than recreating the mirror from scratch. + */ + def bind(newReceiver: Any): MethodMirror } /** A mirror that reflects the instance or static parts of a runtime class. @@ -421,8 +437,8 @@ trait Mirrors { self: Universe => * that can be used to invoke it and construct instances of this mirror's symbols. * * To get a constructor symbol you would like to reflect, - * use `<this mirror>.symbol.typeSignature.member(nme.CONSTRUCTOR).asMethod`. - * For further information about member lookup refer to `Symbol.typeSignature`. + * use `<this mirror>.symbol.info.member(termNames.CONSTRUCTOR).asMethod`. + * For further information about member lookup refer to `Symbol.info`. * * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). * It must be a member (declared or inherited) of the class underlying this mirror. @@ -488,7 +504,7 @@ trait Mirrors { self: Universe => /** A class symbol for the specified runtime class. * @return The class symbol for the runtime class in the current class loader. * @throws java.lang.ClassNotFoundException if no class with that name exists - * @throws scala.reflect.internal.MissingRequirementError if no corresponding symbol exists + * @throws scala.reflect.ScalaReflectionException if no corresponding symbol exists * to do: throws anything else? */ def classSymbol(rtcls: RuntimeClass): ClassSymbol @@ -496,7 +512,7 @@ trait Mirrors { self: Universe => /** A module symbol for the specified runtime class. * @return The module symbol for the runtime class in the current class loader. * @throws java.lang.ClassNotFoundException if no class with that name exists - * @throws scala.reflect.internal.MissingRequirementError if no corresponding symbol exists + * @throws scala.reflect.ScalaReflectionException if no corresponding symbol exists * to do: throws anything else? */ def moduleSymbol(rtcls: RuntimeClass): ModuleSymbol diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala index 7c12f180a8..fe5f47c25d 100644 --- a/src/reflect/scala/reflect/api/Names.scala +++ b/src/reflect/scala/reflect/api/Names.scala @@ -1,6 +1,9 @@ -package scala.reflect +package scala +package reflect package api +import scala.language.implicitConversions + /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * @@ -30,55 +33,49 @@ trait Names { * Enables an alternative notation `"map": TermName` as opposed to `newTermName("map")`. * @group Names */ - implicit def stringToTermName(s: String): TermName = newTermName(s) + @deprecated("Use explicit `TermName(s)` instead", "2.11.0") + implicit def stringToTermName(s: String): TermName = TermName(s) /** An implicit conversion from String to TypeName. * Enables an alternative notation `"List": TypeName` as opposed to `newTypeName("List")`. * @group Names */ - implicit def stringToTypeName(s: String): TypeName = newTypeName(s) + @deprecated("Use explicit `TypeName(s)` instead", "2.11.0") + implicit def stringToTypeName(s: String): TypeName = TypeName(s) /** The abstract type of names. * @group Names */ - type Name >: Null <: NameApi - - /** A tag that preserves the identity of the `Name` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NameTag: ClassTag[Name] + type Name >: Null <: AnyRef with NameApi /** The abstract type of names representing terms. * @group Names */ - type TypeName >: Null <: Name + type TypeName >: Null <: TypeNameApi with Name - /** A tag that preserves the identity of the `TypeName` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** Has no special methods. Is here to provides erased identity for `TypeName`. + * @group API */ -implicit val TypeNameTag: ClassTag[TypeName] + trait TypeNameApi /** The abstract type of names representing types. * @group Names */ - type TermName >: Null <: Name + type TermName >: Null <: TermNameApi with Name - /** A tag that preserves the identity of the `TermName` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** Has no special methods. Is here to provides erased identity for `TermName`. + * @group API */ - implicit val TermNameTag: ClassTag[TermName] + trait TermNameApi /** The API of Name instances. * @group API */ abstract class NameApi { - /** Checks wether the name is a a term name */ + /** Checks wether the name is a term name */ def isTermName: Boolean - /** Checks wether the name is a a type name */ + /** Checks wether the name is a type name */ def isTypeName: Boolean /** Returns a term name that wraps the same string as `this` */ @@ -90,11 +87,13 @@ implicit val TypeNameTag: ClassTag[TypeName] /** Replaces all occurrences of \$op_names in this name by corresponding operator symbols. * Example: `foo_\$plus\$eq` becomes `foo_+=` */ + @deprecated("Use `decodedName.toString` instead", "2.11.0") def decoded: String /** Replaces all occurrences of operator symbols in this name by corresponding \$op_names. * Example: `foo_+=` becomes `foo_\$plus\$eq`. */ + @deprecated("Use `encodedName.toString` instead", "2.11.0") def encoded: String /** The decoded name, still represented as a name. @@ -109,10 +108,38 @@ implicit val TypeNameTag: ClassTag[TypeName] /** Create a new term name. * @group Names */ + @deprecated("Use TermName instead", "2.11.0") def newTermName(s: String): TermName /** Creates a new type name. * @group Names */ + @deprecated("Use TypeName instead", "2.11.0") def newTypeName(s: String): TypeName + + /** The constructor/extractor for `TermName` instances. + * @group Extractors + */ + val TermName: TermNameExtractor + + /** An extractor class to create and pattern match with syntax `TermName(s)`. + * @group Extractors + */ + abstract class TermNameExtractor { + def apply(s: String): TermName + def unapply(name: TermName): Option[String] + } + + /** The constructor/extractor for `TypeName` instances. + * @group Extractors + */ + val TypeName: TypeNameExtractor + + /** An extractor class to create and pattern match with syntax `TypeName(s)`. + * @group Extractors + */ + abstract class TypeNameExtractor { + def apply(s: String): TypeName + def unapply(name: TypeName): Option[String] + } } diff --git a/src/reflect/scala/reflect/api/Position.scala b/src/reflect/scala/reflect/api/Position.scala index 63c67627a3..9d1b7c3812 100644 --- a/src/reflect/scala/reflect/api/Position.scala +++ b/src/reflect/scala/reflect/api/Position.scala @@ -1,17 +1,52 @@ -package scala.reflect +package scala +package reflect package api import scala.reflect.macros.Attachments /** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * Position tracks the origin of [[Symbols#Symbol symbols]] and [[Trees#Tree tree nodes]]. They are commonly used when - * displaying warnings and errors, to indicate the incorrect point in the program. + * Position tracks the origin of [[Symbols#Symbol symbols]] and [[Trees#Tree tree nodes]]. They are commonly used when + * displaying warnings and errors, to indicate the incorrect point in the program. * - * <b>Please note that this trait may be refactored in future versions of the Scala reflection API.</b> + * Every non-empty position refers to a SourceFile and three character + * offsets within it: start, end, and point. The point is where the ^ belongs when + * issuing an error message, usually a Name. A range position can be designated + * as transparent, which excuses it from maintaining the invariants to follow. If + * a transparent position has opaque children, those are considered as if they were + * the direct children of the transparent position's parent. * - * For more information about `Position`s, see the [[http://docs.scala-lang.org/overviews/reflection/annotations-names-scopes.html Reflection Guide: Annotations, Names, Scopes, and More]] + * Note: some of these invariants actually apply to the trees which carry + * the positions, but they are phrased as if the positions themselves were + * the parent/children for conciseness. + * + * Invariant 1: in a focused/offset position, start == point == end + * Invariant 2: in a range position, start <= point < end + * Invariant 3: an offset position never has a child with a range position + * Invariant 4: every range position child of a range position parent is contained within its parent + * Invariant 5: opaque range position siblings overlap at most at a single point + * + * The following tests are useful on positions: + * + * pos.isDefined true if position is not an UndefinedPosition (those being NoPosition and FakePos) + * pos.isRange true if position is a range (opaque or transparent) which implies start < end + * pos.isOpaqueRange true if position is an opaque range + * + * The following accessor methods are provided - an exception will be thrown if + * point/start/end are attempted on an UndefinedPosition. + * + * pos.source The source file of the position, or NoSourceFile if unavailable + * pos.point The offset of the point + * pos.start The (inclusive) start offset, or the point of an offset position + * pos.end The (exclusive) end offset, or the point of an offset position + * + * The following conversion methods are often used: + * + * pos.focus Converts a range position to an offset position focused on the point + * pos.makeTransparent Convert an opaque range into a transparent range + * + * For more information about `Position`s, see the [[http://docs.scala-lang.org/overviews/reflection/annotations-names-scopes.html Reflection Guide: Annotations, Names, Scopes, and More]] * * @groupname Common Commonly used methods * @group ReflectionAPI @@ -19,23 +54,9 @@ import scala.reflect.macros.Attachments trait Position extends Attachments { /** @inheritdoc */ - type Pos >: Null <: Position + type Pos >: Null <: AnyRef with Position - /** Java file corresponding to the source file of this position. - * - * The return type is `scala.reflect.io.AbstractFile`, which belongs to an experimental part of Scala reflection. - * It should not be used unless you know what you are doing. In subsequent releases, this API will be refined - * and exposed as a part of scala.reflect.api. - * - * @group Common - */ - def source: scala.reflect.internal.util.SourceFile - - /** Is this position neither a NoPosition nor a FakePosition? - * If isDefined is true, offset and source are both defined. - * @group Common - */ - def isDefined: Boolean + ////////////////// POSITION FLAVORS ////////////////// /** Is this position a range position? */ def isRange: Boolean @@ -46,14 +67,18 @@ trait Position extends Attachments { /** Is this position a non-transparent range position? */ def isOpaqueRange: Boolean + /** If this is a range position, the offset position of its point. + * Otherwise the position itself + */ + def focus: Pos + /** 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 + ////////////////// POSITION ESSENTIALS ////////////////// - /** The start of the position's range, or point if not a range position. */ - def startOrPoint: Int + /** The start of the position's range, or the point if not a range position. */ + def start: Int /** The point (where the ^ is) of the position, which is easiest to access using the [[line]] and [[column]] values. * The [[lineContent line content]] is also available. @@ -61,104 +86,122 @@ trait Position extends Attachments { */ def point: Int - /** The point (where the ^ is) of the position, or else `default` if undefined. + /** The end of the position's range, or the point if not a range position. + */ + def end: Int + + /** Java file corresponding to the source file of this position. + * + * The return type is `scala.reflect.io.AbstractFile`, which belongs to an experimental part of Scala reflection. + * It should not be used unless you know what you are doing. In subsequent releases, this API will be refined + * and exposed as a part of scala.reflect.api. + * * @group Common */ - def pointOrElse(default: Int): Int + def source: scala.reflect.internal.util.SourceFile - /** The end of the position's range, error if not a range position. + /** The position indicates a [[column `column`]] and the `line` in the source file. + * @group Common */ - def end: Int + def line: Int - /** The end of the position's range, or point if not a range position. + /** The position indicates a `column` and the [[line `line`]] in the source file. + * @group Common */ - def endOrPoint: Int + def column: Int + + ////////////////// POSITION FACTORIES ////////////////// - /** The same position with a different start value (if a range). + /** Returns a new position with the same attributes, but a different start value (if a range). */ def withStart(off: Int): Pos - /** The same position with a different end value (if a range). + /** Returns a new position with the same attributes, but 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). + /** Returns a new position with the same attributes, but 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 + ////////////////// STUFF ////////////////// + + /** Is this position not a NoPosition? + * If isDefined is true, offset and source are both defined. + * @group Common */ - def union(pos: Pos): Pos + @deprecated("Removed from the public API", "2.11.0") def isDefined: Boolean - /** If this is a range position, the offset position of its point. - * Otherwise the position itself + /** The point (where the ^ is) of the position, or else `default` if undefined. + * @group Common */ - def focus: Pos + @deprecated("Removed from the public API", "2.11.0") def pointOrElse(default: Int): Int + + /** The start of the position's range, or point if not a range position. */ + @deprecated("Removed from the public API", "2.11.0") def startOrPoint: Int + + /** The end of the position's range, or point if not a range position. + */ + @deprecated("Removed from the public API", "2.11.0") def endOrPoint: Int + + /** If this is a range, the union with the other range, with the point of this position. + * Otherwise, this position + */ + @deprecated("Removed from the public API", "2.11.0") def union(pos: Pos): Pos /** If this is a range position, the offset position of its start. * Otherwise the position itself */ - def focusStart: Pos + @deprecated("Removed from the public API", "2.11.0") def focusStart: Pos /** If this is a range position, the offset position of its end. * Otherwise the position itself */ - def focusEnd: Pos + @deprecated("Removed from the public API", "2.11.0") 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 + @deprecated("Removed from the public API", "2.11.0") 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 + @deprecated("Removed from the public API", "2.11.0") 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 + @deprecated("Removed from the public API", "2.11.0") 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 + @deprecated("Removed from the public API", "2.11.0") 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 + @deprecated("Removed from the public API", "2.11.0") 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 - - /** The position indicates a [[column `column`]] and the `line` in the source file. - * @group Common - */ - def line: Int - - /** The position indicates a `column` and the [[line `line`]] in the source file. - * @group Common - */ - def column: Int + @deprecated("Removed from the public API", "2.11.0") def sameRange(pos: Pos): Boolean /** Convert this to a position around `point` that spans a single source line */ - def toSingleLine: Pos + @deprecated("Removed from the public API", "2.11.0") def toSingleLine: Pos /** The content of the line this Position refers to. * @group Common */ - def lineContent: String + @deprecated("Removed from the public API", "2.11.0") def lineContent: String /** Show a textual representation of the position. */ - def show: String + @deprecated("Use `universe.show(position)` instead", "2.11.0") def show: String } diff --git a/src/reflect/scala/reflect/api/Positions.scala b/src/reflect/scala/reflect/api/Positions.scala index 87f00fdb88..63ad605656 100644 --- a/src/reflect/scala/reflect/api/Positions.scala +++ b/src/reflect/scala/reflect/api/Positions.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -18,13 +19,7 @@ trait Positions { * The main documentation entry about positions is located at [[scala.reflect.api.Position]]. * @group Positions */ - type Position >: Null <: scala.reflect.api.Position { type Pos = Position } - - /** A tag that preserves the identity of the `Position` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val PositionTag: ClassTag[Position] + type Position >: Null <: AnyRef with scala.reflect.api.Position { type Pos = Position } /** A special "missing" position. * @group Positions diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 85ddcc6523..92ae6d8b44 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api import java.io.{ PrintWriter, StringWriter } @@ -47,7 +48,7 @@ import java.io.{ PrintWriter, StringWriter } * res1: String = Block(List( * ClassDef(Modifiers(FINAL), newTypeName("C"), List(), Template( * List(Ident(newTypeName("AnyRef"))), - * emptyValDef, + * noSelfType, * List( * DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), * Block(List( @@ -67,11 +68,11 @@ import java.io.{ PrintWriter, StringWriter } * scala> import scala.reflect.runtime.{currentMirror => cm} * import scala.reflect.runtime.{currentMirror=>cm} * - * scala> showRaw(cm.mkToolBox().typeCheck(tree), printTypes = true) + * scala> showRaw(cm.mkToolBox().typecheck(tree), printTypes = true) * res2: String = Block[1](List( * ClassDef[2](Modifiers(FINAL), newTypeName("C"), List(), Template[3]( * List(Ident[4](newTypeName("AnyRef"))), - * emptyValDef, + * noSelfType, * List( * DefDef[2](Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree[3](), * Block[1](List( @@ -141,66 +142,97 @@ trait Printers { self: Universe => def print(args: Any*) protected var printTypes = false protected var printIds = false + protected var printOwners = false protected var printKinds = false protected var printMirrors = false + protected var printPositions = false def withTypes: this.type = { printTypes = true; this } def withoutTypes: this.type = { printTypes = false; this } def withIds: this.type = { printIds = true; this } def withoutIds: this.type = { printIds = false; this } + def withOwners: this.type = { printOwners = true; this } + def withoutOwners: this.type = { printOwners = false; this } def withKinds: this.type = { printKinds = true; this } def withoutKinds: this.type = { printKinds = false; this } def withMirrors: this.type = { printMirrors = true; this } def withoutMirrors: this.type = { printMirrors = false; this } + def withPositions: this.type = { printPositions = true; this } + def withoutPositions: this.type = { printPositions = false; this } } /** @group Printers */ - case class BooleanFlag(val value: Option[Boolean]) + case class BooleanFlag(value: Option[Boolean]) /** @group Printers */ object BooleanFlag { import scala.language.implicitConversions implicit def booleanToBooleanFlag(value: Boolean): BooleanFlag = BooleanFlag(Some(value)) implicit def optionToBooleanFlag(value: Option[Boolean]): BooleanFlag = BooleanFlag(value) + import scala.reflect.internal.settings.MutableSettings + implicit def settingToBooleanFlag(setting: MutableSettings#BooleanSetting): BooleanFlag = BooleanFlag(Some(setting.value)) } /** @group Printers */ - protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None): String = { + protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = { val buffer = new StringWriter() val writer = new PrintWriter(buffer) - var printer = mkPrinter(writer) + val printer = mkPrinter(writer) printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) + printOwners.value.map(printOwners => if (printOwners) printer.withOwners else printer.withoutOwners) printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) printMirrors.value.map(printMirrors => if (printMirrors) printer.withMirrors else printer.withoutMirrors) + printPositions.value.map(printPositions => if (printPositions) printer.withPositions else printer.withoutPositions) printer.print(what) writer.flush() buffer.toString } /** By default trees are printed with `show` - * @group Printers + * @group Printers */ override protected def treeToString(tree: Tree) = show(tree) /** Renders a representation of a reflection artifact - * as desugared Java code. + * as desugared Scala code. * - * @group Printers + * @group Printers */ - def show(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None): String = - render(any, newTreePrinter(_), printTypes, printIds, printKinds, printMirrors) + def show(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newTreePrinter(_), printTypes, printIds, printOwners, printKinds, printMirrors, printPositions) /** Hook to define what `show(...)` means. * @group Printers */ protected def newTreePrinter(out: PrintWriter): TreePrinter - /** Renders internal structure of a reflection artifact as the - * visualization of a Scala syntax tree. + /** + * Renders the code of the passed tree, so that: + * 1) it can be later compiled by scalac retaining the same meaning, + * 2) it looks pretty. + * #1 is available for unattributed trees and attributed trees + * #2 is more or less okay indentation-wise, but at the moment there's a lot of desugaring + * left in place, and that's what we plan to improve in the future. + * printTypes, printIds, printPositions options have the same meaning as for TreePrinter + * printRootPkg option is available only for attributed trees. * + * @group Printers + */ + def showCode(tree: Tree, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printPositions: BooleanFlag = None, printRootPkg: Boolean = false) = + render(tree, newCodePrinter(_, tree, printRootPkg), printTypes, printIds, printOwners, printKinds = None, printMirrors = None, printPositions) + + /** + * Hook to define what `showCode(...)` means. * @group Printers */ - def showRaw(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None): String = - render(any, newRawTreePrinter(_), printTypes, printIds, printKinds, printMirrors) + protected def newCodePrinter(out: PrintWriter, tree: Tree, printRootPkg: Boolean): TreePrinter + + /** Renders internal structure of a reflection artifact as the + * visualization of a Scala syntax tree. + * + * @group Printers + */ + def showRaw(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newRawTreePrinter(_), printTypes, printIds, printOwners, printKinds, printMirrors, printPositions) /** Hook to define what `showRaw(...)` means. * @group Printers @@ -222,8 +254,23 @@ trait Printers { self: Universe => */ def show(flags: FlagSet): String + /** Renders a prettified representation of a position. + * @group Printers + */ + def show(position: Position): String + /** Renders internal structure of a flag set. * @group Printers */ def showRaw(flags: FlagSet): String = flags.toString + + /** Renders internal structure of a position. + * @group Printers + */ + def showRaw(position: Position): String = position.toString + + /** Renders a string that represents a declaration of this symbol written in Scala. + * @group Printers + */ + def showDecl(sym: Symbol): String } diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala new file mode 100644 index 0000000000..e905aa4153 --- /dev/null +++ b/src/reflect/scala/reflect/api/Quasiquotes.scala @@ -0,0 +1,25 @@ +package scala.reflect +package api + +trait Quasiquotes { self: Universe => + + /** Implicit class that introduces `q`, `tq`, `cq,` `p` and `fq` string interpolators + * that are also known as quasiquotes. With their help you can easily manipulate + * Scala reflection ASTs. + * + * @see [[http://docs.scala-lang.org/overviews/quasiquotes/intro.html]] + */ + implicit class Quasiquote(ctx: StringContext) { + protected trait api { + // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes` + // using the mechanism implemented in `scala.tools.reflect.FastTrack` + def apply[T](args: T*): Tree = macro ??? + def unapply(scrutinee: Any): Any = macro ??? + } + object q extends api + object tq extends api + object cq extends api + object pq extends api + object fq extends api + } +} diff --git a/src/reflect/scala/reflect/api/Scopes.scala b/src/reflect/scala/reflect/api/Scopes.scala index 7f9799393c..c9142fba47 100644 --- a/src/reflect/scala/reflect/api/Scopes.scala +++ b/src/reflect/scala/reflect/api/Scopes.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -15,7 +16,7 @@ package api * there is the `newScopeWith` function. * * Additional functionality is exposed in member scopes that are returned by - * `members` and `declarations` defined in [[scala.reflect.api.Types#TypeApi]]. + * `members` and `decls` defined in [[scala.reflect.api.Types#TypeApi]]. * Such scopes support the `sorted` method, which sorts members in declaration order. * * @group ReflectionAPI @@ -26,29 +27,18 @@ trait Scopes { self: Universe => * @template * @group Scopes */ - type Scope >: Null <: ScopeApi + type Scope >: Null <: AnyRef with ScopeApi /** The API that all scopes support * @group API */ trait ScopeApi extends Iterable[Symbol] - /** A tag that preserves the identity of the `Scope` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ScopeTag: ClassTag[Scope] - - /** Create a new scope with the given initial elements. - * @group Scopes - */ - def newScopeWith(elems: Symbol*): Scope - /** The type of member scopes, as in class definitions, for example. * @template * @group Scopes */ - type MemberScope >: Null <: Scope with MemberScopeApi + type MemberScope >: Null <: AnyRef with MemberScopeApi with Scope /** The API that all member scopes support * @group API @@ -61,10 +51,4 @@ trait Scopes { self: Universe => */ def sorted: List[Symbol] } - - /** A tag that preserves the identity of the `MemberScope` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MemberScopeTag: ClassTag[MemberScope] -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/api/StandardDefinitions.scala b/src/reflect/scala/reflect/api/StandardDefinitions.scala index 721b0bc7f2..524b7ea14b 100644 --- a/src/reflect/scala/reflect/api/StandardDefinitions.scala +++ b/src/reflect/scala/reflect/api/StandardDefinitions.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api /** @@ -130,10 +131,10 @@ trait StandardDefinitions { * scala> val m = typeOf[C].member(newTermName("m")).asMethod * m: reflect.runtime.universe.MethodSymbol = method m * - * scala> m.params(0)(0).typeSignature + * scala> m.params(0)(0).info * res1: reflect.runtime.universe.Type = => scala.Int * - * scala> showRaw(m.params(0)(0).typeSignature) + * scala> showRaw(m.params(0)(0).info) * res2: String = TypeRef( * ThisType(scala), * scala.<byname>, // <-- ByNameParamClass @@ -158,10 +159,10 @@ trait StandardDefinitions { * scala> val m = typeOf[C].member(newTermName("m")).asMethod * m: reflect.runtime.universe.MethodSymbol = method m * - * scala> m.params(0)(0).typeSignature + * scala> m.params(0)(0).info * res1: reflect.runtime.universe.Type = <repeated...>[Object] * - * scala> showRaw(m.params(0)(0).typeSignature) + * scala> showRaw(m.params(0)(0).info) * res2: String = TypeRef( * ThisType(scala), * scala.<repeated...>, // <-- JavaRepeatedParamClass @@ -183,10 +184,10 @@ trait StandardDefinitions { * scala> val m = typeOf[C].member(newTermName("m")).asMethod * m: reflect.runtime.universe.MethodSymbol = method m * - * scala> m.params(0)(0).typeSignature + * scala> m.params(0)(0).info * res1: reflect.runtime.universe.Type = scala.Int* * - * scala> showRaw(m.params(0)(0).typeSignature) + * scala> showRaw(m.params(0)(0).info) * res2: String = TypeRef( * ThisType(scala), * scala.<repeated>, // <-- RepeatedParamClass @@ -213,29 +214,43 @@ trait StandardDefinitions { /** The module symbol of module `scala.Some`. */ def SomeModule: ModuleSymbol - /** The array of class symbols for classes `scala.ProductX`. + /** Function-like api that lets you acess symbol + * of the definition with given arity and also look + * through all known symbols via `seq`. + */ + abstract class VarArityClassApi extends (Int => Symbol) { + def seq: Seq[ClassSymbol] + } + + /** Function-like object that maps arity to symbols for classes `scala.ProductX`. * - 0th element is `Unit` * - 1st element is `Product1` * - ... * - 22nd element is `Product22` + * - 23nd element is `NoSymbol` + * - ... */ - def ProductClass : Array[ClassSymbol] + def ProductClass: VarArityClassApi - /** The array of class symbols for classes `scala.FunctionX`. + /** Function-like object that maps arity to symbols for classes `scala.FunctionX`. * - 0th element is `Function0` * - 1st element is `Function1` * - ... * - 22nd element is `Function22` + * - 23nd element is `NoSymbol` + * - ... */ - def FunctionClass : Array[ClassSymbol] + def FunctionClass: VarArityClassApi - /** The array of class symbols for classes `scala.TupleX`. + /** Function-like object that maps arity to symbols for classes `scala.TupleX`. * - 0th element is `NoSymbol` - * - 1st element is `Product1` + * - 1st element is `Tuple1` * - ... - * - 22nd element is `Product22` + * - 22nd element is `Tuple22` + * - 23nd element is `NoSymbol` + * - ... */ - def TupleClass: Array[Symbol] // cannot make it Array[ClassSymbol], because TupleClass(0) is supposed to be NoSymbol. weird + def TupleClass: VarArityClassApi /** Contains Scala primitive value classes: * - Byte diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala new file mode 100644 index 0000000000..66ac62cc9e --- /dev/null +++ b/src/reflect/scala/reflect/api/StandardLiftables.scala @@ -0,0 +1,235 @@ +package scala.reflect +package api + +trait StandardLiftables { self: Universe => + import internal._ + import reificationSupport.{SyntacticTuple, ScalaDot} + + trait StandardLiftableInstances { + private def lift[T: Liftable](value: T): Tree = implicitly[Liftable[T]].apply(value) + private def selectScala(names: Name*) = names.tail.foldLeft(ScalaDot(names.head)) { Select(_, _) } + private def callScala(names: Name*)(args: List[Tree]) = Apply(selectScala(names: _*), args) + private def callCollection(name: Name)(args: List[Tree]) = callScala(stdnme.collection, stdnme.immutable, name)(args) + private def liftAsLiteral[T]: Liftable[T] = Liftable { v => Literal(Constant(v)) } + + implicit def liftByte[T <: Byte]: Liftable[T] = liftAsLiteral[T] + implicit def liftShort[T <: Short]: Liftable[T] = liftAsLiteral[T] + implicit def liftChar[T <: Char]: Liftable[T] = liftAsLiteral[T] + implicit def liftInt[T <: Int]: Liftable[T] = liftAsLiteral[T] + implicit def liftLong[T <: Long]: Liftable[T] = liftAsLiteral[T] + implicit def liftFloat[T <: Float]: Liftable[T] = liftAsLiteral[T] + implicit def liftDouble[T <: Double]: Liftable[T] = liftAsLiteral[T] + implicit def liftBoolean[T <: Boolean]: Liftable[T] = liftAsLiteral[T] + implicit def liftUnit: Liftable[Unit] = liftAsLiteral[Unit] + implicit def liftString[T <: String]: Liftable[T] = liftAsLiteral[T] + + implicit def liftScalaSymbol: Liftable[scala.Symbol] = Liftable { v => + callScala(stdnme.Symbol)(Literal(Constant(v.name)) :: Nil) + } + + implicit def liftTree[T <: Tree]: Liftable[T] = Liftable { identity } + implicit def liftName[T <: Name]: Liftable[T] = Liftable { name => Ident(name) } + implicit def liftExpr[T <: Expr[_]]: Liftable[T] = Liftable { expr => expr.tree } + implicit def liftType[T <: Type]: Liftable[T] = Liftable { tpe => TypeTree(tpe) } + implicit def liftTypeTag[T <: WeakTypeTag[_]]: Liftable[T] = Liftable { ttag => TypeTree(ttag.tpe) } + implicit def liftConstant[T <: Constant]: Liftable[T] = Liftable { const => Literal(const) } + + implicit def liftArray[T: Liftable]: Liftable[Array[T]] = Liftable { arr => callScala(stdnme.Array)(arr.map(lift(_)).toList) } + implicit def liftVector[T: Liftable]: Liftable[Vector[T]] = Liftable { vect => callCollection(stdnme.Vector)(vect.map(lift(_)).toList) } + implicit def liftList[T: Liftable]: Liftable[List[T]] = Liftable { lst => callCollection(stdnme.List)(lst.map(lift(_))) } + implicit def liftNil: Liftable[Nil.type] = Liftable { _ => selectScala(stdnme.collection, stdnme.immutable, stdnme.Nil) } + implicit def liftMap[K: Liftable, V: Liftable]: Liftable[Map[K, V]] = Liftable { m => callCollection(stdnme.Map)(m.toList.map(lift(_))) } + implicit def liftSet[T: Liftable]: Liftable[Set[T]] = Liftable { s => callCollection(stdnme.Set)(s.toList.map(lift(_))) } + + implicit def liftSome[T: Liftable]: Liftable[Some[T]] = Liftable { case Some(v) => callScala(stdnme.Some)(lift(v) :: Nil) } + implicit def liftNone: Liftable[None.type] = Liftable { _ => selectScala(stdnme.None) } + implicit def liftOption[T: Liftable]: Liftable[Option[T]] = Liftable { + case some: Some[T] => lift(some) + case none: None.type => lift(none) + } + + implicit def liftLeft[L: Liftable, R]: Liftable[Left[L, R]] = Liftable { case Left(v) => callScala(stdnme.util, stdnme.Left)(lift(v) :: Nil) } + implicit def liftRight[L, R: Liftable]: Liftable[Right[L, R]] = Liftable { case Right(v) => callScala(stdnme.util, stdnme.Right)(lift(v) :: Nil) } + implicit def liftEither[L: Liftable, R: Liftable]: Liftable[Either[L, R]] = Liftable { + case left: Left[L, R] => lift(left) + case right: Right[L, R] => lift(right) + } + + implicit def liftTuple2[T1, T2](implicit liftT1: Liftable[T1], liftT2: Liftable[T2]): Liftable[Tuple2[T1, T2]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: Nil) + } + implicit def liftTuple3[T1, T2, T3](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3]): Liftable[Tuple3[T1, T2, T3]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: Nil) + } + implicit def liftTuple4[T1, T2, T3, T4](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4]): Liftable[Tuple4[T1, T2, T3, T4]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: Nil) + } + implicit def liftTuple5[T1, T2, T3, T4, T5](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5]): Liftable[Tuple5[T1, T2, T3, T4, T5]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: Nil) + } + implicit def liftTuple6[T1, T2, T3, T4, T5, T6](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6]): Liftable[Tuple6[T1, T2, T3, T4, T5, T6]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: Nil) + } + implicit def liftTuple7[T1, T2, T3, T4, T5, T6, T7](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7]): Liftable[Tuple7[T1, T2, T3, T4, T5, T6, T7]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: Nil) + } + implicit def liftTuple8[T1, T2, T3, T4, T5, T6, T7, T8](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8]): Liftable[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: Nil) + } + implicit def liftTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9]): Liftable[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: Nil) + } + implicit def liftTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10]): Liftable[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: Nil) + } + implicit def liftTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11]): Liftable[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: Nil) + } + implicit def liftTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12]): Liftable[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: Nil) + } + implicit def liftTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13]): Liftable[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: Nil) + } + implicit def liftTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14]): Liftable[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: Nil) + } + implicit def liftTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15]): Liftable[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: Nil) + } + implicit def liftTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16]): Liftable[Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: Nil) + } + implicit def liftTuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17]): Liftable[Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: Nil) + } + implicit def liftTuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17], liftT18: Liftable[T18]): Liftable[Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: liftT18(t._18) :: Nil) + } + implicit def liftTuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17], liftT18: Liftable[T18], liftT19: Liftable[T19]): Liftable[Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: liftT18(t._18) :: liftT19(t._19) :: Nil) + } + implicit def liftTuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17], liftT18: Liftable[T18], liftT19: Liftable[T19], liftT20: Liftable[T20]): Liftable[Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: liftT18(t._18) :: liftT19(t._19) :: liftT20(t._20) :: Nil) + } + implicit def liftTuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17], liftT18: Liftable[T18], liftT19: Liftable[T19], liftT20: Liftable[T20], liftT21: Liftable[T21]): Liftable[Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: liftT18(t._18) :: liftT19(t._19) :: liftT20(t._20) :: liftT21(t._21) :: Nil) + } + implicit def liftTuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](implicit liftT1: Liftable[T1], liftT2: Liftable[T2], liftT3: Liftable[T3], liftT4: Liftable[T4], liftT5: Liftable[T5], liftT6: Liftable[T6], liftT7: Liftable[T7], liftT8: Liftable[T8], liftT9: Liftable[T9], liftT10: Liftable[T10], liftT11: Liftable[T11], liftT12: Liftable[T12], liftT13: Liftable[T13], liftT14: Liftable[T14], liftT15: Liftable[T15], liftT16: Liftable[T16], liftT17: Liftable[T17], liftT18: Liftable[T18], liftT19: Liftable[T19], liftT20: Liftable[T20], liftT21: Liftable[T21], liftT22: Liftable[T22]): Liftable[Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]] = Liftable { t => + SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: liftT3(t._3) :: liftT4(t._4) :: liftT5(t._5) :: liftT6(t._6) :: liftT7(t._7) :: liftT8(t._8) :: liftT9(t._9) :: liftT10(t._10) :: liftT11(t._11) :: liftT12(t._12) :: liftT13(t._13) :: liftT14(t._14) :: liftT15(t._15) :: liftT16(t._16) :: liftT17(t._17) :: liftT18(t._18) :: liftT19(t._19) :: liftT20(t._20) :: liftT21(t._21) :: liftT22(t._22) :: Nil) + } + } + + trait StandardUnliftableInstances { + private def unliftPrimitive[Unboxed: ClassTag, Boxed: ClassTag] = Unliftable[Unboxed] { + case Literal(Constant(value)) + if value.getClass == implicitly[ClassTag[Boxed]].runtimeClass + || value.getClass == implicitly[ClassTag[Unboxed]].runtimeClass => + value.asInstanceOf[Unboxed] + } + implicit def unliftByte: Unliftable[Byte] = unliftPrimitive[Byte, java.lang.Byte] + implicit def unliftShort: Unliftable[Short] = unliftPrimitive[Short, java.lang.Short] + implicit def unliftChar: Unliftable[Char] = unliftPrimitive[Char, java.lang.Character] + implicit def unliftInt: Unliftable[Int] = unliftPrimitive[Int, java.lang.Integer] + implicit def unliftLong: Unliftable[Long] = unliftPrimitive[Long, java.lang.Long] + implicit def unliftFloat: Unliftable[Float] = unliftPrimitive[Float, java.lang.Float] + implicit def unliftDouble: Unliftable[Double] = unliftPrimitive[Double, java.lang.Double] + implicit def unliftBoolean: Unliftable[Boolean] = unliftPrimitive[Boolean, java.lang.Boolean] + implicit def unliftUnit: Unliftable[Unit] = unliftPrimitive[Unit, scala.runtime.BoxedUnit] + implicit def unliftString: Unliftable[String] = Unliftable { case Literal(Constant(s: String)) => s } + + implicit def unliftScalaSymbol: Unliftable[scala.Symbol] = Unliftable { + case Apply(ScalaDot(stdnme.Symbol), List(Literal(Constant(name: String)))) => scala.Symbol(name) + } + + implicit def unliftName[T <: Name : ClassTag]: Unliftable[T] = Unliftable[T] { case Ident(name: T) => name; case Bind(name: T, Ident(stdnme.WILDCARD)) => name } + implicit def unliftType: Unliftable[Type] = Unliftable[Type] { case tt: TypeTree if tt.tpe != null => tt.tpe } + implicit def unliftConstant: Unliftable[Constant] = Unliftable[Constant] { case Literal(const) => const } + + implicit def unliftTuple2[T1, T2](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2]): Unliftable[Tuple2[T1, T2]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: Nil) => Tuple2(v1, v2) + } + implicit def unliftTuple3[T1, T2, T3](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3]): Unliftable[Tuple3[T1, T2, T3]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: Nil) => Tuple3(v1, v2, v3) + } + implicit def unliftTuple4[T1, T2, T3, T4](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4]): Unliftable[Tuple4[T1, T2, T3, T4]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: Nil) => Tuple4(v1, v2, v3, v4) + } + implicit def unliftTuple5[T1, T2, T3, T4, T5](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5]): Unliftable[Tuple5[T1, T2, T3, T4, T5]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: Nil) => Tuple5(v1, v2, v3, v4, v5) + } + implicit def unliftTuple6[T1, T2, T3, T4, T5, T6](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6]): Unliftable[Tuple6[T1, T2, T3, T4, T5, T6]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: Nil) => Tuple6(v1, v2, v3, v4, v5, v6) + } + implicit def unliftTuple7[T1, T2, T3, T4, T5, T6, T7](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7]): Unliftable[Tuple7[T1, T2, T3, T4, T5, T6, T7]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: Nil) => Tuple7(v1, v2, v3, v4, v5, v6, v7) + } + implicit def unliftTuple8[T1, T2, T3, T4, T5, T6, T7, T8](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8]): Unliftable[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: Nil) => Tuple8(v1, v2, v3, v4, v5, v6, v7, v8) + } + implicit def unliftTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9]): Unliftable[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: Nil) => Tuple9(v1, v2, v3, v4, v5, v6, v7, v8, v9) + } + implicit def unliftTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10]): Unliftable[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: Nil) => Tuple10(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) + } + implicit def unliftTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11]): Unliftable[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: Nil) => Tuple11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) + } + implicit def unliftTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12]): Unliftable[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: Nil) => Tuple12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) + } + implicit def unliftTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13]): Unliftable[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: Nil) => Tuple13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) + } + implicit def unliftTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14]): Unliftable[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: Nil) => Tuple14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) + } + implicit def unliftTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15]): Unliftable[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: Nil) => Tuple15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) + } + implicit def unliftTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16]): Unliftable[Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: Nil) => Tuple16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) + } + implicit def unliftTuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17]): Unliftable[Tuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: Nil) => Tuple17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) + } + implicit def unliftTuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17], UnliftT18: Unliftable[T18]): Unliftable[Tuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: Nil) => Tuple18(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) + } + implicit def unliftTuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17], UnliftT18: Unliftable[T18], UnliftT19: Unliftable[T19]): Unliftable[Tuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: UnliftT19(v19) :: Nil) => Tuple19(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) + } + implicit def unliftTuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17], UnliftT18: Unliftable[T18], UnliftT19: Unliftable[T19], UnliftT20: Unliftable[T20]): Unliftable[Tuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: UnliftT19(v19) :: UnliftT20(v20) :: Nil) => Tuple20(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) + } + implicit def unliftTuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17], UnliftT18: Unliftable[T18], UnliftT19: Unliftable[T19], UnliftT20: Unliftable[T20], UnliftT21: Unliftable[T21]): Unliftable[Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: UnliftT19(v19) :: UnliftT20(v20) :: UnliftT21(v21) :: Nil) => Tuple21(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) + } + implicit def unliftTuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2], UnliftT3: Unliftable[T3], UnliftT4: Unliftable[T4], UnliftT5: Unliftable[T5], UnliftT6: Unliftable[T6], UnliftT7: Unliftable[T7], UnliftT8: Unliftable[T8], UnliftT9: Unliftable[T9], UnliftT10: Unliftable[T10], UnliftT11: Unliftable[T11], UnliftT12: Unliftable[T12], UnliftT13: Unliftable[T13], UnliftT14: Unliftable[T14], UnliftT15: Unliftable[T15], UnliftT16: Unliftable[T16], UnliftT17: Unliftable[T17], UnliftT18: Unliftable[T18], UnliftT19: Unliftable[T19], UnliftT20: Unliftable[T20], UnliftT21: Unliftable[T21], UnliftT22: Unliftable[T22]): Unliftable[Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]] = Unliftable { + case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: UnliftT19(v19) :: UnliftT20(v20) :: UnliftT21(v21) :: UnliftT22(v22) :: Nil) => Tuple22(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) + } + } + + // names used internally by implementations of standard liftables and unliftables + // can't be `private object nme` because of https://groups.google.com/forum/#!topic/scala-internals/b-Full9WZeE + // can't be `private[this] object nme` because then STARR has problems prioritizing this.nme over self.nme + // therefore I'm essentially forced to give this object a non-standard name + private object stdnme { + val Array = TermName("Array") + val collection = TermName("collection") + val immutable = TermName("immutable") + val Left = TermName("Left") + val List = TermName("List") + val Map = TermName("Map") + val None = TermName("None") + val Nil = TermName("Nil") + val Right = TermName("Right") + val Set = TermName("Set") + val Some = TermName("Some") + val Symbol = TermName("Symbol") + val util = TermName("util") + val Vector = TermName("Vector") + val WILDCARD = self.nme.WILDCARD + } +} diff --git a/src/reflect/scala/reflect/api/StandardNames.scala b/src/reflect/scala/reflect/api/StandardNames.scala index 4886e4f8f7..19bdfcae59 100644 --- a/src/reflect/scala/reflect/api/StandardNames.scala +++ b/src/reflect/scala/reflect/api/StandardNames.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api // Q: I have a pretty name. Can I put it here? @@ -27,15 +28,23 @@ package api trait StandardNames { self: Universe => + /** @see [[termNames]] */ + @deprecated("Use `termNames` instead", "2.11.0") + val nme: TermNamesApi + /** A value containing all [[TermNamesApi standard term names]]. * @group StandardNames */ - val nme: TermNamesApi + val termNames: TermNamesApi + + /** @see [[typeNames]] */ + @deprecated("Use `typeNames` instead", "2.11.0") + val tpnme: TypeNamesApi /** A value containing all [[TypeNamesApi standard type names]]. * @group StandardNames */ - val tpnme: TypeNamesApi + val typeNames: TypeNamesApi /** Defines standard names, common for term and type names: These can be accessed via the [[nme]] and [[tpnme]] members. * @group API @@ -84,6 +93,11 @@ trait StandardNames { */ val ROOTPKG: NameType + /** The term name `<empty>`. + * Represents the empty package. + */ + val EMPTY_PACKAGE_NAME: NameType + /** The string " " (a single whitespace). * `LOCAL_SUFFIX_STRING` is appended to the names of local identifiers, * when it's necessary to prevent a naming conflict. For example, underlying fields diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index c8e03f1d91..dddd3c0e61 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -29,7 +30,7 @@ package api * scala> val test = typeOf[C[Int]].member(newTermName("test")).asMethod * test: reflect.runtime.universe.MethodSymbol = method test * - * scala> test.typeSignature + * scala> test.info * res0: reflect.runtime.universe.Type = [U](x: T)(y: U)scala.Int * }}} * @@ -59,99 +60,39 @@ trait Symbols { self: Universe => * @group Symbols * @template */ - type Symbol >: Null <: SymbolApi - - /** A tag that preserves the identity of the `Symbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SymbolTag: ClassTag[Symbol] + type Symbol >: Null <: AnyRef with SymbolApi /** The type of type symbols representing type, class, and trait declarations, * as well as type parameters. * @group Symbols * @template */ - type TypeSymbol >: Null <: Symbol with TypeSymbolApi - - /** A tag that preserves the identity of the `TypeSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeSymbolTag: ClassTag[TypeSymbol] + type TypeSymbol >: Null <: TypeSymbolApi with Symbol /** The type of term symbols representing val, var, def, and object declarations as * well as packages and value parameters. * @group Symbols * @template */ - type TermSymbol >: Null <: Symbol with TermSymbolApi - - /** A tag that preserves the identity of the `TermSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TermSymbolTag: ClassTag[TermSymbol] + type TermSymbol >: Null <: TermSymbolApi with Symbol /** The type of method symbols representing def declarations. * @group Symbols * @template */ - type MethodSymbol >: Null <: TermSymbol with MethodSymbolApi - - /** A tag that preserves the identity of the `MethodSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MethodSymbolTag: ClassTag[MethodSymbol] + type MethodSymbol >: Null <: MethodSymbolApi with TermSymbol /** The type of module symbols representing object declarations. * @group Symbols * @template */ - type ModuleSymbol >: Null <: TermSymbol with ModuleSymbolApi - - /** A tag that preserves the identity of the `ModuleSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ModuleSymbolTag: ClassTag[ModuleSymbol] + type ModuleSymbol >: Null <: ModuleSymbolApi with TermSymbol /** The type of class symbols representing class and trait definitions. * @group Symbols * @template */ - type ClassSymbol >: Null <: TypeSymbol with ClassSymbolApi - - /** A tag that preserves the identity of the `ClassSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ClassSymbolTag: ClassTag[ClassSymbol] - - /** The type of free terms introduced by reification. - * @group Symbols - * @template - */ - type FreeTermSymbol >: Null <: TermSymbol with FreeTermSymbolApi - - /** A tag that preserves the identity of the `FreeTermSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FreeTermSymbolTag: ClassTag[FreeTermSymbol] - - /** The type of free types introduced by reification. - * @group Symbols - * @template - */ - type FreeTypeSymbol >: Null <: TypeSymbol with FreeTypeSymbolApi - - /** A tag that preserves the identity of the `FreeTypeSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FreeTypeSymbolTag: ClassTag[FreeTypeSymbol] + type ClassSymbol >: Null <: ClassSymbolApi with TypeSymbol /** A special "missing" symbol. Commonly used in the API to denote a default or empty value. * @group Symbols @@ -178,12 +119,8 @@ trait Symbols { self: Universe => * @groupdesc Helpers These methods enable collections-like operations on symbols. * @groupname Type TypeSymbol Members * @groupprio Type -1 - * @groupname FreeType FreeType Symbol Members - * @groupprio FreeType -2 * @groupname Term TermSymbol Members * @groupprio Term -1 - * @groupname FreeTerm FreeTerm Symbol Members - * @groupprio FreeTerm -2 * @groupname Class Class Symbol Members * @groupprio Class -2 * @groupname Method Method Symbol Members @@ -218,7 +155,7 @@ trait Symbols { self: Universe => /** The name of the symbol as a member of the `Name` type. * @group Basics */ - def name: Name + def name: NameType /** The encoded full path name of this symbol, where outer names and inner names * are separated by periods. @@ -226,6 +163,9 @@ trait Symbols { self: Universe => */ def fullName: String + /** Position of the tree. */ + def pos: Position + /** Does this symbol represent the definition of a type? * Note that every symbol is either a term or a type. * So for every symbol `sym` (except for `NoSymbol`), @@ -245,7 +185,7 @@ trait Symbols { self: Universe => /** Does this symbol represent the definition of a term? * Note that every symbol is either a term or a type. * So for every symbol `sym` (except for `NoSymbol`), - * either `sym.isTerm` is true or `sym.isTerm` is true. + * either `sym.isTerm` is true or `sym.isType` is true. * * @group Tests */ @@ -265,6 +205,15 @@ trait Symbols { self: Universe => */ def isMethod: Boolean = false + /** Does this method represent a constructor? + * + * If `owner` is a class, then this is a vanilla JVM constructor. + * If `owner` is a trait, then this is a mixin constructor. + * + * @group Method + */ + def isConstructor: Boolean + /** This symbol cast to a MethodSymbol. * @throws ScalaReflectionException if `isMethod` is false. * @@ -322,45 +271,6 @@ trait Symbols { self: Universe => */ def asClass: ClassSymbol = throw new ScalaReflectionException(s"$this is not a class") - /** Does this symbol represent a free term captured by reification? - * If yes, `isTerm` is also guaranteed to be true. - * - * @group Tests - */ - def isFreeTerm: Boolean = false - - /** This symbol cast to a free term symbol. - * @throws ScalaReflectionException if `isFreeTerm` is false. - * - * @group Conversions - */ - def asFreeTerm: FreeTermSymbol = throw new ScalaReflectionException(s"$this is not a free term") - - /** Does this symbol represent a free type captured by reification? - * If yes, `isType` is also guaranteed to be true. - * - * @group Tests - */ - def isFreeType: Boolean = false - - /** This symbol cast to a free type symbol. - * @throws ScalaReflectionException if `isFreeType` is false. - * - * @group Conversions - */ - def asFreeType: FreeTypeSymbol = throw new ScalaReflectionException(s"$this is not a free type") - - /** @group Constructors */ - def newTermSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol - /** @group Constructors */ - def newModuleAndClassSymbol(name: Name, pos: Position = NoPosition, flags: FlagSet = NoFlags): (ModuleSymbol, ClassSymbol) - /** @group Constructors */ - def newMethodSymbol(name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): MethodSymbol - /** @group Constructors */ - def newTypeSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TypeSymbol - /** @group Constructors */ - def newClassSymbol(name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): ClassSymbol - /** Source file if this symbol is created during this compilation run, * or a class file if this symbol is loaded from a *.class or *.jar. * @@ -370,6 +280,7 @@ trait Symbols { self: Universe => * * @group Basics */ + @deprecated("Use `pos.source.file` instead", "2.11.0") def associatedFile: scala.reflect.io.AbstractFile /** A list of annotations attached to this Symbol. @@ -382,15 +293,32 @@ trait Symbols { self: Universe => * For a module: the class with the same name in the same package. * For all others: NoSymbol * + * This API may return unexpected results for module classes, packages and package classes. + * Use `companion` instead in order to get predictable results. + * * @group Basics */ + @deprecated("Use `companion` instead, but beware of possible changes in behavior", "2.11.0") def companionSymbol: Symbol + /** For a class: its companion object if exists. + * For a module or a module class: companion class of the module if exists. + * For a package or a package class: NoSymbol. + * For all others: NoSymbol. + */ + def companion: Symbol + + /** @see [[infoIn]] */ + def typeSignatureIn(site: Type): Type + /** The type signature of this symbol seen as a member of given type `site`. * * @group Basics */ - def typeSignatureIn(site: Type): Type + def infoIn(site: Type): Type + + /** @see [[info]] */ + def typeSignature: Type /** The type signature of this symbol. * @@ -398,17 +326,27 @@ trait Symbols { self: Universe => * instantiation of a generic type. For example, signature * of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`, * will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature - * with type parameters appropriately instantiated, one should use `typeSignatureIn`. + * with type parameters appropriately instantiated, one should use `infoIn`. * * @group Basics */ - def typeSignature: Type + def info: Type + + /** @see [[overrides]] */ + @deprecated("Use `overrides` instead", "2.11.0") + def allOverriddenSymbols: List[Symbol] /** Returns all symbols overriden by this symbol. * * @group Basics */ - def allOverriddenSymbols: List[Symbol] + def overrides: List[Symbol] + + /** The overloaded alternatives of this symbol + * + * @group Basics + */ + def alternatives: List[Symbol] /******************* tests *******************/ @@ -427,16 +365,13 @@ trait Symbols { self: Universe => */ def isImplementationArtifact: Boolean - /** Does this symbol represent a local declaration or definition? - * - * If yes, either `isPrivate` or `isProtected` are guaranteed to be true. - * Local symbols can only be accessed from the same object instance. - * - * If yes, `privateWithin` might tell more about this symbol's visibility scope. + /** Does this symbol represent a declaration or definition written in a source file as `private[this]` + * or generated in tree/symbol form with the combination of flags LOCAL and PRIVATE? + * If yes, `isPrivate` is guaranteed to be true, * * @group Tests */ - def isLocal: Boolean + def isPrivateThis: Boolean /** Does this symbol represent a private declaration or definition? * If yes, `privateWithin` might tell more about this symbol's visibility scope. @@ -445,6 +380,14 @@ trait Symbols { self: Universe => */ def isPrivate: Boolean + /** Does this symbol represent a declaration or definition written in a source file as `protected[this]` + * or generated in tree/symbol form with the combination of flags LOCAL and PROTECTED? + * If yes, `isProtected` is guaranteed to be true, + * + * @group Tests + */ + def isProtectedThis: Boolean + /** Does this symbol represent a protected declaration or definition? * If yes, `privateWithin` might tell more about this symbol's visibility scope. * @@ -459,7 +402,7 @@ trait Symbols { self: Universe => def isPublic: Boolean /** - * Set when symbol has a modifier of the form private[X], NoSymbol otherwise. + * Set when symbol has a modifier of the form private[X] or protected[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]) @@ -488,7 +431,7 @@ trait Symbols { self: Universe => def privateWithin: Symbol /** Does this symbol represent the definition of a package? - * If yes, `isTerm` is also guaranteed to be true. + * Known issues: [[https://issues.scala-lang.org/browse/SI-6732]]. * * @group Tests */ @@ -501,12 +444,6 @@ trait Symbols { self: Universe => */ def isPackageClass: Boolean - /** Does this symbol or its underlying type represent a typechecking error? - * - * @group Tests - */ - def isErroneous : 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. @@ -522,11 +459,11 @@ trait Symbols { self: Universe => */ def isFinal: Boolean - /** Is this symbol overriding something? + /** Is this symbol abstract (i.e. an abstract class, an abstract method, value or type member)? * * @group Tests */ - def isOverride: Boolean + def isAbstract: Boolean /** Is this symbol labelled as "abstract override"? * @@ -660,12 +597,6 @@ trait Symbols { self: Universe => */ def isLazy: Boolean - /** The overloaded alternatives of this symbol - * - * @group Term - */ - def alternatives: List[Symbol] - /** Used to provide a better error message for `asMethod` */ override protected def isOverloadedMethod = alternatives exists (_.isMethod) @@ -751,7 +682,7 @@ trait Symbols { self: Universe => * Example: Given a class declaration `class C[T] { ... } `, that generates a symbol * `C`. Then `C.toType` is the type `C[T]`. * - * By contrast, `C.typeSignature` would be a type signature of form + * By contrast, `C.info` would be a type signature of form * `PolyType(ClassInfoType(...))` that describes type parameters, value * parameters, parent types, and members of `C`. * @@ -774,13 +705,6 @@ trait Symbols { self: Universe => */ 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. - * - * @group Type - */ - def isSkolem : Boolean - /** Does this symbol represent the definition of a type alias? * * @group Type @@ -791,6 +715,7 @@ trait Symbols { self: Universe => * * @group Type */ + @deprecated("Use isAbstract instead", "2.11.0") def isAbstractType : Boolean /** Does this symbol represent an existentially bound type? @@ -816,15 +741,6 @@ trait Symbols { self: Universe => final override def isMethod = true final override def asMethod = this - /** Does this method represent a constructor? - * - * If `owner` is a class, then this is a vanilla JVM constructor. - * If `owner` is a trait, then this is a mixin constructor. - * - * @group Method - */ - def isConstructor: Boolean - /** Does this symbol denote the primary constructor of its enclosing class? * * @group Method @@ -837,6 +753,10 @@ trait Symbols { self: Universe => */ def typeParams: List[Symbol] + /** @see [[paramLists]] */ + @deprecated("Use `paramLists` instead", "2.11.0") + def paramss: List[List[Symbol]] + /** All parameter lists of the method. * The name ending with "ss" indicates that the result type is a list of lists. * @@ -846,7 +766,7 @@ trait Symbols { self: Universe => * * @group Method */ - def paramss: List[List[Symbol]] + def paramLists: List[List[Symbol]] /** Does this method support variable length argument lists? * @@ -859,6 +779,14 @@ trait Symbols { self: Universe => * @group Method */ def returnType: Type + + /** Exceptions that this method is known to throw. + * For Scala methods, the list is calculated from [[throws]] annotations present on a method. + * For Java methods, the list is calculated from `throws` clauses attached to the method and stored in bytecode. + * + * @group Method + */ + def exceptions: List[Symbol] } /** The API of module symbols. @@ -924,6 +852,7 @@ trait Symbols { self: Universe => * * @group Class */ + @deprecated("Use isAbstract instead", "2.11.0") def isAbstractClass: Boolean /** Does this symbol represent a case class? @@ -973,50 +902,37 @@ trait Symbols { self: Universe => */ def thisPrefix: Type - /** For a polymorphic class/trait, its type parameters, the empty list for all other classes/trait + /** The type `C.super[M]`, where `C` is the current class and `M` is supertpe. * * @group Class */ - def typeParams: List[Symbol] - } + def superPrefix(supertpe: Type): Type - /** The API of free term symbols. - * The main source of information about symbols is the [[Symbols]] page. - * - * $SYMACCESSORS - * @group API - */ - trait FreeTermSymbolApi extends TermSymbolApi { this: FreeTermSymbol => - final override def isFreeTerm = true - final override def asFreeTerm = this - - /** The place where this symbol has been spawned + /** For a polymorphic class/trait, its type parameters, the empty list for all other classes/trait * - * @group FreeTerm + * @group Class */ - def origin: String + def typeParams: List[Symbol] - /** The valus this symbol refers to + /** For a Scala class or module class, the primary constructor of the class. + * For a Scala trait, its mixin constructor. + * For a Scala package class, NoSymbol. + * For a Java class, NoSymbol. * - * @group FreeTerm - */ - def value: Any - } - - /** The API of free type symbols. - * The main source of information about symbols is the [[Symbols]] page. - * - * $SYMACCESSORS - * @group API - */ - trait FreeTypeSymbolApi extends TypeSymbolApi { this: FreeTypeSymbol => - final override def isFreeType = true - final override def asFreeType = this - - /** The place where this symbol has been spawned + * Known issues: Due to SI-8367, primaryConstructor may return unexpected results + * when called for Java classes (for some vague definition of a "Java class", which apparently + * not only includes javac-produced classfiles, but also consists of classes defined in + * Scala programs under the java.lang package). What's even worse, for some Java classes + * we can't even guarantee stability of the return value - depending on your classloader configuration + * and/or JDK version you might get different primaryConstructor for the same ClassSymbol. + * We have logged these issues at SI-8193. * - * @group FreeType + * @group Class */ - def origin: String + // TODO: SI-8193 I think we should only return a non-empty symbol if called for Scala classes + // returning something for traits and module classes is outright confusing + // This, however, will require some refactoring in the compiler, so I'll leave it for later + // as at the moment we don't have time or risk tolerance for that + def primaryConstructor: Symbol } } diff --git a/src/reflect/scala/reflect/api/TagInterop.scala b/src/reflect/scala/reflect/api/TagInterop.scala deleted file mode 100644 index 5de811578e..0000000000 --- a/src/reflect/scala/reflect/api/TagInterop.scala +++ /dev/null @@ -1,43 +0,0 @@ -package scala.reflect -package api - -/** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> - * - * This trait provides type tag <-> manifest interoperability. - * @group ReflectionAPI - * - * @groupname TagInterop TypeTag and Manifest Interoperability - */ -trait TagInterop { self: Universe => - // TODO `mirror` parameters are now of type `Any`, because I can't make these path-dependent types work - // if you're brave enough, replace `Any` with `Mirror`, recompile and run interop_typetags_are_manifests.scala - - /** - * Convert a [[scala.reflect.api.TypeTags#TypeTag]] to a [[scala.reflect.Manifest]]. - * - * Compiler usually generates these conversions automatically, when a type tag for a type `T` is in scope, - * and an implicit of type `Manifest[T]` is requested, but this method can also be called manually. - * For example: - * {{{ - * typeTagToManifest(scala.reflect.runtime.currentMirror, implicitly[TypeTag[String]]) - * }}} - * @group TagInterop - */ - def typeTagToManifest[T: ClassTag](mirror: Any, tag: Universe#TypeTag[T]): Manifest[T] = - throw new UnsupportedOperationException("This universe does not support tag -> manifest conversions. Use a JavaUniverse, e.g. the scala.reflect.runtime.universe.") - - /** - * Convert a [[scala.reflect.Manifest]] to a [[scala.reflect.api.TypeTags#TypeTag]]. - * - * Compiler usually generates these conversions automatically, when a manifest for a type `T` is in scope, - * and an implicit of type `TypeTag[T]` is requested, but this method can also be called manually. - * For example: - * {{{ - * manifestToTypeTag(scala.reflect.runtime.currentMirror, implicitly[Manifest[String]]) - * }}} - * @group TagInterop - */ - def manifestToTypeTag[T](mirror: Any, manifest: Manifest[T]): Universe#TypeTag[T] = - throw new UnsupportedOperationException("This universe does not support manifest -> tag conversions. Use a JavaUniverse, e.g. the scala.reflect.runtime.universe.") -} diff --git a/src/reflect/scala/reflect/api/TreeCreator.scala b/src/reflect/scala/reflect/api/TreeCreator.scala index 6969418470..027c840955 100644 --- a/src/reflect/scala/reflect/api/TreeCreator.scala +++ b/src/reflect/scala/reflect/api/TreeCreator.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** This is an internal implementation class. diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 0937a93738..ff8926651b 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api /** @@ -58,13 +59,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Tree >: Null <: TreeApi - - /** A tag that preserves the identity of the `Tree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TreeTag: ClassTag[Tree] + type Tree >: Null <: AnyRef with TreeApi /** The API that all trees support. * The main source of information about trees is the [[scala.reflect.api.Trees]] page. @@ -75,11 +70,25 @@ trait Trees { self: Universe => def isDef: Boolean /** Is this tree one of the empty trees? - * Empty trees are: the `EmptyTree` null object, `TypeTree` instances that don't carry a type - * and the special `emptyValDef` singleton. + * + * Empty trees are: the `EmptyTree` null object and `TypeTree` instances that don't carry a type. + * + * @see `canHaveAttrs` */ def isEmpty: Boolean + /** Is this tree not an empty tree? + * + * @see `isEmpty` + */ + def nonEmpty: Boolean + + /** Can this tree carry attributes (i.e. symbols, types or positions)? + * Typically the answer is yes, except for the `EmptyTree` null object and + * two special singletons: `noSelfType` and `pendingSuperCall`. + */ + def canHaveAttrs: Boolean + /** The canonical way to test if a Tree represents a term. */ def isTerm: Boolean @@ -159,29 +168,6 @@ trait Trees { self: Universe => */ 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). @@ -207,13 +193,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TermTree >: Null <: AnyRef with Tree with TermTreeApi - - /** A tag that preserves the identity of the `TermTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TermTreeTag: ClassTag[TermTree] + type TermTree >: Null <: TermTreeApi with Tree /** The API that all term trees support * @group API @@ -226,13 +206,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TypTree >: Null <: AnyRef with Tree with TypTreeApi - - /** A tag that preserves the identity of the `TypTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypTreeTag: ClassTag[TypTree] + type TypTree >: Null <: TypTreeApi with Tree /** The API that all typ trees support * @group API @@ -240,17 +214,14 @@ trait Trees { self: Universe => trait TypTreeApi extends TreeApi { this: TypTree => } - /** A tree with a mutable symbol field, initialized to NoSymbol. + /** A tree that carries a symbol, e.g. by defining it (`DefTree`) or by referring to it (`RefTree`). + * Such trees start their life naked, returning `NoSymbol`, but after being typechecked without errors + * they hold non-empty symbols. + * * @group Trees * @template */ - type SymTree >: Null <: AnyRef with Tree with SymTreeApi - - /** A tag that preserves the identity of the `SymTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SymTreeTag: ClassTag[SymTree] + type SymTree >: Null <: SymTreeApi with Tree /** The API that all sym trees support * @group API @@ -260,24 +231,18 @@ trait Trees { self: Universe => def symbol: Symbol } - /** A tree with a name - effectively, a DefTree or RefTree. + /** A tree that carries a name, e.g. by defining it (`DefTree`) or by referring to it (`RefTree`). * @group Trees * @template */ - type NameTree >: Null <: AnyRef with Tree with NameTreeApi - - /** A tag that preserves the identity of the `NameTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NameTreeTag: ClassTag[NameTree] + type NameTree >: Null <: NameTreeApi with Tree /** The API that all name trees support * @group API */ trait NameTreeApi extends TreeApi { this: NameTree => /** The underlying name. - * For example, the `<List>` part of `Ident("List": TermName)`. + * For example, the `List` part of `Ident(TermName("List"))`. */ def name: Name } @@ -288,20 +253,14 @@ trait Trees { self: Universe => * @group Trees * @template */ - type RefTree >: Null <: SymTree with NameTree with RefTreeApi - - /** A tag that preserves the identity of the `RefTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val RefTreeTag: ClassTag[RefTree] + type RefTree >: Null <: RefTreeApi with SymTree with NameTree /** The API that all ref trees support * @group API */ trait RefTreeApi extends SymTreeApi with NameTreeApi { this: RefTree => /** The qualifier of the reference. - * For example, the `<scala>` part of `Select("scala": TermName, "List": TermName)`. + * For example, the `Ident(TermName("scala"))` part of `Select(Ident(TermName("scala")), TermName("List"))`. * `EmptyTree` for `Ident` instances. */ def qualifier: Tree @@ -310,17 +269,28 @@ trait Trees { self: Universe => def name: Name } - /** A tree which defines a symbol-carrying entity. - * @group Trees - * @template + /** The constructor/extractor for `RefTree` instances. + * @group Extractors + */ + val RefTree: RefTreeExtractor + + /** An extractor class to create and pattern match with syntax `RefTree(qual, name)`. + * This AST node corresponds to either Ident, Select or SelectFromTypeTree. + * @group Extractors */ - type DefTree >: Null <: SymTree with NameTree with DefTreeApi + abstract class RefTreeExtractor { + def apply(qualifier: Tree, name: Name): RefTree + def unapply(refTree: RefTree): Option[(Tree, Name)] + } - /** A tag that preserves the identity of the `DefTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** A tree representing a symbol-defining entity: + * 1) A declaration or a definition (type, class, object, package, val, var, or def) + * 2) `Bind` that is used to represent binding occurrences in pattern matches + * 3) `LabelDef` that is used internally to represent while loops + * @group Trees + * @template */ - implicit val DefTreeTag: ClassTag[DefTree] + type DefTree >: Null <: DefTreeApi with SymTree with NameTree /** The API that all def trees support * @group API @@ -335,13 +305,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type MemberDef >: Null <: DefTree with MemberDefApi - - /** A tag that preserves the identity of the `MemberDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MemberDefTag: ClassTag[MemberDef] + type MemberDef >: Null <: MemberDefApi with DefTree /** The API that all member defs support * @group API @@ -355,13 +319,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type PackageDef >: Null <: MemberDef with PackageDefApi - - /** A tag that preserves the identity of the `PackageDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val PackageDefTag: ClassTag[PackageDef] + type PackageDef >: Null <: PackageDefApi with MemberDef /** The constructor/extractor for `PackageDef` instances. * @group Extractors @@ -394,13 +352,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ImplDef >: Null <: MemberDef with ImplDefApi - - /** A tag that preserves the identity of the `ImplDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImplDefTag: ClassTag[ImplDef] + type ImplDef >: Null <: ImplDefApi with MemberDef /** The API that all impl defs support * @group API @@ -414,13 +366,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ClassDef >: Null <: ImplDef with ClassDefApi - - /** A tag that preserves the identity of the `ClassDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ClassDefTag: ClassTag[ClassDef] + type ClassDef >: Null <: ClassDefApi with ImplDef /** The constructor/extractor for `ClassDef` instances. * @group Extractors @@ -440,6 +386,10 @@ trait Trees { self: Universe => abstract class ClassDefExtractor { def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template): ClassDef def unapply(classDef: ClassDef): Option[(Modifiers, TypeName, List[TypeDef], Template)] + + /** @see [[InternalApi.classDef]] */ + @deprecated("Use `internal.classDef` instead", "2.11.0") + def apply(sym: Symbol, impl: Template)(implicit token: CompatToken): ClassDef = internal.classDef(sym, impl) } /** The API that all class defs support @@ -465,13 +415,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ModuleDef >: Null <: ImplDef with ModuleDefApi - - /** A tag that preserves the identity of the `ModuleDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ModuleDefTag: ClassTag[ModuleDef] + type ModuleDef >: Null <: ModuleDefApi with ImplDef /** The constructor/extractor for `ModuleDef` instances. * @group Extractors @@ -491,6 +435,10 @@ trait Trees { self: Universe => abstract class ModuleDefExtractor { def apply(mods: Modifiers, name: TermName, impl: Template): ModuleDef def unapply(moduleDef: ModuleDef): Option[(Modifiers, TermName, Template)] + + /** @see [[InternalApi.moduleDef]] */ + @deprecated("Use `internal.moduleDef` instead", "2.11.0") + def apply(sym: Symbol, impl: Template)(implicit token: CompatToken): ModuleDef = internal.moduleDef(sym, impl) } /** The API that all module defs support @@ -511,20 +459,14 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ValOrDefDef >: Null <: MemberDef with ValOrDefDefApi - - /** A tag that preserves the identity of the `ValOrDefDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ValOrDefDefTag: ClassTag[ValOrDefDef] + type ValOrDefDef >: Null <: ValOrDefDefApi with MemberDef /** The API that all val defs and def defs support * @group API */ trait ValOrDefDefApi extends MemberDefApi { this: ValOrDefDef => /** @inheritdoc */ - def name: Name // can't be a TermName because macros can be type names. + def name: TermName /** The type ascribed to the definition. * An empty `TypeTree` if the type hasn't been specified explicitly @@ -548,13 +490,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ValDef >: Null <: ValOrDefDef with ValDefApi - - /** A tag that preserves the identity of the `ValDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ValDefTag: ClassTag[ValDef] + type ValDef >: Null <: ValDefApi with ValOrDefDef /** The constructor/extractor for `ValDef` instances. * @group Extractors @@ -579,6 +515,14 @@ trait Trees { self: Universe => abstract class ValDefExtractor { def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef def unapply(valDef: ValDef): Option[(Modifiers, TermName, Tree, Tree)] + + /** @see [[InternalApi.valDef]] */ + @deprecated("Use `internal.valDef` instead", "2.11.0") + def apply(sym: Symbol, rhs: Tree)(implicit token: CompatToken): ValDef = internal.valDef(sym, rhs) + + /** @see [[InternalApi.valDef]] */ + @deprecated("Use `internal.valDef` instead", "2.11.0") + def apply(sym: Symbol)(implicit token: CompatToken): ValDef = internal.valDef(sym) } /** The API that all val defs support @@ -603,13 +547,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type DefDef >: Null <: ValOrDefDef with DefDefApi - - /** A tag that preserves the identity of the `DefDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val DefDefTag: ClassTag[DefDef] + type DefDef >: Null <: DefDefApi with ValOrDefDef /** The constructor/extractor for `DefDef` instances. * @group Extractors @@ -626,8 +564,28 @@ trait Trees { self: Universe => * @group Extractors */ abstract class DefDefExtractor { - def apply(mods: Modifiers, name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef - def unapply(defDef: DefDef): Option[(Modifiers, Name, List[TypeDef], List[List[ValDef]], Tree, Tree)] + def apply(mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef + def unapply(defDef: DefDef): Option[(Modifiers, TermName, List[TypeDef], List[List[ValDef]], Tree, Tree)] + + /** @see [[InternalApi.defDef]] */ + @deprecated("Use `internal.defDef` instead", "2.11.0") + def apply(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree)(implicit token: CompatToken): DefDef = internal.defDef(sym, mods, vparamss, rhs) + + /** @see [[InternalApi.defDef]] */ + @deprecated("Use `internal.defDef` instead", "2.11.0") + def apply(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree)(implicit token: CompatToken): DefDef = internal.defDef(sym, vparamss, rhs) + + /** @see [[InternalApi.defDef]] */ + @deprecated("Use `internal.defDef` instead", "2.11.0") + def apply(sym: Symbol, mods: Modifiers, rhs: Tree)(implicit token: CompatToken): DefDef = internal.defDef(sym, mods, rhs) + + /** @see [[InternalApi.defDef]] */ + @deprecated("Use `internal.defDef` instead", "2.11.0") + def apply(sym: Symbol, rhs: Tree)(implicit token: CompatToken): DefDef = internal.defDef(sym, rhs) + + /** @see [[InternalApi.defDef]] */ + @deprecated("Use `internal.defDef` instead", "2.11.0") + def apply(sym: Symbol, rhs: List[List[Symbol]] => Tree)(implicit token: CompatToken): DefDef = internal.defDef(sym, rhs) } /** The API that all def defs support @@ -638,7 +596,7 @@ trait Trees { self: Universe => def mods: Modifiers /** @inheritdoc */ - def name: Name + def name: TermName /** The type parameters of the method. */ def tparams: List[TypeDef] @@ -658,13 +616,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TypeDef >: Null <: MemberDef with TypeDefApi - - /** A tag that preserves the identity of the `TypeDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeDefTag: ClassTag[TypeDef] + type TypeDef >: Null <: TypeDefApi with MemberDef /** The constructor/extractor for `TypeDef` instances. * @group Extractors @@ -686,6 +638,14 @@ trait Trees { self: Universe => abstract class TypeDefExtractor { def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree): TypeDef def unapply(typeDef: TypeDef): Option[(Modifiers, TypeName, List[TypeDef], Tree)] + + /** @see [[InternalApi.typeDef]] */ + @deprecated("Use `internal.typeDef` instead", "2.11.0") + def apply(sym: Symbol, rhs: Tree)(implicit token: CompatToken): TypeDef = internal.typeDef(sym, rhs) + + /** @see [[InternalApi.typeDef]] */ + @deprecated("Use `internal.typeDef` instead", "2.11.0") + def apply(sym: Symbol)(implicit token: CompatToken): TypeDef = internal.typeDef(sym) } /** The API that all type defs support @@ -723,13 +683,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type LabelDef >: Null <: DefTree with TermTree with LabelDefApi - - /** A tag that preserves the identity of the `LabelDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LabelDefTag: ClassTag[LabelDef] + type LabelDef >: Null <: LabelDefApi with DefTree with TermTree /** The constructor/extractor for `LabelDef` instances. * @group Extractors @@ -752,6 +706,10 @@ trait Trees { self: Universe => abstract class LabelDefExtractor { def apply(name: TermName, params: List[Ident], rhs: Tree): LabelDef def unapply(labelDef: LabelDef): Option[(TermName, List[Ident], Tree)] + + /** @see [[InternalApi.labelDef]] */ + @deprecated("Use `internal.labelDef` instead", "2.11.0") + def apply(sym: Symbol, params: List[Symbol], rhs: Tree)(implicit token: CompatToken): LabelDef = internal.labelDef(sym, params, rhs) } /** The API that all label defs support @@ -772,7 +730,7 @@ trait Trees { self: Universe => def rhs: Tree } - /** Import selector + /** Import selector (not a tree, but a component of the `Import` tree) * * Representation of an imported name its optional rename and their optional positions * @@ -787,12 +745,6 @@ trait Trees { self: Universe => */ type ImportSelector >: Null <: AnyRef with ImportSelectorApi - /** A tag that preserves the identity of the `ImportSelector` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImportSelectorTag: ClassTag[ImportSelector] - /** The constructor/extractor for `ImportSelector` instances. * @group Extractors */ @@ -837,13 +789,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Import >: Null <: SymTree with ImportApi - - /** A tag that preserves the identity of the `Import` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImportTag: ClassTag[Import] + type Import >: Null <: ImportApi with SymTree /** The constructor/extractor for `Import` instances. * @group Extractors @@ -895,13 +841,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Template >: Null <: SymTree with TemplateApi - - /** A tag that preserves the identity of the `Template` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TemplateTag: ClassTag[Template] + type Template >: Null <: TemplateApi with SymTree /** The constructor/extractor for `Template` instances. * @group Extractors @@ -940,7 +880,7 @@ trait Trees { self: Universe => def parents: List[Tree] /** Self type of the template. - * Is equal to `emptyValDef` if the self type is not specified. + * Is equal to `noSelfType` if the self type is not specified. */ def self: ValDef @@ -953,13 +893,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Block >: Null <: TermTree with BlockApi - - /** A tag that preserves the identity of the `Block` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BlockTag: ClassTag[Block] + type Block >: Null <: BlockApi with TermTree /** The constructor/extractor for `Block` instances. * @group Extractors @@ -998,13 +932,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type CaseDef >: Null <: AnyRef with Tree with CaseDefApi - - /** A tag that preserves the identity of the `CaseDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val CaseDefTag: ClassTag[CaseDef] + type CaseDef >: Null <: CaseDefApi with Tree /** The constructor/extractor for `CaseDef` instances. * @group Extractors @@ -1017,7 +945,7 @@ trait Trees { self: Universe => * `case` pat `if` guard => body * * If the guard is not present, the `guard` is set to `EmptyTree`. - * If the body is not specified, the `body` is set to `Literal(Constant())` + * If the body is not specified, the `body` is set to `Literal(Constant(()))` * @group Extractors */ abstract class CaseDefExtractor { @@ -1038,7 +966,7 @@ trait Trees { self: Universe => def guard: Tree /** The body of the pattern matching clause. - * Is equal to `Literal(Constant())` if the body is not specified. + * Is equal to `Literal(Constant(()))` if the body is not specified. */ def body: Tree } @@ -1051,13 +979,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Alternative >: Null <: TermTree with AlternativeApi - - /** A tag that preserves the identity of the `Alternative` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AlternativeTag: ClassTag[Alternative] + type Alternative >: Null <: AlternativeApi with TermTree /** The constructor/extractor for `Alternative` instances. * @group Extractors @@ -1089,13 +1011,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Star >: Null <: TermTree with StarApi - - /** A tag that preserves the identity of the `Star` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val StarTag: ClassTag[Star] + type Star >: Null <: StarApi with TermTree /** The constructor/extractor for `Star` instances. * @group Extractors @@ -1130,13 +1046,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Bind >: Null <: DefTree with BindApi - - /** A tag that preserves the identity of the `Bind` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BindTag: ClassTag[Bind] + type Bind >: Null <: BindApi with DefTree /** The constructor/extractor for `Bind` instances. * @group Extractors @@ -1187,7 +1097,7 @@ trait Trees { self: Universe => * UnApply( * // a dummy node that carries the type of unapplication to patmat * // the <unapply-selector> here doesn't have an underlying symbol - * // it only has a type assigned, therefore after `resetAllAttrs` this tree is no longer typeable + * // it only has a type assigned, therefore after `untypecheck` this tree is no longer typeable * Apply(Select(Ident(Foo), newTermName("unapply")), List(Ident(newTermName("<unapply-selector>")))), * // arguments of the unapply => nothing synthetic here * List(Bind(newTermName("x"), Ident(nme.WILDCARD)))), @@ -1199,13 +1109,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type UnApply >: Null <: TermTree with UnApplyApi - - /** A tag that preserves the identity of the `UnApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val UnApplyTag: ClassTag[UnApply] + type UnApply >: Null <: UnApplyApi with TermTree /** The constructor/extractor for `UnApply` instances. * @group Extractors @@ -1241,13 +1145,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Function >: Null <: TermTree with SymTree with FunctionApi - - /** A tag that preserves the identity of the `Function` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FunctionTag: ClassTag[Function] + type Function >: Null <: FunctionApi with TermTree with SymTree /** The constructor/extractor for `Function` instances. * @group Extractors @@ -1285,13 +1183,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Assign >: Null <: TermTree with AssignApi - - /** A tag that preserves the identity of the `Assign` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AssignTag: ClassTag[Assign] + type Assign >: Null <: AssignApi with TermTree /** The constructor/extractor for `Assign` instances. * @group Extractors @@ -1327,13 +1219,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type AssignOrNamedArg >: Null <: TermTree with AssignOrNamedArgApi - - /** A tag that preserves the identity of the `AssignOrNamedArg` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AssignOrNamedArgTag: ClassTag[AssignOrNamedArg] + type AssignOrNamedArg >: Null <: AssignOrNamedArgApi with TermTree /** The constructor/extractor for `AssignOrNamedArg` instances. * @group Extractors @@ -1374,13 +1260,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type If >: Null <: TermTree with IfApi - - /** A tag that preserves the identity of the `If` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val IfTag: ClassTag[If] + type If >: Null <: IfApi with TermTree /** The constructor/extractor for `If` instances. * @group Extractors @@ -1431,13 +1311,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Match >: Null <: TermTree with MatchApi - - /** A tag that preserves the identity of the `Match` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MatchTag: ClassTag[Match] + type Match >: Null <: MatchApi with TermTree /** The constructor/extractor for `Match` instances. * @group Extractors @@ -1472,13 +1346,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Return >: Null <: TermTree with SymTree with ReturnApi - - /** A tag that preserves the identity of the `Return` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ReturnTag: ClassTag[Return] + type Return >: Null <: ReturnApi with SymTree with TermTree /** The constructor/extractor for `Return` instances. * @group Extractors @@ -1510,13 +1378,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Try >: Null <: TermTree with TryApi - - /** A tag that preserves the identity of the `Try` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TryTag: ClassTag[Try] + type Try >: Null <: TryApi with TermTree /** The constructor/extractor for `Try` instances. * @group Extractors @@ -1554,13 +1416,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Throw >: Null <: TermTree with ThrowApi - - /** A tag that preserves the identity of the `Throw` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThrowTag: ClassTag[Throw] + type Throw >: Null <: ThrowApi with TermTree /** The constructor/extractor for `Throw` instances. * @group Extractors @@ -1590,13 +1446,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type New >: Null <: TermTree with NewApi - - /** A tag that preserves the identity of the `New` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NewTag: ClassTag[New] + type New >: Null <: NewApi with TermTree /** The constructor/extractor for `New` instances. * @group Extractors @@ -1646,13 +1496,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Typed >: Null <: TermTree with TypedApi - - /** A tag that preserves the identity of the `Typed` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypedTag: ClassTag[Typed] + type Typed >: Null <: TypedApi with TermTree /** The constructor/extractor for `Typed` instances. * @group Extractors @@ -1685,13 +1529,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type GenericApply >: Null <: TermTree with GenericApplyApi - - /** A tag that preserves the identity of the `GenericApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val GenericApplyTag: ClassTag[GenericApply] + type GenericApply >: Null <: GenericApplyApi with TermTree /** The API that all applies support * @group API @@ -1712,13 +1550,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TypeApply >: Null <: GenericApply with TypeApplyApi - - /** A tag that preserves the identity of the `TypeApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeApplyTag: ClassTag[TypeApply] + type TypeApply >: Null <: TypeApplyApi with GenericApply /** The constructor/extractor for `TypeApply` instances. * @group Extractors @@ -1756,13 +1588,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Apply >: Null <: GenericApply with ApplyApi - - /** A tag that preserves the identity of the `Apply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ApplyTag: ClassTag[Apply] + type Apply >: Null <: ApplyApi with GenericApply /** The constructor/extractor for `Apply` instances. * @group Extractors @@ -1799,13 +1625,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Super >: Null <: TermTree with SuperApi - - /** A tag that preserves the identity of the `Super` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SuperTag: ClassTag[Super] + type Super >: Null <: SuperApi with TermTree /** The constructor/extractor for `Super` instances. * @group Extractors @@ -1851,13 +1671,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type This >: Null <: TermTree with SymTree with ThisApi - - /** A tag that preserves the identity of the `This` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThisTag: ClassTag[This] + type This >: Null <: ThisApi with TermTree with SymTree /** The constructor/extractor for `This` instances. * @group Extractors @@ -1892,13 +1706,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Select >: Null <: RefTree with SelectApi - - /** A tag that preserves the identity of the `Select` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SelectTag: ClassTag[Select] + type Select >: Null <: SelectApi with RefTree /** The constructor/extractor for `Select` instances. * @group Extractors @@ -1937,13 +1745,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Ident >: Null <: RefTree with IdentApi - - /** A tag that preserves the identity of the `Ident` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val IdentTag: ClassTag[Ident] + type Ident >: Null <: IdentApi with RefTree /** The constructor/extractor for `Ident` instances. * @group Extractors @@ -1968,77 +1770,18 @@ trait Trees { self: Universe => * @group API */ trait IdentApi extends RefTreeApi { this: Ident => + /** Was this ident created from a backquoted identifier? */ + def isBackquoted: Boolean + /** @inheritdoc */ def name: Name } - /** Marks underlying reference to id as boxed. - * - * <b>Precondition:<\b> id must refer to a captured variable - * A reference such marked will refer to the boxed entity, no dereferencing - * with `.elem` is done on it. - * This tree node can be emitted by macros such as reify that call referenceCapturedVariable. - * It is eliminated in LambdaLift, where the boxing conversion takes place. - * @group Trees - * @template - */ - type ReferenceToBoxed >: Null <: TermTree with ReferenceToBoxedApi - - /** A tag that preserves the identity of the `ReferenceToBoxed` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ReferenceToBoxedTag: ClassTag[ReferenceToBoxed] - - /** The constructor/extractor for `ReferenceToBoxed` instances. - * @group Extractors - */ - val ReferenceToBoxed: ReferenceToBoxedExtractor - - /** An extractor class to create and pattern match with syntax `ReferenceToBoxed(ident)`. - * This AST node does not have direct correspondence to Scala code, - * and is emitted by macros to reference capture vars directly without going through `elem`. - * - * For example: - * - * var x = ... - * fun { x } - * - * Will emit: - * - * Ident(x) - * - * Which gets transformed to: - * - * Select(Ident(x), "elem") - * - * If `ReferenceToBoxed` were used instead of Ident, no transformation would be performed. - * @group Extractors - */ - abstract class ReferenceToBoxedExtractor { - def apply(ident: Ident): ReferenceToBoxed - def unapply(referenceToBoxed: ReferenceToBoxed): Option[Ident] - } - - /** The API that all references support - * @group API - */ - trait ReferenceToBoxedApi extends TermTreeApi { this: ReferenceToBoxed => - /** The underlying reference. */ - def ident: Tree - } - /** Literal * @group Trees * @template */ - type Literal >: Null <: TermTree with LiteralApi - - /** A tag that preserves the identity of the `Literal` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LiteralTag: ClassTag[Literal] + type Literal >: Null <: LiteralApi with TermTree /** The constructor/extractor for `Literal` instances. * @group Extractors @@ -2071,13 +1814,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type Annotated >: Null <: AnyRef with Tree with AnnotatedApi - - /** A tag that preserves the identity of the `Annotated` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AnnotatedTag: ClassTag[Annotated] + type Annotated >: Null <: AnnotatedApi with Tree /** The constructor/extractor for `Annotated` instances. * @group Extractors @@ -2111,13 +1848,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type SingletonTypeTree >: Null <: TypTree with SingletonTypeTreeApi - - /** A tag that preserves the identity of the `SingletonTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SingletonTypeTreeTag: ClassTag[SingletonTypeTree] + type SingletonTypeTree >: Null <: SingletonTypeTreeApi with TypTree /** The constructor/extractor for `SingletonTypeTree` instances. * @group Extractors @@ -2147,13 +1878,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type SelectFromTypeTree >: Null <: TypTree with RefTree with SelectFromTypeTreeApi - - /** A tag that preserves the identity of the `SelectFromTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SelectFromTypeTreeTag: ClassTag[SelectFromTypeTree] + type SelectFromTypeTree >: Null <: SelectFromTypeTreeApi with TypTree with RefTree /** The constructor/extractor for `SelectFromTypeTree` instances. * @group Extractors @@ -2194,13 +1919,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type CompoundTypeTree >: Null <: TypTree with CompoundTypeTreeApi - - /** A tag that preserves the identity of the `CompoundTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val CompoundTypeTreeTag: ClassTag[CompoundTypeTree] + type CompoundTypeTree >: Null <: CompoundTypeTreeApi with TypTree /** The constructor/extractor for `CompoundTypeTree` instances. * @group Extractors @@ -2230,13 +1949,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type AppliedTypeTree >: Null <: TypTree with AppliedTypeTreeApi - - /** A tag that preserves the identity of the `AppliedTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AppliedTypeTreeTag: ClassTag[AppliedTypeTree] + type AppliedTypeTree >: Null <: AppliedTypeTreeApi with TypTree /** The constructor/extractor for `AppliedTypeTree` instances. * @group Extractors @@ -2278,13 +1991,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TypeBoundsTree >: Null <: TypTree with TypeBoundsTreeApi - - /** A tag that preserves the identity of the `TypeBoundsTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeBoundsTreeTag: ClassTag[TypeBoundsTree] + type TypeBoundsTree >: Null <: TypeBoundsTreeApi with TypTree /** The constructor/extractor for `TypeBoundsTree` instances. * @group Extractors @@ -2321,13 +2028,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type ExistentialTypeTree >: Null <: TypTree with ExistentialTypeTreeApi - - /** A tag that preserves the identity of the `ExistentialTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ExistentialTypeTreeTag: ClassTag[ExistentialTypeTree] + type ExistentialTypeTree >: Null <: ExistentialTypeTreeApi with TypTree /** The constructor/extractor for `ExistentialTypeTree` instances. * @group Extractors @@ -2341,8 +2042,8 @@ trait Trees { self: Universe => * @group Extractors */ abstract class ExistentialTypeTreeExtractor { - def apply(tpt: Tree, whereClauses: List[Tree]): ExistentialTypeTree - def unapply(existentialTypeTree: ExistentialTypeTree): Option[(Tree, List[Tree])] + def apply(tpt: Tree, whereClauses: List[MemberDef]): ExistentialTypeTree + def unapply(existentialTypeTree: ExistentialTypeTree): Option[(Tree, List[MemberDef])] } /** The API that all existential type trees support @@ -2352,8 +2053,12 @@ trait Trees { self: Universe => /** The underlying type of the existential type. */ def tpt: Tree - /** The clauses of the definition of the existential type. */ - def whereClauses: List[Tree] + /** The clauses of the definition of the existential type. + * Elements are one of the following: + * 1) TypeDef with TypeBoundsTree right-hand side + * 2) ValDef with empty right-hand side + */ + def whereClauses: List[MemberDef] } /** A synthetic tree holding an arbitrary type. Not to be confused with @@ -2364,13 +2069,7 @@ trait Trees { self: Universe => * @group Trees * @template */ - type TypeTree >: Null <: TypTree with TypeTreeApi - - /** A tag that preserves the identity of the `TypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeTreeTag: ClassTag[TypeTree] + type TypeTree >: Null <: TypeTreeApi with TypTree /** The constructor/extractor for `TypeTree` instances. * @group Extractors @@ -2403,93 +2102,33 @@ trait Trees { self: Universe => * no definition of a self value of self type. * @group Trees */ - val emptyValDef: ValDef - -// ---------------------- factories ---------------------------------------------- - - /** A factory method for `ClassDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical ClassDef constructor to create a class and then initialize its position and symbol manually", "2.10.1") - def ClassDef(sym: Symbol, impl: Template): ClassDef - - /** A factory method for `ModuleDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical ModuleDef constructor to create an object and then initialize its position and symbol manually", "2.10.1") - def ModuleDef(sym: Symbol, impl: Template): ModuleDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical ValDef constructor to create a val and then initialize its position and symbol manually", "2.10.1") - def ValDef(sym: Symbol, rhs: Tree): ValDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical ValDef constructor to create a val with an empty right-hand side and then initialize its position and symbol manually", "2.10.1") - def ValDef(sym: Symbol): ValDef + val noSelfType: ValDef - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") - def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") - def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") - def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") - def DefDef(sym: Symbol, rhs: Tree): DefDef - - /** A factory method for `ValDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") - def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef - - /** A factory method for `TypeDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical TypeDef constructor to create a type alias and then initialize its position and symbol manually", "2.10.1") - def TypeDef(sym: Symbol, rhs: Tree): TypeDef + @deprecated("Use `noSelfType` instead", "2.11.0") + val emptyValDef: ValDef - /** A factory method for `TypeDef` nodes. - * @group Factories + /** An empty superclass constructor call corresponding to: + * super.<init>() + * This is used as a placeholder in the primary constructor body in class templates + * to denote the insertion point of a call to superclass constructor after the typechecker + * figures out the superclass of a given template. + * @group Trees */ - @deprecated("Use the canonical TypeDef constructor to create an abstract type or type parameter and then initialize its position and symbol manually", "2.10.1") - def TypeDef(sym: Symbol): TypeDef + val pendingSuperCall: Apply - /** A factory method for `LabelDef` nodes. - * @group Factories - */ - @deprecated("Use the canonical LabelDef constructor to create a label and then initialize its position and symbol manually", "2.10.1") - def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef +// ---------------------- factories ---------------------------------------------- /** A factory method for `Block` nodes. * Flattens directly nested blocks. * @group Factories */ - @deprecated("Use the canonical Block constructor, explicitly specifying its expression if necessary. Flatten directly nested blocks manually if needed", "2.10.1") + @deprecated("Use q\"{..$stats}\" instead. Flatten directly nested blocks manually if needed", "2.10.1") def Block(stats: Tree*): Block /** A factory method for `CaseDef` nodes. * @group Factories */ - @deprecated("Use the canonical CaseDef constructor passing EmptyTree for guard", "2.10.1") + @deprecated("Use cq\"$pat => $body\" instead", "2.10.1") def CaseDef(pat: Tree, body: Tree): CaseDef /** A factory method for `Bind` nodes. @@ -2501,50 +2140,50 @@ trait Trees { self: Universe => /** A factory method for `Try` nodes. * @group Factories */ - @deprecated("Use canonical CaseDef constructors to to create exception catching expressions and then wrap them in Try", "2.10.1") + @deprecated("Convert cases into casedefs and use q\"try $body catch { case ..$newcases }\" instead", "2.10.1") def Try(body: Tree, cases: (Tree, Tree)*): Try /** A factory method for `Throw` nodes. * @group Factories */ - @deprecated("Use the canonical New constructor to create an object instantiation expression and then wrap it in Throw", "2.10.1") + @deprecated("Use q\"throw new $tpe(..$args)\" instead", "2.10.1") def Throw(tpe: Type, args: Tree*): Throw /** Factory method for object creation `new tpt(args_1)...(args_n)` * A `New(t, as)` is expanded to: `(new t).<init>(as)` * @group Factories */ - @deprecated("Use Apply(...Apply(Select(New(tpt), nme.CONSTRUCTOR), args1)...argsN) instead", "2.10.1") + @deprecated("Use q\"new $tpt(...$argss)\" instead", "2.10.1") def New(tpt: Tree, argss: List[List[Tree]]): Tree /** 0-1 argument list new, based on a type. * @group Factories */ - @deprecated("Use New(TypeTree(tpe), args.toList) instead", "2.10.1") + @deprecated("Use q\"new $tpe(..$args)\" instead", "2.10.1") def New(tpe: Type, args: Tree*): Tree /** 0-1 argument list new, based on a symbol. * @group Factories */ - @deprecated("Use New(sym.toType, args) instead", "2.10.1") + @deprecated("Use q\"new ${sym.toType}(..$args)\" instead", "2.10.1") def New(sym: Symbol, args: Tree*): Tree /** A factory method for `Apply` nodes. * @group Factories */ - @deprecated("Use Apply(Ident(sym), args.toList) instead", "2.10.1") + @deprecated("Use q\"$sym(..$args)\" instead", "2.10.1") def Apply(sym: Symbol, args: Tree*): Tree /** 0-1 argument list new, based on a type tree. * @group Factories */ - @deprecated("Use Apply(Select(New(tpt), nme.CONSTRUCTOR), args) instead", "2.10.1") + @deprecated("Use q\"new $tpt(..$args)\" instead", "2.10.1") def ApplyConstructor(tpt: Tree, args: List[Tree]): Tree /** A factory method for `Super` nodes. * @group Factories */ - @deprecated("Use Super(This(sym), mix) instead", "2.10.1") + @deprecated("Use q\"$sym.super[$mix].x\".qualifier instead", "2.10.1") def Super(sym: Symbol, mix: TypeName): Tree /** A factory method for `This` nodes. @@ -2556,7 +2195,7 @@ trait Trees { self: Universe => * The string `name` argument is assumed to represent a [[scala.reflect.api.Names#TermName `TermName`]]. * @group Factories */ - @deprecated("Use Select(tree, newTermName(name)) instead", "2.10.1") + @deprecated("Use Select(tree, TermName(name)) instead", "2.10.1") def Select(qualifier: Tree, name: String): Select /** A factory method for `Select` nodes. @@ -2567,7 +2206,7 @@ trait Trees { self: Universe => /** A factory method for `Ident` nodes. * @group Factories */ - @deprecated("Use Ident(newTermName(name)) instead", "2.10.1") + @deprecated("Use Ident(TermName(name)) instead", "2.10.1") def Ident(name: String): Ident /** A factory method for `Ident` nodes. @@ -2586,7 +2225,7 @@ trait Trees { self: Universe => * @template * @group Copying */ - type TreeCopier <: TreeCopierOps + type TreeCopier >: Null <: AnyRef with TreeCopierOps /** The standard (lazy) tree copier. * @group Copying @@ -2762,6 +2401,11 @@ trait Trees { self: Universe => */ def Ident(tree: Tree, name: Name): Ident + /** Creates a `RefTree` node from the given components, having a given `tree` as a prototype. + * Having a tree as a prototype means that the tree's attachments, type and symbol will be copied into the result. + */ + def RefTree(tree: Tree, qualifier: Tree, selector: Name): RefTree + /** Creates a `ReferenceToBoxed` node from the given components, having a given `tree` as a prototype. * Having a tree as a prototype means that the tree's attachments, type and symbol will be copied into the result. */ @@ -2810,7 +2454,7 @@ trait Trees { self: Universe => /** Creates a `ExistentialTypeTree` node from the given components, having a given `tree` as a prototype. * Having a tree as a prototype means that the tree's attachments, type and symbol will be copied into the result. */ - def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[Tree]): ExistentialTypeTree + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[MemberDef]): ExistentialTypeTree } // ---------------------- traversing and transforming ------------------------------ @@ -2821,18 +2465,32 @@ trait Trees { self: Universe => class Traverser { protected[scala] var currentOwner: Symbol = rootMirror.RootClass + /** Traverse something which Trees contain, but which isn't a Tree itself. */ + def traverseName(name: Name): Unit = () + def traverseConstant(c: Constant): Unit = () + def traverseImportSelector(sel: ImportSelector): Unit = () + def traverseModifiers(mods: Modifiers): Unit = traverseAnnotations(mods.annotations) + /** Traverses a single tree. */ - def traverse(tree: Tree): Unit = itraverse(this, tree) + def traverse(tree: Tree): Unit = itraverse(this, tree) + def traversePattern(pat: Tree): Unit = traverse(pat) + def traverseGuard(guard: Tree): Unit = traverse(guard) + def traverseTypeAscription(tpt: Tree): Unit = traverse(tpt) + // Special handling of noSelfType necessary for backward compat: existing + // traversers break down when they see the unexpected tree. + def traverseSelfType(self: ValDef): Unit = if (self ne noSelfType) traverse(self) /** Traverses a list of trees. */ - def traverseTrees(trees: List[Tree]) { - trees foreach traverse - } + def traverseTrees(trees: List[Tree]): Unit = trees foreach traverse + def traverseTypeArgs(args: List[Tree]): Unit = traverseTrees(args) + def traverseParents(parents: List[Tree]): Unit = traverseTrees(parents) + def traverseCases(cases: List[CaseDef]): Unit = traverseTrees(cases) + def traverseAnnotations(annots: List[Tree]): Unit = traverseTrees(annots) /** Traverses a list of lists of trees. */ - def traverseTreess(treess: List[List[Tree]]) { - treess foreach traverseTrees - } + def traverseTreess(treess: List[List[Tree]]): Unit = treess foreach traverseTrees + def traverseParams(params: List[Tree]): Unit = traverseTrees(params) + def traverseParamss(vparamss: List[List[Tree]]): Unit = vparamss foreach traverseParams /** Traverses a list of trees with a given owner symbol. */ def traverseStats(stats: List[Tree], exprOwner: Symbol) { @@ -2897,7 +2555,8 @@ trait Trees { self: Universe => def transform(tree: Tree): Tree = itransform(this, tree) /** Transforms a list of trees. */ - def transformTrees(trees: List[Tree]): List[Tree] = trees mapConserve (transform(_)) + def transformTrees(trees: List[Tree]): List[Tree] = + if (trees.isEmpty) Nil else trees mapConserve transform /** Transforms a `Template`. */ def transformTemplate(tree: Template): Template = @@ -2907,7 +2566,8 @@ trait Trees { self: Universe => trees mapConserve (tree => transform(tree).asInstanceOf[TypeDef]) /** Transforms a `ValDef`. */ def transformValDef(tree: ValDef): ValDef = - if (tree.isEmpty) tree else transform(tree).asInstanceOf[ValDef] + if (tree eq noSelfType) tree + else transform(tree).asInstanceOf[ValDef] /** Transforms a list of `ValDef` nodes. */ def transformValDefs(trees: List[ValDef]): List[ValDef] = trees mapConserve (transformValDef(_)) @@ -2915,6 +2575,8 @@ trait Trees { self: Universe => def transformValDefss(treess: List[List[ValDef]]): List[List[ValDef]] = treess mapConserve (transformValDefs(_)) /** Transforms a list of `CaseDef` nodes. */ + def transformMemberDefs(trees: List[MemberDef]): List[MemberDef] = + trees mapConserve (tree => transform(tree).asInstanceOf[MemberDef]) def transformCaseDefs(trees: List[CaseDef]): List[CaseDef] = trees mapConserve (tree => transform(tree).asInstanceOf[CaseDef]) /** Transforms a list of `Ident` nodes. */ @@ -2926,8 +2588,10 @@ trait Trees { self: Universe => if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(transform(stat)) else transform(stat)) filter (EmptyTree != _) /** Transforms `Modifiers`. */ - def transformModifiers(mods: Modifiers): Modifiers = - mods.mapAnnotations(transformTrees) + def transformModifiers(mods: Modifiers): Modifiers = { + if (mods.annotations.isEmpty) mods + else mods mapAnnotations transformTrees + } /** Transforms a tree with a given owner symbol. */ def atOwner[A](owner: Symbol)(trans: => A): A = { @@ -2951,17 +2615,11 @@ trait Trees { self: Universe => */ protected def xtransform(transformer: Transformer, tree: Tree): Tree = throw new MatchError(tree) - /** The type of tree modifiers. + /** The type of tree modifiers (not a tree, but rather part of DefTrees). * @group Traversal */ type Modifiers >: Null <: AnyRef with ModifiersApi - /** A tag that preserves the identity of the `Modifiers` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Traversal - */ - implicit val ModifiersTag: ClassTag[Modifiers] - /** The API that all Modifiers support * @group API */ @@ -2993,15 +2651,19 @@ trait Trees { self: Universe => /** The constructor/extractor for `Modifiers` instances. * @group Traversal */ - val Modifiers: ModifiersCreator + val Modifiers: ModifiersExtractor + + @deprecated("Use ModifiersExtractor instead", "2.11.0") + type ModifiersCreator = ModifiersExtractor /** An extractor class to create and pattern match with syntax `Modifiers(flags, privateWithin, annotations)`. * Modifiers encapsulate flags, visibility annotations and Scala annotations for member definitions. * @group Traversal */ - abstract class ModifiersCreator { + abstract class ModifiersExtractor { def apply(): Modifiers = Modifiers(NoFlags, tpnme.EMPTY, List()) def apply(flags: FlagSet, privateWithin: Name, annotations: List[Tree]): Modifiers + def unapply(mods: Modifiers): Option[(FlagSet, Name, List[Tree])] } /** The factory for `Modifiers` instances. diff --git a/src/reflect/scala/reflect/api/TypeCreator.scala b/src/reflect/scala/reflect/api/TypeCreator.scala index 24271cb48d..37fff90b43 100644 --- a/src/reflect/scala/reflect/api/TypeCreator.scala +++ b/src/reflect/scala/reflect/api/TypeCreator.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** A mirror-aware factory for types. diff --git a/src/reflect/scala/reflect/api/TypeTags.scala b/src/reflect/scala/reflect/api/TypeTags.scala index 7457910226..1dfc84be69 100644 --- a/src/reflect/scala/reflect/api/TypeTags.scala +++ b/src/reflect/scala/reflect/api/TypeTags.scala @@ -134,7 +134,7 @@ import scala.language.implicitConversions * reflection APIs provided by Java (for classes) and Scala (for types).</li> * * <li>'''Certain manifest operations(i.e., <:<, >:> and typeArguments) are not - * supported.''' <br/>Instead, one culd use the reflection APIs provided by Java (for + * supported.''' <br/>Instead, one could use the reflection APIs provided by Java (for * classes) and Scala (for types).</li> *</ul> * @@ -333,6 +333,12 @@ trait TypeTags { self: Universe => * @group TypeTags */ def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe + + /** + * Type symbol of `x` as derived from a type tag. + * @group TypeTags + */ + def symbolOf[T: WeakTypeTag]: TypeSymbol } private[scala] class SerializedTypeTag(var tpec: TypeCreator, var concrete: Boolean) extends Serializable { diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index 72163ef0e9..f6995dd5de 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -50,20 +51,15 @@ package api * * @contentDiagram hideNodes "*Api" */ -trait Types { self: Universe => +trait Types { + self: Universe => /** The type of Scala types, and also Scala type signatures. * (No difference is internally made between the two). * @template * @group Types */ - type Type >: Null <: TypeApi - - /** A tag that preserves the identity of the `Type` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeTagg: ClassTag[Type] + type Type >: Null <: AnyRef with TypeApi /** This constant is used as a special value that indicates that no meaningful type exists. * @group Types @@ -80,6 +76,12 @@ trait Types { self: Universe => /** The API of types. * The main source of information about types is the [[scala.reflect.api.Types]] page. * @group API + * + * @define dealiasWidenWarning Note that type aliases can hide beneath + * singleton types and singleton types can hide inside type aliases. + * Moreover, aliases might lurk in the upper bounds of abstract types. + * Therefore careful thought has to be applied to identify and carry out + * unwrapping logic specific to your use case. */ abstract class TypeApi { /** The term symbol associated with the type, or `NoSymbol` for types @@ -92,11 +94,19 @@ trait Types { self: Universe => */ def typeSymbol: Symbol + /** @see [[decl]] */ + @deprecated("Use `decl` instead", "2.11.0") + def declaration(name: Name): Symbol + /** 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 + def decl(name: Name): Symbol + + /** @see [[decls]] */ + @deprecated("Use `decls` instead", "2.11.0") + def declarations: MemberScope /** A `Scope` containing directly declared members of this type. * Unlike `members` this method doesn't returns inherited members. @@ -104,7 +114,7 @@ trait Types { self: Universe => * Members in the returned scope might appear in arbitrary order. * Use `declarations.sorted` to get an ordered list of members. */ - def declarations: MemberScope + def decls: MemberScope /** The member with given name, either directly declared or inherited, * an OverloadedSymbol if several exist, NoSymbol if none exist. @@ -119,6 +129,11 @@ trait Types { self: Universe => */ def members: MemberScope + /** Type signature of the companion of the underlying class symbol. + * NoType if the underlying symbol is not a class symbol, or if it doesn't have a companion. + */ + def companion: Type + /** Is this type a type constructor that is missing its type arguments? */ def takesTypeArgs: Boolean @@ -127,7 +142,7 @@ trait Types { self: Universe => */ def typeConstructor: Type - /** + /** 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. * @@ -135,8 +150,18 @@ trait Types { self: Universe => * TypeRef(pre, <List>, List()) is replaced by * PolyType(X, TypeRef(pre, <List>, List(X))) */ + @deprecated("Use `dealias` or `etaExpand` instead", "2.11.0") def normalize: Type + /** 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 etaExpand: Type + /** Does this type conform to given type argument `that`? */ def <:< (that: Type): Boolean @@ -209,11 +234,132 @@ trait Types { self: Universe => * class Outer { class C ; val x: C } * val o: Outer * <o.x.type>.widen = o.C + * + * $dealiasWidenWarning */ def widen: Type + /** Expands type aliases arising from type members. + * $dealiasWidenWarning + */ + def dealias: Type + + /******* popular methods from subclasses *******/ + + /** List of type arguments ingrained in this type reference. + * Depending on your use case you might or might not want to call `dealias` first. + * + * {{{ + * scala> type T = List[Int] + * defined type alias T + * + * scala> typeOf[T].typeArgs + * res0: List[reflect.runtime.universe.Type] = List() + * + * scala> typeOf[T].dealias.typeArgs + * res1: List[reflect.runtime.universe.Type] = List(scala.Int) + * }}} + */ + def typeArgs: List[Type] + + /** @see [[paramLists]] */ + @deprecated("Use `paramLists` instead", "2.11.0") + def paramss: List[List[Symbol]] + + /** For a method or poly type, a list of its value parameter sections, + * the empty list of lists for all other types. + */ + def paramLists: List[List[Symbol]] + + /** For a poly type, its type parameters, + * the empty list for all other types. + */ + def typeParams: List[Symbol] + + /** For a (nullary) method or poly type, its direct result type + * (can be a MethodType if the method has multiple argument lists), + * the type itself for all other types. + * + * {{{ + * scala> class C { def foo[T](x: T)(y: T) = ??? } + * defined class C + * + * scala> typeOf[C].member(TermName("foo")).asMethod + * res0: reflect.runtime.universe.MethodSymbol = method foo + * + * scala> res0.info // PolyType wrapping a MethodType + * res1: reflect.runtime.universe.Type = [T](x: T)(y: T)scala.Nothing + * + * scala> res1.resultType // MethodType wrapping a MethodType + * res2: reflect.runtime.universe.Type = (x: T)(y: T)scala.Nothing + * + * scala> res1.resultType.resultType // vanilla MethodType + * res3: reflect.runtime.universe.Type = (y: T)scala.Nothing + * + * scala> res1.resultType.resultType.resultType + * res4: reflect.runtime.universe.Type = scala.Nothing + * + * scala> res1.finalResultType + * res5: reflect.runtime.universe.Type = scala.Nothing + * }}} + * + * @see finalResultType + */ + def resultType: Type + + /** For a curried/nullary method or poly type its non-method result type, + * the type itself for all other types. + * + * {{{ + * scala> class C { + * | def foo[T](x: T)(y: T) = ??? + * | def bar: Int = ??? + * | } + * defined class C + * + * scala> typeOf[C].member(TermName("foo")).asMethod + * res0: reflect.runtime.universe.MethodSymbol = method foo + * + * scala> res0.info // PolyType wrapping a MethodType + * res1: reflect.runtime.universe.Type = [T](x: T)(y: T)scala.Nothing + * + * scala> res1.resultType // MethodType wrapping a MethodType + * res2: reflect.runtime.universe.Type = (x: T)(y: T)scala.Nothing + * + * scala> res1.resultType.resultType // vanilla MethodType + * res3: reflect.runtime.universe.Type = (y: T)scala.Nothing + * + * scala> res1.resultType.resultType.resultType + * res4: reflect.runtime.universe.Type = scala.Nothing + * + * scala> res1.finalResultType + * res5: reflect.runtime.universe.Type = scala.Nothing + * + * scala> typeOf[C].member(TermName("bar")).asMethod + * res6: reflect.runtime.universe.MethodSymbol = method bar + * + * scala> res6.info + * res7: reflect.runtime.universe.Type = => scala.Int + * + * scala> res6.info.resultType + * res8: reflect.runtime.universe.Type = scala.Int + * + * scala> res6.info.finalResultType + * res9: reflect.runtime.universe.Type = scala.Int + * }}} + * + * @see resultType + */ + def finalResultType: Type + /******************* helpers *******************/ + /** Provides an alternate if type is NoType. + * + * @group Helpers + */ + def orElse(alt: => Type): Type + /** Substitute symbols in `to` for corresponding occurrences of references to * symbols `from` in this type. */ @@ -254,13 +400,12 @@ trait Types { self: Universe => * @template * @group Types */ - type SingletonType >: Null <: Type + type SingletonType >: Null <: SingletonTypeApi with Type - /** A tag that preserves the identity of the `SingletonType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** Has no special methods. Is here to provides erased identity for `SingletonType`. + * @group API */ - implicit val SingletonTypeTag: ClassTag[SingletonType] + trait SingletonTypeApi /** A singleton type that describes types of the form on the left with the * corresponding `ThisType` representation to the right: @@ -270,13 +415,7 @@ trait Types { self: Universe => * @template * @group Types */ - type ThisType >: Null <: AnyRef with SingletonType with ThisTypeApi - - /** A tag that preserves the identity of the `ThisType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThisTypeTag: ClassTag[ThisType] + type ThisType >: Null <: ThisTypeApi with SingletonType /** The constructor/extractor for `ThisType` instances. * @group Extractors @@ -288,11 +427,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class ThisTypeExtractor { - /** - * Creates a ThisType from the given class symbol. - */ - def apply(sym: Symbol): Type def unapply(tpe: ThisType): Option[Symbol] + + /** @see [[InternalApi.thisType]] */ + @deprecated("Use `internal.thisType` instead", "2.11.0") + def apply(sym: Symbol)(implicit token: CompatToken): Type = internal.thisType(sym) } /** The API that all this types support. @@ -314,13 +453,7 @@ trait Types { self: Universe => * @template * @group Types */ - type SingleType >: Null <: AnyRef with SingletonType with SingleTypeApi - - /** A tag that preserves the identity of the `SingleType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SingleTypeTag: ClassTag[SingleType] + type SingleType >: Null <: SingleTypeApi with SingletonType /** The constructor/extractor for `SingleType` instances. * @group Extractors @@ -333,8 +466,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class SingleTypeExtractor { - def apply(pre: Type, sym: Symbol): Type // not SingleTypebecause of implementation details def unapply(tpe: SingleType): Option[(Type, Symbol)] + + /** @see [[InternalApi.singleType]] */ + @deprecated("Use `ClassSymbol.thisPrefix` or `internal.singleType` instead") + def apply(pre: Type, sym: Symbol)(implicit token: CompatToken): Type = internal.singleType(pre, sym) } /** The API that all single types support. @@ -359,13 +495,7 @@ trait Types { self: Universe => * @template * @group Types */ - type SuperType >: Null <: AnyRef with SingletonType with SuperTypeApi - - /** A tag that preserves the identity of the `SuperType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SuperTypeTag: ClassTag[SuperType] + type SuperType >: Null <: SuperTypeApi with SingletonType /** The constructor/extractor for `SuperType` instances. * @group Extractors @@ -376,8 +506,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class SuperTypeExtractor { - def apply(thistpe: Type, supertpe: Type): Type // not SuperTypebecause of implementation details def unapply(tpe: SuperType): Option[(Type, Type)] + + /** @see [[InternalApi.superType]] */ + @deprecated("Use `ClassSymbol.superPrefix` or `internal.superType` instead", "2.11.0") + def apply(thistpe: Type, supertpe: Type)(implicit token: CompatToken): Type = internal.superType(thistpe, supertpe) } /** The API that all super types support. @@ -404,13 +537,7 @@ trait Types { self: Universe => * @template * @group Types */ - type ConstantType >: Null <: AnyRef with SingletonType with ConstantTypeApi - - /** A tag that preserves the identity of the `ConstantType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ConstantTypeTag: ClassTag[ConstantType] + type ConstantType >: Null <: ConstantTypeApi with SingletonType /** The constructor/extractor for `ConstantType` instances. * @group Extractors @@ -422,8 +549,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class ConstantTypeExtractor { - def apply(value: Constant): ConstantType def unapply(tpe: ConstantType): Option[Constant] + + /** @see [[InternalApi.constantType]] */ + @deprecated("Use `value.tpe` or `internal.constantType` instead", "2.11.0") + def apply(value: Constant)(implicit token: CompatToken): ConstantType = internal.constantType(value) } /** The API that all constant types support. @@ -448,13 +578,7 @@ trait Types { self: Universe => * @template * @group Types */ - type TypeRef >: Null <: AnyRef with Type with TypeRefApi - - /** A tag that preserves the identity of the `TypeRef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeRefTag: ClassTag[TypeRef] + type TypeRef >: Null <: TypeRefApi with Type /** The constructor/extractor for `TypeRef` instances. * @group Extractors @@ -468,8 +592,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class TypeRefExtractor { - def apply(pre: Type, sym: Symbol, args: List[Type]): Type // not TypeRefbecause of implementation details def unapply(tpe: TypeRef): Option[(Type, Symbol, List[Type])] + + /** @see [[InternalApi.typeRef]] */ + @deprecated("Use `internal.typeRef` instead", "2.11.0") + def apply(pre: Type, sym: Symbol, args: List[Type])(implicit token: CompatToken): Type = internal.typeRef(pre, sym, args) } /** The API that all type refs support. @@ -495,13 +622,12 @@ trait Types { self: Universe => * @template * @group Types */ - type CompoundType >: Null <: AnyRef with Type + type CompoundType >: Null <: CompoundTypeApi with Type - /** A tag that preserves the identity of the `CompoundType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags + /** Has no special methods. Is here to provides erased identity for `CompoundType`. + * @group API */ - implicit val CompoundTypeTag: ClassTag[CompoundType] + trait CompoundTypeApi /** The `RefinedType` type defines types of any of the forms on the left, * with their RefinedType representations to the right. @@ -513,13 +639,7 @@ trait Types { self: Universe => * @template * @group Types */ - type RefinedType >: Null <: AnyRef with CompoundType with RefinedTypeApi - - /** A tag that preserves the identity of the `RefinedType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val RefinedTypeTag: ClassTag[RefinedType] + type RefinedType >: Null <: RefinedTypeApi with CompoundType /** The constructor/extractor for `RefinedType` instances. * @group Extractors @@ -532,13 +652,15 @@ trait Types { self: Universe => * @group Extractors */ abstract class RefinedTypeExtractor { - def apply(parents: List[Type], decls: Scope): RefinedType - - /** An alternative constructor that passes in the synthetic classs symbol - * that backs the refined type. (Normally, a fresh class symbol is created automatically). - */ - def apply(parents: List[Type], decls: Scope, clazz: Symbol): RefinedType def unapply(tpe: RefinedType): Option[(List[Type], Scope)] + + /** @see [[InternalApi.refinedType]] */ + @deprecated("Use `internal.refinedType` instead", "2.11.0") + def apply(parents: List[Type], decls: Scope)(implicit token: CompatToken): RefinedType = internal.refinedType(parents, decls) + + /** @see [[InternalApi.refinedType]] */ + @deprecated("Use `internal.refinedType` instead", "2.11.0") + def apply(parents: List[Type], decls: Scope, clazz: Symbol)(implicit token: CompatToken): RefinedType = internal.refinedType(parents, decls, clazz) } /** The API that all refined types support. @@ -550,7 +672,7 @@ trait Types { self: Universe => def parents: List[Type] /** The scope that holds the definitions comprising the type. */ - def decls: Scope + def decls: MemberScope } /** The `ClassInfo` type signature is used to define parents and declarations @@ -565,13 +687,7 @@ trait Types { self: Universe => * @template * @group Types */ - type ClassInfoType >: Null <: AnyRef with CompoundType with ClassInfoTypeApi - - /** A tag that preserves the identity of the `ClassInfoType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ClassInfoTypeTag: ClassTag[ClassInfoType] + type ClassInfoType >: Null <: ClassInfoTypeApi with CompoundType /** The constructor/extractor for `ClassInfoType` instances. * @group Extractors @@ -585,8 +701,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class ClassInfoTypeExtractor { - def apply(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType def unapply(tpe: ClassInfoType): Option[(List[Type], Scope, Symbol)] + + /** @see [[InternalApi.classInfoType]] */ + @deprecated("Use `internal.classInfoType` instead", "2.11.0") + def apply(parents: List[Type], decls: Scope, typeSymbol: Symbol)(implicit token: CompatToken): ClassInfoType = internal.classInfoType(parents, decls, typeSymbol) } /** The API that all class info types support. @@ -598,7 +717,7 @@ trait Types { self: Universe => def parents: List[Type] /** The scope that holds the definitions comprising the class type. */ - def decls: Scope + def decls: MemberScope /** The symbol underlying the class type. */ def typeSymbol: Symbol @@ -608,13 +727,7 @@ trait Types { self: Universe => * @template * @group Types */ - type MethodType >: Null <: AnyRef with Type with MethodTypeApi - - /** A tag that preserves the identity of the `MethodType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MethodTypeTag: ClassTag[MethodType] + type MethodType >: Null <: MethodTypeApi with Type /** The constructor/extractor for `MethodType` instances. * @group Extractors @@ -637,8 +750,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class MethodTypeExtractor { - def apply(params: List[Symbol], resultType: Type): MethodType def unapply(tpe: MethodType): Option[(List[Symbol], Type)] + + /** @see [[InternalApi.methodType]] */ + @deprecated("Use `internal.methodType` instead", "2.11.0") + def apply(params: List[Symbol], resultType: Type)(implicit token: CompatToken): MethodType = internal.methodType(params, resultType) } /** The API that all method types support. @@ -658,13 +774,7 @@ trait Types { self: Universe => * @template * @group Types */ - type NullaryMethodType >: Null <: AnyRef with Type with NullaryMethodTypeApi - - /** A tag that preserves the identity of the `NullaryMethodType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NullaryMethodTypeTag: ClassTag[NullaryMethodType] + type NullaryMethodType >: Null <: NullaryMethodTypeApi with Type /** The constructor/extractor for `NullaryMethodType` instances. * @group Extractors @@ -676,8 +786,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class NullaryMethodTypeExtractor { - def apply(resultType: Type): NullaryMethodType def unapply(tpe: NullaryMethodType): Option[(Type)] + + /** @see [[InternalApi.nullaryMethodType]] */ + @deprecated("Use `internal.nullaryMethodType` instead", "2.11.0") + def apply(resultType: Type)(implicit token: CompatToken): NullaryMethodType = internal.nullaryMethodType(resultType) } /** The API that all nullary method types support. @@ -694,13 +807,7 @@ trait Types { self: Universe => * @template * @group Types */ - type PolyType >: Null <: AnyRef with Type with PolyTypeApi - - /** A tag that preserves the identity of the `PolyType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val PolyTypeTag: ClassTag[PolyType] + type PolyType >: Null <: PolyTypeApi with Type /** The constructor/extractor for `PolyType` instances. * @group Extractors @@ -713,8 +820,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class PolyTypeExtractor { - def apply(typeParams: List[Symbol], resultType: Type): PolyType def unapply(tpe: PolyType): Option[(List[Symbol], Type)] + + /** @see [[InternalApi.polyType]] */ + @deprecated("Use `internal.polyType` instead", "2.11.0") + def apply(typeParams: List[Symbol], resultType: Type)(implicit token: CompatToken): PolyType = internal.polyType(typeParams, resultType) } /** The API that all polymorphic types support. @@ -734,13 +844,7 @@ trait Types { self: Universe => * @template * @group Types */ - type ExistentialType >: Null <: AnyRef with Type with ExistentialTypeApi - - /** A tag that preserves the identity of the `ExistentialType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ExistentialTypeTag: ClassTag[ExistentialType] + type ExistentialType >: Null <: ExistentialTypeApi with Type /** The constructor/extractor for `ExistentialType` instances. * @group Extractors @@ -754,8 +858,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class ExistentialTypeExtractor { - def apply(quantified: List[Symbol], underlying: Type): ExistentialType def unapply(tpe: ExistentialType): Option[(List[Symbol], Type)] + + /** @see [[InternalApi.existentialType]] */ + @deprecated("Use `internal.existentialType` instead", "2.11.0") + def apply(quantified: List[Symbol], underlying: Type)(implicit token: CompatToken): ExistentialType = internal.existentialType(quantified, underlying) } /** The API that all existential types support. @@ -775,13 +882,7 @@ trait Types { self: Universe => * @template * @group Types */ - type AnnotatedType >: Null <: AnyRef with Type with AnnotatedTypeApi - - /** A tag that preserves the identity of the `AnnotatedType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AnnotatedTypeTag: ClassTag[AnnotatedType] + type AnnotatedType >: Null <: AnnotatedTypeApi with Type /** The constructor/extractor for `AnnotatedType` instances. * @group Extractors @@ -789,14 +890,17 @@ trait Types { self: Universe => val AnnotatedType: AnnotatedTypeExtractor /** An extractor class to create and pattern match with syntax - * `AnnotatedType(annotations, underlying, selfsym)`. + * `AnnotatedType(annotations, underlying)`. * Here, `annotations` are the annotations decorating the underlying type `underlying`. * `selfSym` is a symbol representing the annotated type itself. * @group Extractors */ abstract class AnnotatedTypeExtractor { - def apply(annotations: List[Annotation], underlying: Type, selfsym: Symbol): AnnotatedType - def unapply(tpe: AnnotatedType): Option[(List[Annotation], Type, Symbol)] + def unapply(tpe: AnnotatedType): Option[(List[Annotation], Type)] + + /** @see [[InternalApi.annotatedType]] */ + @deprecated("Use `internal.annotatedType` instead", "2.11.0") + def apply(annotations: List[Annotation], underlying: Type)(implicit token: CompatToken): AnnotatedType = internal.annotatedType(annotations, underlying) } /** The API that all annotated types support. @@ -809,9 +913,6 @@ trait Types { self: Universe => /** The annotee. */ def underlying: Type - - /** A symbol that represents the annotated type itself. */ - def selfsym: Symbol } /** The `TypeBounds` type signature is used to indicate lower and upper type bounds @@ -826,13 +927,7 @@ trait Types { self: Universe => * @template * @group Types */ - type TypeBounds >: Null <: AnyRef with Type with TypeBoundsApi - - /** A tag that preserves the identity of the `TypeBounds` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeBoundsTag: ClassTag[TypeBounds] + type TypeBounds >: Null <: TypeBoundsApi with Type /** The constructor/extractor for `TypeBounds` instances. * @group Extractors @@ -845,8 +940,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class TypeBoundsExtractor { - def apply(lo: Type, hi: Type): TypeBounds def unapply(tpe: TypeBounds): Option[(Type, Type)] + + /** @see [[InternalApi.typeBounds]] */ + @deprecated("Use `internal.typeBounds` instead", "2.11.0") + def apply(lo: Type, hi: Type)(implicit token: CompatToken): TypeBounds = internal.typeBounds(lo, hi) } /** The API that all type bounds support. @@ -883,13 +981,7 @@ trait Types { self: Universe => * @template * @group Types */ - type BoundedWildcardType >: Null <: AnyRef with Type with BoundedWildcardTypeApi - - /** A tag that preserves the identity of the `BoundedWildcardType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BoundedWildcardTypeTag: ClassTag[BoundedWildcardType] + type BoundedWildcardType >: Null <: BoundedWildcardTypeApi with Type /** The constructor/extractor for `BoundedWildcardType` instances. * @group Extractors @@ -901,8 +993,11 @@ trait Types { self: Universe => * @group Extractors */ abstract class BoundedWildcardTypeExtractor { - def apply(bounds: TypeBounds): BoundedWildcardType def unapply(tpe: BoundedWildcardType): Option[TypeBounds] + + /** @see [[InternalApi.boundedWildcardType]] */ + @deprecated("Use `internal.boundedWildcardType` instead", "2.11.0") + def apply(bounds: TypeBounds)(implicit token: CompatToken): BoundedWildcardType = internal.boundedWildcardType(bounds) } /** The API that all this types support. @@ -924,74 +1019,17 @@ trait Types { self: Universe => */ def glb(ts: List[Type]): Type - // Creators --------------------------------------------------------------- - // too useful and too non-trivial to be left out of public API - - /** The canonical creator for single-types - * @group TypeCreators - */ - def singleType(pre: Type, sym: Symbol): Type - - /** the canonical creator for a refined type with a given scope - * @group TypeCreators - */ - def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): Type - - /** The canonical creator for a refined type with an initially empty scope. - * @group TypeCreators - */ - def refinedType(parents: List[Type], owner: Symbol): Type - - /** The canonical creator for typerefs - * @group TypeCreators - */ - 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. - * @group TypeCreators - */ - 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? - * @group TypeCreators - */ - def intersectionType(tps: List[Type], owner: Symbol): Type - /** A creator for type applications - * @group Types + * @group TypeOps */ 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). - * @group Types - */ - def polyType(tparams: List[Symbol], tpe: Type): Type + /** @see [[appliedType]] */ + def appliedType(tycon: Type, args: 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`. - * @group TypeCreators - */ - def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type + /** @see [[appliedType]] */ + def appliedType(sym: Symbol, args: List[Type]): Type + + /** @see [[appliedType]] */ + def appliedType(sym: Symbol, args: Type*): Type } diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 15fa11c6cf..a3d1d291eb 100644 --- a/src/reflect/scala/reflect/api/Universe.scala +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -40,7 +41,8 @@ package api * res1: reflect.runtime.universe.Type = scala.Either[String,Int] * }}} * - * To obtain a `Universe` for use within a Scala macro, use [[scala.reflect.macros.Context#universe]]. For example: + * To obtain a `Universe` for use within a Scala macro, use [[scala.reflect.macros.blackbox.Context#universe]]. + * or [[scala.reflect.macros.whitebox.Context#universe]]. For example: * {{{ * def printf(format: String, params: Any*): Unit = macro impl * def impl(c: Context)(format: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = { @@ -67,13 +69,15 @@ abstract class Universe extends Symbols with Positions with Exprs with TypeTags - with TagInterop + with ImplicitTags with StandardDefinitions with StandardNames - with BuildUtils + with StandardLiftables with Mirrors with Printers - with Importers + with Liftables + with Quasiquotes + with Internals { /** Use `reify` to produce the abstract syntax tree representing a given Scala expression. * @@ -92,5 +96,5 @@ abstract class Universe extends Symbols */ // implementation is hardwired to `scala.reflect.reify.Taggers` // using the mechanism implemented in `scala.tools.reflect.FastTrack` - def reify[T](expr: T): Expr[T] = ??? // macro + def reify[T](expr: T): Expr[T] = macro ??? } diff --git a/src/reflect/scala/reflect/api/package.scala b/src/reflect/scala/reflect/api/package.scala index dbda84dd0e..a8f409e123 100644 --- a/src/reflect/scala/reflect/api/package.scala +++ b/src/reflect/scala/reflect/api/package.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect import scala.reflect.api.{Universe => ApiUniverse} @@ -42,6 +43,6 @@ package object api { // implementation is hardwired into `scala.reflect.reify.Taggers` // using the mechanism implemented in `scala.tools.reflect.FastTrack` // todo. once we have implicit macros for tag generation, we can remove these anchors - private[scala] def materializeWeakTypeTag[T](u: ApiUniverse): u.WeakTypeTag[T] = ??? // macro - private[scala] def materializeTypeTag[T](u: ApiUniverse): u.TypeTag[T] = ??? // macro + private[scala] def materializeWeakTypeTag[T](u: ApiUniverse): u.WeakTypeTag[T] = macro ??? + private[scala] def materializeTypeTag[T](u: ApiUniverse): u.TypeTag[T] = macro ??? }
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala index 1ab975b233..74310e1c34 100644 --- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal /** Additions to the type checker that can be added at @@ -52,7 +53,7 @@ trait AnnotationCheckers { * given type tp, taking into account the given mode (see method adapt in trait Typers). */ @deprecated("Create an AnalyzerPlugin and use canAdaptAnnotations", "2.10.1") - def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = false + def canAdaptAnnotations(tree: Tree, mode: Mode, pt: Type): Boolean = false /** * Adapt a tree that has an annotated type to the given type tp, taking into account the given @@ -62,7 +63,7 @@ trait AnnotationCheckers { * class cannot do the adaptiong, it should return the tree unchanged. */ @deprecated("Create an AnalyzerPlugin and use adaptAnnotations", "2.10.1") - def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree + def adaptAnnotations(tree: Tree, mode: Mode, pt: Type): Tree = tree /** * Adapt the type of a return expression. The decision of a typer plugin whether the type @@ -126,13 +127,13 @@ trait AnnotationCheckers { else annotationCheckers.foldLeft(tpe)((tpe, checker) => if (!checker.isActive()) tpe else checker.addAnnotations(tree, tpe)) - def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = + def canAdaptAnnotations(tree: Tree, mode: Mode, pt: Type): Boolean = if (annotationCheckers.isEmpty) false else annotationCheckers.exists(checker => { checker.isActive() && checker.canAdaptAnnotations(tree, mode, pt) }) - def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = + def adaptAnnotations(tree: Tree, mode: Mode, pt: Type): Tree = if (annotationCheckers.isEmpty) tree else annotationCheckers.foldLeft(tree)((tree, checker) => if (!checker.isActive()) tree else checker.adaptAnnotations(tree, mode, pt)) diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 80b6b16d0a..f814a746f5 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -3,17 +3,19 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal -import util._ import pickling.ByteCodecs import scala.annotation.tailrec import scala.collection.immutable.ListMap +import scala.language.postfixOps /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.Annotations { self: SymbolTable => - import definitions.{ ThrowsClass, StaticAnnotationClass, isMetaAnnotation } + import definitions._ + import treeInfo._ // Common annotation code between Symbol and Type. // For methods altering the annotation list, on Symbol it mutates @@ -27,6 +29,8 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => def filterAnnotations(p: AnnotationInfo => Boolean): Self // Retain only annotations meeting the condition. def withoutAnnotations: Self // Remove all annotations from this type. + def staticAnnotations = annotations filter (_.isStatic) + /** Symbols of any @throws annotations on this symbol. */ def throwsAnnotations(): List[Symbol] = annotations collect { @@ -40,14 +44,13 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => // monomorphic one by introducing existentials, see SI-7009 for details existentialAbstraction(throwableSym.typeParams, throwableSym.tpe) } - val throwsAnn = AnnotationInfo(appliedType(definitions.ThrowsClass, throwableTpe), List(Literal(Constant(throwableTpe))), Nil) - withAnnotations(List(throwsAnn)) + this withAnnotation AnnotationInfo(appliedType(ThrowsClass, throwableTpe), List(Literal(Constant(throwableTpe))), Nil) } /** Tests for, get, or remove an annotation */ def hasAnnotation(cls: Symbol): Boolean = //OPT inlined from exists to save on #closures; was: annotations exists (_ matches cls) - dropOtherAnnotations(annotations, cls).nonEmpty + dropOtherAnnotations(annotations, cls) ne Nil def getAnnotation(cls: Symbol): Option[AnnotationInfo] = //OPT inlined from exists to save on #closures; was: annotations find (_ matches cls) @@ -74,7 +77,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => * - arrays of constants * - or nested classfile annotations */ - abstract class ClassfileAnnotArg extends Product + sealed abstract class ClassfileAnnotArg extends Product with JavaArgumentApi implicit val JavaArgumentTag = ClassTag[ClassfileAnnotArg](classOf[ClassfileAnnotArg]) case object UnmappableAnnotArg extends ClassfileAnnotArg @@ -122,25 +125,32 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => * 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. + * Details about the storage format of pickles at the bytecode level (classfile annotations) can be found in SIP-10. */ 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)) } + + /* In order to store a byte array (the pickle) using a bytecode-level annotation, + * the most compact representation is used (which happens to be string-constant and not byte array as one would expect). + * However, a String constant in a classfile annotation is limited to a maximum of 65535 characters. + * Method `fitsInOneString` tells us whether the pickle can be held by a single classfile-annotation of string-type. + * Otherwise an array of strings will be used. + */ def fitsInOneString: Boolean = { + // due to escaping, a zero byte in a classfile-annotation of string-type takes actually two characters. 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 + + (sevenBitsMayBeZero.length + numZeros) <= 65535 } + def sigAnnot: Type = - if (this.isLong) - definitions.ScalaLongSignatureAnnotation.tpe - else + if (fitsInOneString) definitions.ScalaSignatureAnnotation.tpe + else + definitions.ScalaLongSignatureAnnotation.tpe private def mapToNextModSevenBits(src: Array[Byte]): Array[Byte] = { var i = 0 @@ -282,8 +292,8 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => * metaAnnotations = List(setter, field). */ def metaAnnotations: List[AnnotationInfo] = atp match { - case AnnotatedType(metas, _, _) => metas - case _ => Nil + case AnnotatedType(metas, _) => metas + case _ => Nil } /** The default kind of members to which this annotation is attached. @@ -304,10 +314,6 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => /** 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 { @@ -340,15 +346,74 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => } implicit val AnnotationTag = ClassTag[AnnotationInfo](classOf[AnnotationInfo]) + protected[scala] def annotationToTree(ann: Annotation): Tree = { + def reverseEngineerArgs(): List[Tree] = { + def reverseEngineerArg(jarg: ClassfileAnnotArg): Tree = jarg match { + case LiteralAnnotArg(const) => + val tpe = if (const.tag == UnitTag) UnitTpe else ConstantType(const) + Literal(const) setType tpe + case ArrayAnnotArg(jargs) => + val args = jargs map reverseEngineerArg + // TODO: I think it would be a good idea to typecheck Java annotations using a more traditional algorithm + // sure, we can't typecheck them as is using the `new jann(foo = bar)` syntax (because jann is going to be an @interface) + // however we can do better than `typedAnnotation` by desugaring the aforementioned expression to + // something like `new jann() { override def annotatedType() = ...; override def foo = bar }` + // and then using the results of that typecheck to produce a Java-compatible classfile entry + // in that case we're going to have correctly typed Array.apply calls, however that's 2.12 territory + // and for 2.11 exposing an untyped call to ArrayModule should suffice + Apply(Ident(ArrayModule), args.toList) + case NestedAnnotArg(ann: Annotation) => + annotationToTree(ann) + case _ => + EmptyTree + } + def reverseEngineerArgs(jargs: List[(Name, ClassfileAnnotArg)]): List[Tree] = jargs match { + case (name, jarg) :: rest => AssignOrNamedArg(Ident(name), reverseEngineerArg(jarg)) :: reverseEngineerArgs(rest) + case Nil => Nil + } + if (ann.javaArgs.isEmpty) ann.scalaArgs + else reverseEngineerArgs(ann.javaArgs.toList) + } + + // TODO: at the moment, constructor selection is unattributed, because AnnotationInfos lack necessary information + // later on, in 2.12, for every annotation we could save an entire tree instead of just bits and pieces + // but for 2.11 the current situation will have to do + val ctorSelection = Select(New(TypeTree(ann.atp)), nme.CONSTRUCTOR) + Apply(ctorSelection, reverseEngineerArgs()) setType ann.atp + } + + protected[scala] def treeToAnnotation(tree: Tree): Annotation = tree match { + case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => + def encodeJavaArg(arg: Tree): ClassfileAnnotArg = arg match { + case Literal(const) => LiteralAnnotArg(const) + case Apply(ArrayModule, args) => ArrayAnnotArg(args map encodeJavaArg toArray) + case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => NestedAnnotArg(treeToAnnotation(arg)) + case _ => throw new Exception("unexpected java argument shape $arg: literals, arrays and nested annotations are supported") + } + def encodeJavaArgs(args: List[Tree]): List[(Name, ClassfileAnnotArg)] = args match { + case AssignOrNamedArg(Ident(name), arg) :: rest => (name, encodeJavaArg(arg)) :: encodeJavaArgs(rest) + case arg :: rest => throw new Exception("unexpected java argument shape $arg: only AssignOrNamedArg trees are supported") + case Nil => Nil + } + val atp = tpt.tpe + if (atp != null && (atp.typeSymbol isNonBottomSubClass StaticAnnotationClass)) AnnotationInfo(atp, args, Nil) + else if (atp != null && (atp.typeSymbol isNonBottomSubClass ClassfileAnnotationClass)) AnnotationInfo(atp, Nil, encodeJavaArgs(args)) + else throw new Exception(s"unexpected annotation type $atp: only subclasses of StaticAnnotation and ClassfileAnnotation are supported") + case _ => + throw new Exception("""unexpected tree shape: only q"new $annType(..$args)" is supported""") + } + object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil) + object ErroneousAnnotation extends CompleteAnnotationInfo(ErrorType, Nil, Nil) + /** Extracts symbol of thrown exception from AnnotationInfo. * * Supports both “old-style” `@throws(classOf[Exception])` * as well as “new-stye” `@throws[Exception]("cause")` annotations. */ object ThrownException { - def unapply(ann: AnnotationInfo): Option[Symbol] = + def unapply(ann: AnnotationInfo): Option[Symbol] = { ann match { case AnnotationInfo(tpe, _, _) if tpe.typeSymbol != ThrowsClass => None @@ -356,8 +421,11 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => case AnnotationInfo(_, List(Literal(Constant(tpe: Type))), _) => Some(tpe.typeSymbol) // new-style: @throws[Exception], @throws[Exception]("cause") - case AnnotationInfo(TypeRef(_, _, args), _, _) => - Some(args.head.typeSymbol) + case AnnotationInfo(TypeRef(_, _, arg :: _), _, _) => + Some(arg.typeSymbol) + case AnnotationInfo(TypeRef(_, _, Nil), _, _) => + Some(ThrowableClass) } + } } } diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 3c2b128c52..0ca8611719 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal // todo implement in terms of BitSet @@ -37,7 +38,7 @@ trait BaseTypeSeqs { * 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]) { + class BaseTypeSeq protected[reflect] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqCount) if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqLenTotal, elems.length) @@ -64,16 +65,15 @@ trait BaseTypeSeqs { //Console.println("compute closure of "+this+" => glb("+variants+")") pending += i try { - mergePrefixAndArgs(variants, -1, lubDepth(variants)) match { - case Some(tp0) => + mergePrefixAndArgs(variants, Variance.Contravariant, lubDepth(variants)) match { + case NoType => typeError("no common type instance of base types "+(variants mkString ", and ")+" exists.") + case tp0 => pending(i) = false elems(i) = tp0 tp0 - case None => - typeError( - "no common type instance of base types "+(variants mkString ", and ")+" exists.") } - } catch { + } + catch { case CyclicInheritance => typeError( "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.") @@ -115,7 +115,7 @@ trait BaseTypeSeqs { def map(f: Type => Type): BaseTypeSeq = { // inlined `elems map f` for performance val len = length - var arr = new Array[Type](len) + val arr = new Array[Type](len) var i = 0 while (i < len) { arr(i) = f(elems(i)) @@ -130,9 +130,9 @@ trait BaseTypeSeqs { lazy val maxDepth = maxDepthOfElems - protected def maxDepthOfElems: Int = { - var d = 0 - for (i <- 1 until length) d = max(d, typeDepth(elems(i))) + protected def maxDepthOfElems: Depth = { + var d = Depth.Zero + 1 until length foreach (i => d = d max typeDepth(elems(i))) d } @@ -158,7 +158,7 @@ trait BaseTypeSeqs { val parents = tp.parents // Console.println("computing baseTypeSeq of " + tsym.tpe + " " + parents)//DEBUG val buf = new mutable.ListBuffer[Type] - buf += tsym.tpe + buf += tsym.tpe_* var btsSize = 1 if (parents.nonEmpty) { val nparents = parents.length @@ -166,9 +166,10 @@ trait BaseTypeSeqs { val index = new Array[Int](nparents) var i = 0 for (p <- parents) { + val parentBts = p.dealias.baseTypeSeq // dealias need for SI-8046. pbtss(i) = - if (p.baseTypeSeq eq undetBaseTypeSeq) AnyClass.info.baseTypeSeq - else p.baseTypeSeq + if (parentBts eq undetBaseTypeSeq) AnyClass.info.baseTypeSeq + else parentBts index(i) = 0 i += 1 } @@ -180,7 +181,7 @@ trait BaseTypeSeqs { def nextRawElem(i: Int): Type = { val j = index(i) val pbts = pbtss(i) - if (j < pbts.length) pbts.rawElem(j) else AnyClass.tpe + if (j < pbts.length) pbts.rawElem(j) else AnyTpe } var minSym: Symbol = NoSymbol while (minSym != AnyClass) { @@ -193,15 +194,23 @@ trait BaseTypeSeqs { i += 1 } var minTypes: List[Type] = List() + def alreadyInMinTypes(tp: Type): Boolean = { + @annotation.tailrec def loop(tps: List[Type]): Boolean = tps match { + case Nil => false + case x :: xs => (tp =:= x) || loop(xs) + } + loop(minTypes) + } + 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 + if (!alreadyInMinTypes(tp)) minTypes ::= tp case tp => - if (!(minTypes exists (tp =:= _))) minTypes = tp :: minTypes + if (!alreadyInMinTypes(tp)) minTypes ::= tp } index(i) = index(i) + 1 } @@ -226,7 +235,7 @@ trait BaseTypeSeqs { 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 => typeDepth(f(x))).max + override protected def maxDepthOfElems: Depth = elems.map(x => typeDepth(f(x))).max override def toString = elems.mkString("MBTS(", ",", ")") } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala deleted file mode 100644 index 9f41f0336e..0000000000 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ /dev/null @@ -1,68 +0,0 @@ -package scala.reflect -package internal - -import Flags._ - -trait BuildUtils { self: SymbolTable => - - class BuildImpl extends BuildApi { - - def selectType(owner: Symbol, name: String): TypeSymbol = - select(owner, newTypeName(name)).asType - - def selectTerm(owner: Symbol, name: String): TermSymbol = { - val result = select(owner, newTermName(name)).asTerm - if (result.isOverloaded) result.suchThat(!_.isMethod).asTerm - else result - } - - private def select(owner: Symbol, name: Name): Symbol = { - val result = owner.info decl name - if (result ne NoSymbol) result - else - mirrorThatLoaded(owner).missingHook(owner, name) orElse - MissingRequirementError.notFound("%s %s in %s".format(if (name.isTermName) "term" else "type", 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.asMethod - else MissingRequirementError.notFound("overloaded method %s #%d in %s".format(name, index, owner.fullName)) - } - - def newFreeTerm(name: String, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = - newFreeTermSymbol(newTermName(name), value, flags, origin) - - def newFreeType(name: String, flags: Long = 0L, origin: String = null): FreeTypeSymbol = - newFreeTypeSymbol(newTypeName(name), flags, 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: BuildApi = new BuildImpl -} diff --git a/src/reflect/scala/reflect/internal/CapturedVariables.scala b/src/reflect/scala/reflect/internal/CapturedVariables.scala index a3d2a8bd94..ef9646b80f 100644 --- a/src/reflect/scala/reflect/internal/CapturedVariables.scala +++ b/src/reflect/scala/reflect/internal/CapturedVariables.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal import Flags._ @@ -19,7 +20,7 @@ trait CapturedVariables { self: SymbolTable => /** Convert type of a captured variable to *Ref type. */ def capturedVariableType(vble: Symbol): Type = - capturedVariableType(vble, NoType, false) + capturedVariableType(vble, NoType, erasedTypes = false) /** Convert type of a captured variable to *Ref type. */ diff --git a/src/reflect/scala/reflect/internal/Chars.scala b/src/reflect/scala/reflect/internal/Chars.scala index 2d07092862..74413fdaba 100644 --- a/src/reflect/scala/reflect/internal/Chars.scala +++ b/src/reflect/scala/reflect/internal/Chars.scala @@ -2,7 +2,8 @@ * Copyright 2006-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.annotation.{ tailrec, switch } diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index eb70ff3f17..e0a6757d34 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -3,13 +3,13 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.annotation.switch object ClassfileConstants { - final val JAVA_MAGIC = 0xCAFEBABE final val JAVA_MAJOR_VERSION = 45 final val JAVA_MINOR_VERSION = 3 @@ -340,7 +340,7 @@ object ClassfileConstants { case JAVA_ACC_PRIVATE => PRIVATE case JAVA_ACC_PROTECTED => PROTECTED case JAVA_ACC_FINAL => FINAL - case JAVA_ACC_SYNTHETIC => SYNTHETIC + case JAVA_ACC_SYNTHETIC => SYNTHETIC | ARTIFACT // maybe should be just artifact? 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 @@ -349,7 +349,7 @@ object ClassfileConstants { private def translateFlags(jflags: Int, baseFlags: Long, isAnnotation: Boolean, isClass: Boolean): Long = { def translateFlag0(jflags: Int): Long = translateFlag(jflags, isAnnotation, isClass) var res: Long = JAVA | baseFlags - /** fast, elegant, maintainable, pick any two... */ + /* fast, elegant, maintainable, pick any two... */ res |= translateFlag0(jflags & JAVA_ACC_PRIVATE) res |= translateFlag0(jflags & JAVA_ACC_PROTECTED) res |= translateFlag0(jflags & JAVA_ACC_FINAL) @@ -367,7 +367,7 @@ object ClassfileConstants { translateFlags(jflags, if ((jflags & JAVA_ACC_FINAL) == 0) MUTABLE else 0 , isAnnotation(jflags), isClass = false) } def methodFlags(jflags: Int): Long = { - translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE else 0, isAnnotation(jflags), isClass = false) + translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE | ARTIFACT else 0, isAnnotation(jflags), isClass = false) } } object FlagTranslation extends FlagTranslation { } @@ -375,11 +375,4 @@ object ClassfileConstants { 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 index 28bc3e1dd0..85d0efdcba 100644 --- a/src/reflect/scala/reflect/internal/Constants.scala +++ b/src/reflect/scala/reflect/internal/Constants.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import java.lang.Integer.toOctalString @@ -62,17 +63,17 @@ trait Constants extends api.Constants { 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 UnitTag => UnitTpe + case BooleanTag => BooleanTpe + case ByteTag => ByteTpe + case ShortTag => ShortTpe + case CharTag => CharTpe + case IntTag => IntTpe + case LongTag => LongTpe + case FloatTag => FloatTpe + case DoubleTag => DoubleTpe + case StringTag => StringTpe + case NullTag => NullTpe case ClazzTag => ClassType(typeValue) case EnumTag => EnumType(symbolValue) } @@ -94,7 +95,7 @@ trait Constants extends api.Constants { def booleanValue: Boolean = if (tag == BooleanTag) value.asInstanceOf[Boolean] - else throw new Error("value " + value + " is not a boolean"); + else throw new Error("value " + value + " is not a boolean") def byteValue: Byte = tag match { case ByteTag => value.asInstanceOf[Byte] @@ -211,7 +212,7 @@ trait Constants extends api.Constants { case '"' => "\\\"" case '\'' => "\\\'" case '\\' => "\\\\" - case _ => if (ch.isControl) "\\0" + toOctalString(ch) else String.valueOf(ch) + case _ => if (ch.isControl) "\\0" + toOctalString(ch.toInt) else String.valueOf(ch) } def escapedStringValue: String = { @@ -222,7 +223,15 @@ trait Constants extends api.Constants { case ClazzTag => def show(tpe: Type) = "classOf[" + signature(tpe) + "]" typeValue match { - case ErasedValueType(orig) => show(orig) + case ErasedValueType(clazz, underlying) => + // A note on tpe_* usage here: + // + // We've intentionally erased the type arguments to the value class so that different + // instantiations of a particular value class that erase to the same underlying type + // don't result in spurious bridges (e.g. run/t6385.scala). I don't think that matters; + // printing trees of `classOf[ValueClass[String]]` shows `classOf[ValueClass]` at phase + // erasure both before and after the use of `tpe_*` here. + show(clazz.tpe_*) case _ => show(typeValue) } case CharTag => "'" + escapedChar(charValue) + "'" diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 09d7af82d1..bf560a21e5 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -3,19 +3,20 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal +import scala.language.postfixOps import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } import Flags._ -import PartialFunction._ import scala.reflect.api.{Universe => ApiUniverse} trait Definitions extends api.StandardDefinitions { self: SymbolTable => - import rootMirror.{getModule, getClassByName, getRequiredClass, getRequiredModule, getRequiredPackage, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageObjectIfDefined, requiredClass, requiredModule} + import rootMirror.{getModuleByName, getPackage, getClassByName, getRequiredClass, getRequiredModule, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageIfDefined, getPackageObjectIfDefined, requiredClass, requiredModule} object definitions extends DefinitionsClass @@ -29,12 +30,13 @@ trait Definitions extends api.StandardDefinitions { 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) + clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz) markAllCompleted } - private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol = { + private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long): MethodSymbol = { val msym = owner.newMethod(name.encode, NoPosition, flags) val params = msym.newSyntheticValueParams(formals) - msym setInfo MethodType(params, restpe) + val info = if (owner.isJavaDefined) JavaMethodType(params, restpe) else MethodType(params, restpe) + msym setInfo info markAllCompleted } 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) @@ -67,7 +69,7 @@ trait Definitions extends api.StandardDefinitions { tpnme.Unit -> VOID_TAG ) - private def catastrophicFailure() = + private[Definitions] def catastrophicFailure() = abort("Could not find value classes! This is a catastrophic failure. scala " + scala.util.Properties.versionString) @@ -77,16 +79,8 @@ trait Definitions extends api.StandardDefinitions { 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[Definitions] 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) @@ -94,12 +88,10 @@ trait Definitions extends api.StandardDefinitions { 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 boxedModule = classesMap(x => getModuleByName(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) @@ -124,19 +116,19 @@ trait Definitions extends api.StandardDefinitions { 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 UnitTpe = UnitClass.toTypeConstructor - lazy val ByteTpe = ByteClass.toTypeConstructor - lazy val ShortTpe = ShortClass.toTypeConstructor - lazy val CharTpe = CharClass.toTypeConstructor - lazy val IntTpe = IntClass.toTypeConstructor - lazy val LongTpe = LongClass.toTypeConstructor - lazy val FloatTpe = FloatClass.toTypeConstructor - lazy val DoubleTpe = DoubleClass.toTypeConstructor - lazy val BooleanTpe = BooleanClass.toTypeConstructor + def Boolean_and = getMemberMethod(BooleanClass, nme.ZAND) + def Boolean_or = getMemberMethod(BooleanClass, nme.ZOR) + def Boolean_not = getMemberMethod(BooleanClass, nme.UNARY_!) + + lazy val UnitTpe = UnitClass.tpe + lazy val ByteTpe = ByteClass.tpe + lazy val ShortTpe = ShortClass.tpe + lazy val CharTpe = CharClass.tpe + lazy val IntTpe = IntClass.tpe + lazy val LongTpe = LongClass.tpe + lazy val FloatTpe = FloatClass.tpe + lazy val DoubleTpe = DoubleClass.tpe + lazy val BooleanTpe = BooleanClass.tpe lazy val ScalaNumericValueClasses = ScalaValueClasses filterNot Set[Symbol](UnitClass, BooleanClass) lazy val ScalaValueClassesNoUnit = ScalaValueClasses filterNot (_ eq UnitClass) @@ -151,50 +143,26 @@ trait Definitions extends api.StandardDefinitions { FloatClass, DoubleClass ) - def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + + def underlyingOfValueClass(clazz: Symbol): Type = + clazz.derivedValueClassUnbox.tpe.resultType + } abstract class DefinitionsClass extends DefinitionsApi with ValueClassDefinitions { private var isInitialized = false def isDefinitionsInitialized = isInitialized - // symbols related to packages - var emptypackagescope: Scope = null //debug - - @deprecated("Moved to rootMirror.RootPackage", "2.10.0") - val RootPackage: ModuleSymbol = rootMirror.RootPackage - - @deprecated("Moved to rootMirror.RootClass", "2.10.0") - val RootClass: ClassSymbol = rootMirror.RootClass - - @deprecated("Moved to rootMirror.EmptyPackage", "2.10.0") - val EmptyPackage: ModuleSymbol = rootMirror.EmptyPackage - - @deprecated("Moved to rootMirror.EmptyPackageClass", "2.10.0") - val EmptyPackageClass: ClassSymbol = rootMirror.EmptyPackageClass - // It becomes tricky to create dedicated objects for other symbols because // of initialization order issues. - lazy val JavaLangPackage = getRequiredPackage(sn.JavaLang) + lazy val JavaLangPackage = getPackage("java.lang") lazy val JavaLangPackageClass = JavaLangPackage.moduleClass.asClass - lazy val ScalaPackage = getRequiredPackage(nme.scala_) + lazy val ScalaPackage = getPackage("scala") lazy val ScalaPackageClass = ScalaPackage.moduleClass.asClass - lazy val RuntimePackage = getRequiredPackage("scala.runtime") + lazy val RuntimePackage = getPackage("scala.runtime") lazy val RuntimePackageClass = RuntimePackage.moduleClass.asClass - 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 @@ -224,61 +192,92 @@ trait Definitions extends api.StandardDefinitions { */ def fullyInitializeSymbol(sym: Symbol): Symbol = { sym.initialize + // Watch out for those darn raw types on method parameters + if (sym.owner.initialize.isJavaDefined) + sym.cookJavaRawInfo() + fullyInitializeType(sym.info) - fullyInitializeType(sym.tpe) + fullyInitializeType(sym.tpe_*) sym } def fullyInitializeType(tp: Type): Type = { tp.typeParams foreach fullyInitializeSymbol - tp.paramss.flatten foreach fullyInitializeSymbol + mforeach(tp.paramss)(fullyInitializeSymbol) tp } def fullyInitializeScope(scope: Scope): Scope = { scope.sorted foreach fullyInitializeSymbol scope } + /** Is this symbol a member of Object or Any? */ + def isUniversalMember(sym: Symbol) = ObjectClass isSubClass sym.owner + + /** Is this symbol unimportable? Unimportable symbols include: + * - constructors, because <init> is not a real name + * - private[this] members, which cannot be referenced from anywhere else + * - members of Any or Object, because every instance will inherit a + * definition which supersedes the imported one + */ + def isUnimportable(sym: Symbol) = ( + (sym eq NoSymbol) + || sym.isConstructor + || sym.isPrivateLocal + ) + def isUnimportableUnlessRenamed(sym: Symbol) = isUnimportable(sym) || isUniversalMember(sym) + def isImportable(sym: Symbol) = !isUnimportable(sym) + /** Is this type equivalent to Any, AnyVal, or AnyRef? */ def isTrivialTopType(tp: Type) = ( - tp =:= AnyClass.tpe - || tp =:= AnyValClass.tpe - || tp =:= AnyRefClass.tpe + tp =:= AnyTpe + || tp =:= AnyValTpe + || tp =:= AnyRefTpe ) - /** Does this type have a parent which is none of Any, AnyVal, or AnyRef? */ - def hasNonTrivialParent(tp: Type) = tp.parents exists (t => !isTrivialTopType(tp)) + + def hasMultipleNonImplicitParamLists(member: Symbol): Boolean = hasMultipleNonImplicitParamLists(member.info) + def hasMultipleNonImplicitParamLists(info: Type): Boolean = info match { + case PolyType(_, restpe) => hasMultipleNonImplicitParamLists(restpe) + case MethodType(_, MethodType(p :: _, _)) if !p.isImplicit => true + case _ => false + } 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) + ClassInfoType(AnyTpe :: 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 AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) markAllCompleted + lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe) markAllCompleted lazy val ObjectClass = getRequiredClass(sn.Object.toString) - lazy val AnyTpe = definitions.AnyClass.toTypeConstructor - lazy val AnyRefTpe = definitions.AnyRefClass.toTypeConstructor - lazy val ObjectTpe = definitions.ObjectClass.toTypeConstructor - // 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 + // Cached types for core monomorphic classes + lazy val AnyRefTpe = AnyRefClass.tpe + lazy val AnyTpe = AnyClass.tpe + lazy val AnyValTpe = AnyValClass.tpe + lazy val BoxedUnitTpe = BoxedUnitClass.tpe + lazy val NothingTpe = NothingClass.tpe + lazy val NullTpe = NullClass.tpe + lazy val ObjectTpe = ObjectClass.tpe + lazy val SerializableTpe = SerializableClass.tpe + lazy val StringTpe = StringClass.tpe + lazy val ThrowableTpe = ThrowableClass.tpe + + lazy val ConstantTrue = ConstantType(Constant(true)) + lazy val ConstantFalse = ConstantType(Constant(false)) + lazy val ConstantNull = ConstantType(Constant(null)) lazy val AnyValClass: ClassSymbol = (ScalaPackageClass.info member tpnme.AnyVal orElse { - val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, List(AnyClass.tpe, NotNullClass.tpe), ABSTRACT) + val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, AnyTpe :: Nil, ABSTRACT) val av_constr = anyval.newClassConstructor(NoPosition) anyval.info.decls enter av_constr - anyval + anyval markAllCompleted }).asInstanceOf[ClassSymbol] - lazy val AnyValTpe = definitions.AnyValClass.toTypeConstructor def AnyVal_getClass = getMemberMethod(AnyValClass, nme.getClass_) // bottom types @@ -289,8 +288,10 @@ trait Definitions extends api.StandardDefinitions { locally { this initFlags ABSTRACT | FINAL this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this) + this markAllCompleted } final override def isBottomClass = true + final override def isThreadsafe(purpose: SymbolOps): Boolean = true } final object NothingClass extends BottomClassSymbol(tpnme.Nothing, AnyClass) { override def isSubClass(that: Symbol) = true @@ -301,8 +302,6 @@ trait Definitions extends api.StandardDefinitions { || (that ne NothingClass) && (that isSubClass ObjectClass) ) } - lazy val NothingTpe = definitions.NothingClass.toTypeConstructor - lazy val NullTpe = definitions.NullClass.toTypeConstructor // exceptions and other throwables lazy val ClassCastExceptionClass = requiredClass[ClassCastException] @@ -314,6 +313,8 @@ trait Definitions extends api.StandardDefinitions { lazy val ThrowableClass = getClassByName(sn.Throwable) lazy val UninitializedErrorClass = requiredClass[UninitializedFieldError] + lazy val UninitializedFieldConstructor = UninitializedErrorClass.primaryConstructor + // fundamental reference classes lazy val PartialFunctionClass = requiredClass[PartialFunction[_,_]] lazy val AbstractPartialFunctionClass = requiredClass[scala.runtime.AbstractPartialFunction[_,_]] @@ -336,22 +337,9 @@ trait Definitions extends api.StandardDefinitions { // 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_wrapArray(tp: Type) = getMemberMethod(PredefModule, wrapArrayMethodName(tp)) - 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). - */ + lazy val PredefModule = requiredModule[scala.Predef.type] + def Predef_wrapArray(tp: Type) = getMemberMethod(PredefModule, wrapArrayMethodName(tp)) + def Predef_??? = getMemberMethod(PredefModule, nme.???) def isPredefMemberNamed(sym: Symbol, name: Name) = ( (sym.name == name) && (sym.owner == PredefModule.moduleClass) ) @@ -359,50 +347,30 @@ trait Definitions extends api.StandardDefinitions { /** 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 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) + def Symbol_apply = getMemberMethod(SymbolModule, nme.apply) // 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 SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, AnyTpe :: Nil, ABSTRACT | TRAIT | FINAL) markAllCompleted 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 CloneableClass = requiredClass[scala.Cloneable] lazy val JavaCloneableClass = requiredClass[java.lang.Cloneable] lazy val JavaNumberClass = requiredClass[java.lang.Number] + lazy val JavaEnumClass = requiredClass[java.lang.Enum[_]] 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 ByNameParamClass = specialPolyClass(tpnme.BYNAME_PARAM_CLASS_NAME, COVARIANT)(_ => AnyTpe) 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)) @@ -410,7 +378,8 @@ trait Definitions extends api.StandardDefinitions { def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass def isRepeatedParamType(tp: Type) = isScalaRepeatedParamType(tp) || isJavaRepeatedParamType(tp) - def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe) + def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe_*) + def isByName(param: Symbol) = isByNameParamType(param.tpe_*) def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) @@ -419,52 +388,49 @@ trait Definitions extends api.StandardDefinitions { def isVarArgsList(params: Seq[Symbol]) = params.nonEmpty && isRepeatedParamType(params.last.tpe) def isVarArgTypes(formals: Seq[Type]) = formals.nonEmpty && isRepeatedParamType(formals.last) + def firstParamType(tpe: Type): Type = tpe.paramTypes match { + case p :: _ => p + case _ => NoType + } + def isImplicitParamss(paramss: List[List[Symbol]]) = paramss match { + case (p :: _) :: _ => p.isImplicit + case _ => false + } + def hasRepeatedParam(tp: Type): Boolean = tp match { case MethodType(formals, restpe) => isScalaVarArgs(formals) || hasRepeatedParam(restpe) case PolyType(_, restpe) => hasRepeatedParam(restpe) case _ => false } - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) - case _ => tp - } - - def seqToRepeated(tp: Type): Type = (tp baseType SeqClass) match { - case TypeRef(_, SeqClass, arg :: Nil) => scalaRepeatedType(arg) - case _ => tp - } - - 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") + // wrapping and unwrapping + def dropByName(tp: Type): Type = elementExtract(ByNameParamClass, tp) orElse tp + def dropRepeated(tp: Type): Type = ( + if (isJavaRepeatedParamType(tp)) elementExtract(JavaRepeatedParamClass, tp) orElse tp + else if (isScalaRepeatedParamType(tp)) elementExtract(RepeatedParamClass, tp) orElse tp + else tp + ) + def repeatedToSingle(tp: Type): Type = elementExtract(RepeatedParamClass, tp) orElse elementExtract(JavaRepeatedParamClass, tp) orElse tp + // We don't need to deal with JavaRepeatedParamClass here, as `repeatedToSeq` is only called in the patmat translation for Scala sources. + def repeatedToSeq(tp: Type): Type = elementTransform(RepeatedParamClass, tp)(seqType) orElse tp + def seqToRepeated(tp: Type): Type = elementTransform(SeqClass, tp)(scalaRepeatedType) orElse tp + def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(_ <:< AnyRefTpe) + def isArrayOfSymbol(tp: Type, elem: Symbol) = elementTest(ArrayClass, tp)(_.typeSymbol == elem) + def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp) // 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 IterableClass = requiredClass[scala.collection.Iterable[_]] 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) + def 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] @@ -479,47 +445,44 @@ trait Definitions extends api.StandardDefinitions { // 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_) + // XML + lazy val ScalaXmlTopScope = getModuleIfDefined("scala.xml.TopScope") + lazy val ScalaXmlPackage = getPackageIfDefined("scala.xml") + // scala.reflect lazy val ReflectPackage = requiredModule[scala.reflect.`package`.type] lazy val ReflectApiPackage = getPackageObjectIfDefined("scala.reflect.api") // defined in scala-reflect.jar, so we need to be careful 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 + def ReflectRuntimeUniverse = ReflectRuntimePackage.map(sym => getMemberValue(sym, nme.universe)) + def ReflectRuntimeCurrentMirror = ReflectRuntimePackage.map(sym => getMemberMethod(sym, nme.currentMirror)) + + lazy val UniverseClass = getClassIfDefined("scala.reflect.api.Universe") // defined in scala-reflect.jar, so we need to be careful + def UniverseInternal = getMemberValue(UniverseClass, nme.internal) - lazy val PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest) lazy val PartialManifestModule = requiredModule[scala.reflect.ClassManifestFactory.type] lazy val FullManifestClass = requiredClass[scala.reflect.Manifest[_]] lazy val FullManifestModule = requiredModule[scala.reflect.ManifestFactory.type] lazy val OptManifestClass = requiredClass[scala.reflect.OptManifest[_]] lazy val NoManifest = requiredModule[scala.reflect.NoManifest.type] + lazy val TreesClass = getClassIfDefined("scala.reflect.api.Trees") // defined in scala-reflect.jar, so we need to be careful + 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 + def ExprClass = ExprsClass.map(sym => getMemberClass(sym, tpnme.Expr)) + def ExprSplice = ExprClass.map(sym => getMemberMethod(sym, nme.splice)) + def ExprValue = ExprClass.map(sym => getMemberMethod(sym, nme.value)) lazy val ClassTagModule = requiredModule[scala.reflect.ClassTag[_]] lazy val ClassTagClass = requiredClass[scala.reflect.ClassTag[_]] lazy val TypeTagsClass = getClassIfDefined("scala.reflect.api.TypeTags") // defined in scala-reflect.jar, so we need to be careful - lazy val WeakTypeTagClass = if (TypeTagsClass != NoSymbol) getMemberClass(TypeTagsClass, tpnme.WeakTypeTag) else NoSymbol - lazy val WeakTypeTagModule = if (TypeTagsClass != NoSymbol) getMemberModule(TypeTagsClass, nme.WeakTypeTag) else NoSymbol - lazy val TypeTagClass = if (TypeTagsClass != NoSymbol) getMemberClass(TypeTagsClass, tpnme.TypeTag) else NoSymbol - lazy val TypeTagModule = if (TypeTagsClass != NoSymbol) getMemberModule(TypeTagsClass, nme.TypeTag) else NoSymbol - def materializeClassTag = getMemberMethod(ReflectPackage, nme.materializeClassTag) - def materializeWeakTypeTag = if (ReflectApiPackage != NoSymbol) getMemberMethod(ReflectApiPackage, nme.materializeWeakTypeTag) else NoSymbol - def materializeTypeTag = if (ReflectApiPackage != NoSymbol) getMemberMethod(ReflectApiPackage, nme.materializeTypeTag) else NoSymbol 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 MirrorClass = getClassIfDefined("scala.reflect.api.Mirror") // defined in scala-reflect.jar, so we need to be careful @@ -527,15 +490,26 @@ trait Definitions extends api.StandardDefinitions { lazy val TypeCreatorClass = getClassIfDefined("scala.reflect.api.TypeCreator") // defined in scala-reflect.jar, so we need to be careful lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful - lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.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) getTypeMember(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 - lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl] + private def Context_210 = if (settings.isScala211) NoSymbol else getClassIfDefined("scala.reflect.macros.Context") // needed under -Xsource:2.10 + lazy val BlackboxContextClass = getClassIfDefined("scala.reflect.macros.blackbox.Context").orElse(Context_210) // defined in scala-reflect.jar, so we need to be careful - lazy val StringContextClass = requiredClass[scala.StringContext] - def StringContext_f = getMemberMethod(StringContextClass, nme.f) + lazy val WhiteboxContextClass = getClassIfDefined("scala.reflect.macros.whitebox.Context").orElse(Context_210) // defined in scala-reflect.jar, so we need to be careful + def MacroContextPrefix = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.prefix)) + def MacroContextPrefixType = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.PrefixType)) + def MacroContextUniverse = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.universe)) + def MacroContextExprClass = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.Expr)) + def MacroContextWeakTypeTagClass = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.WeakTypeTag)) + def MacroContextTreeType = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.Tree)) + lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl] + + lazy val StringContextClass = requiredClass[scala.StringContext] + + // SI-8392 a reflection universe on classpath may not have + // quasiquotes, if e.g. crosstyping with -Xsource on + lazy val QuasiquoteClass = if (ApiUniverseClass != NoSymbol) getMemberIfDefined(ApiUniverseClass, tpnme.Quasiquote) else NoSymbol + lazy val QuasiquoteClass_api = if (QuasiquoteClass != NoSymbol) getMember(QuasiquoteClass, tpnme.api) else NoSymbol + lazy val QuasiquoteClass_api_apply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.apply) else NoSymbol + lazy val QuasiquoteClass_api_unapply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.unapply) else NoSymbol lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature] lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature] @@ -543,7 +517,6 @@ trait Definitions extends api.StandardDefinitions { // Option classes lazy val OptionClass: ClassSymbol = requiredClass[Option[_]] lazy val OptionModule: ModuleSymbol = requiredModule[scala.Option.type] - lazy val Option_apply = getMemberMethod(OptionModule, nme.apply) lazy val SomeClass: ClassSymbol = requiredClass[Some[_]] lazy val NoneModule: ModuleSymbol = requiredModule[scala.None.type] lazy val SomeModule: ModuleSymbol = requiredModule[scala.Some.type] @@ -551,10 +524,6 @@ trait Definitions extends api.StandardDefinitions { def compilerTypeFromTag(tt: ApiUniverse # WeakTypeTag[_]): Type = tt.in(rootMirror).tpe def compilerSymbolFromTag(tt: ApiUniverse # WeakTypeTag[_]): 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 @@ -563,37 +532,33 @@ trait Definitions extends api.StandardDefinitions { // 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: _*) + class VarArityClass(name: String, maxArity: Int, countFrom: Int = 0, init: Option[ClassSymbol] = None) extends VarArityClassApi { + private val offset = countFrom - init.size + private def isDefinedAt(i: Int) = i < seq.length + offset && i >= offset + val seq: IndexedSeq[ClassSymbol] = (init ++: countFrom.to(maxArity).map { i => getRequiredClass("scala." + name + i) }).toVector + def apply(i: Int) = if (isDefinedAt(i)) seq(i - offset) else NoSymbol + def specificType(args: List[Type], others: Type*): Type = { + val arity = args.length + if (!isDefinedAt(arity)) NoType + else appliedType(apply(arity), args ++ others: _*) + } } + // would be created synthetically for the default args. We call all objects in this method from the generated code + // in JavaUniverseForce, so it is clearer to define this explicitly define this in source. + object VarArityClass 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) + + lazy val ProductClass = new VarArityClass("Product", MaxProductArity, countFrom = 1, init = Some(UnitClass)) + lazy val TupleClass = new VarArityClass("Tuple", MaxTupleArity, countFrom = 1) + lazy val FunctionClass = new VarArityClass("Function", MaxFunctionArity) + lazy val AbstractFunctionClass = new VarArityClass("runtime.AbstractFunction", MaxFunctionArity) /** 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 tupleType(elems: List[Type]) = TupleClass.specificType(elems) + def functionType(formals: List[Type], restpe: Type) = FunctionClass.specificType(formals, restpe) + def abstractFunctionType(formals: List[Type], restpe: Type) = AbstractFunctionClass.specificType(formals, restpe) def wrapArrayMethodName(elemtp: Type): TermName = elemtp.typeSymbol match { case ByteClass => nme.wrapByteArray @@ -606,17 +571,13 @@ trait Definitions extends api.StandardDefinitions { case BooleanClass => nme.wrapBooleanArray case UnitClass => nme.wrapUnitArray case _ => - if ((elemtp <:< AnyRefClass.tpe) && !isPhantomClass(elemtp.typeSymbol)) nme.wrapRefArray + if ((elemtp <:< AnyRefTpe) && !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 isTupleSymbol(sym: Symbol) = TupleClass.seq contains unspecializedSymbol(sym) + def isFunctionSymbol(sym: Symbol) = FunctionClass.seq contains unspecializedSymbol(sym) + def isProductNSymbol(sym: Symbol) = ProductClass.seq contains unspecializedSymbol(sym) def unspecializedSymbol(sym: Symbol): Symbol = { if (sym hasFlag SPECIALIZED) { @@ -627,31 +588,71 @@ trait Definitions extends api.StandardDefinitions { } else sym } + def unspecializedTypeArgs(tp: Type): List[Type] = + (tp baseType unspecializedSymbol(tp.typeSymbolDirect)).typeArgs + + object MacroContextType { + def unapply(tp: Type) = { + def isOneOfContextTypes(tp: Type) = + tp =:= BlackboxContextClass.tpe || tp =:= WhiteboxContextClass.tpe + def isPrefix(sym: Symbol) = + sym.allOverriddenSymbols.contains(MacroContextPrefixType) + + tp.dealias match { + case RefinedType(List(tp), Scope(sym)) if isOneOfContextTypes(tp) && isPrefix(sym) => Some(tp) + case tp if isOneOfContextTypes(tp) => Some(tp) + case _ => None + } + } + } - // 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)) + def isMacroContextType(tp: Type) = MacroContextType.unapply(tp).isDefined + + def isWhiteboxContextType(tp: Type) = + isMacroContextType(tp) && (tp <:< WhiteboxContextClass.tpe) + + private def macroBundleParamInfo(tp: Type) = { + val ctor = tp.erasure.typeSymbol.primaryConstructor + ctor.paramss match { + case List(List(c)) => + val sym = c.info.typeSymbol + val isContextCompatible = sym.isNonBottomSubClass(BlackboxContextClass) || sym.isNonBottomSubClass(WhiteboxContextClass) + if (isContextCompatible) c.info else NoType case _ => - false - }) + NoType + } } - // 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 looksLikeMacroBundleType(tp: Type) = + macroBundleParamInfo(tp) != NoType + + def isMacroBundleType(tp: Type) = { + val isMonomorphic = tp.typeSymbol.typeParams.isEmpty + val isContextCompatible = isMacroContextType(macroBundleParamInfo(tp)) + val hasSingleConstructor = !tp.declaration(nme.CONSTRUCTOR).isOverloaded + val nonAbstract = !tp.erasure.typeSymbol.isAbstractClass + isMonomorphic && isContextCompatible && hasSingleConstructor && nonAbstract } - def isTupleType(tp: Type) = isTupleTypeDirect(tp.normalize) + + def isBlackboxMacroBundleType(tp: Type) = { + val isBundle = isMacroBundleType(tp) + val unwrappedContext = MacroContextType.unapply(macroBundleParamInfo(tp)).getOrElse(NoType) + val isBlackbox = unwrappedContext =:= BlackboxContextClass.tpe + isBundle && isBlackbox + } + + def isListType(tp: Type) = tp <:< classExistentialType(ListClass) + def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass) + + // These "direct" calls perform no dealiasing. They are most needed when + // printing types when one wants to preserve the true nature of the type. + def isFunctionTypeDirect(tp: Type) = !tp.isHigherKinded && isFunctionSymbol(tp.typeSymbolDirect) + def isTupleTypeDirect(tp: Type) = !tp.isHigherKinded && isTupleSymbol(tp.typeSymbolDirect) + + // Note that these call .dealiasWiden and not .normalize, the latter of which + // tends to change the course of events by forcing types. + def isFunctionType(tp: Type) = isFunctionTypeDirect(tp.dealiasWiden) + def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden) lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product] def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity) @@ -659,43 +660,122 @@ trait Definitions extends api.StandardDefinitions { 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 { + @deprecated("No longer used", "2.11.0") def getProductArgs(tpe: Type): List[Type] = tpe.baseClasses find isProductNSymbol match { case Some(x) => tpe.baseType(x).typeArgs case _ => Nil } + @deprecated("No longer used", "2.11.0") def unapplyUnwrap(tpe:Type) = tpe.finalResultType.dealiasWiden match { + case RefinedType(p :: _, _) => p.dealiasWiden + case tp => tp + } + + def getterMemberTypes(tpe: Type, getters: List[Symbol]): List[Type] = + getters map (m => dropNullaryMethod(tpe memberType m)) + def dropNullaryMethod(tp: Type) = tp match { case NullaryMethodType(restpe) => restpe case _ => tp } - def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match { - case RefinedType(p :: _, _) => p.normalize - case tp => tp + /** An implementation of finalResultType which does only what + * finalResultType is documented to do. Defining it externally to + * Type helps ensure people can't come to depend on accidental + * aspects of its behavior. This is all of it! + */ + def finalResultType(tp: Type): Type = tp match { + case PolyType(_, restpe) => finalResultType(restpe) + case MethodType(_, restpe) => finalResultType(restpe) + case NullaryMethodType(restpe) => finalResultType(restpe) + case _ => tp + } + /** Similarly, putting all the isStable logic in one place. + * This makes it like 1000x easier to see the overall logic + * of the method. + */ + def isStable(tp: Type): Boolean = tp match { + case _: SingletonType => true + case NoPrefix => true + case TypeRef(_, NothingClass | SingletonClass, _) => true + case TypeRef(_, sym, _) if sym.isAbstractType => tp.bounds.hi.typeSymbol isSubClass SingletonClass + case TypeRef(pre, sym, _) if sym.isModuleClass => isStable(pre) + case TypeRef(_, _, _) if tp ne tp.dealias => isStable(tp.dealias) + case TypeVar(origin, _) => isStable(origin) + case AnnotatedType(_, atp) => isStable(atp) // Really? + case _: SimpleTypeProxy => isStable(tp.underlying) + case _ => false } + def isVolatile(tp: Type): Boolean = { + // 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 + def isVolatileAbstractType: Boolean = { + def sym = tp.typeSymbol + def volatileUpperBound = isVolatile(tp.bounds.hi) + def safeIsVolatile = ( + if (volatileRecursions < TypeConstants.LogVolatileThreshold) + volatileUpperBound + // we can return true when pendingVolatiles contains sym, because + // a cycle will be detected afterwards and an error will result anyway. + else pendingVolatiles(sym) || { + pendingVolatiles += sym + try volatileUpperBound finally pendingVolatiles -= sym + } + ) + volatileRecursions += 1 + try safeIsVolatile finally volatileRecursions -= 1 + } + /** 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. + */ + def isVolatileRefinedType: Boolean = { + val RefinedType(parents, decls) = tp + def isVisibleDeferred(m: Symbol) = m.isDeferred && ((tp nonPrivateMember m.name).alternatives contains m) + def contributesAbstractMembers(p: Type) = p.deferredMembers exists isVisibleDeferred + def dropConcreteParents = parents dropWhile (p => !p.typeSymbol.isAbstractType) + + (parents exists isVolatile) || { + dropConcreteParents match { + case Nil => false + case ps => (ps ne parents) || (ps.tail exists contributesAbstractMembers) || (decls exists isVisibleDeferred) + } + } + } - def functionApply(n: Int) = getMemberMethod(FunctionClass(n), nme.apply) + tp match { + case ThisType(_) => false + case SingleType(_, sym) => isVolatile(tp.underlying) && (sym.hasVolatileType || !sym.isStable) + case NullaryMethodType(restpe) => isVolatile(restpe) + case PolyType(_, restpe) => isVolatile(restpe) + case TypeRef(_, _, _) if tp ne tp.dealias => isVolatile(tp.dealias) + case TypeRef(_, sym, _) if sym.isAbstractType => isVolatileAbstractType + case RefinedType(_, _) => isVolatileRefinedType + case TypeVar(origin, _) => isVolatile(origin) + case _: SimpleTypeProxy => isVolatile(tp.underlying) + case _ => false + } + } + private[this] var volatileRecursions: Int = 0 + private[this] val pendingVolatiles = mutable.HashSet[Symbol]() def abstractFunctionForFunctionType(tp: Type) = { assert(isFunctionType(tp), tp) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) } - - 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 functionNBaseType(tp: Type): Type = tp.baseClasses find isFunctionSymbol match { + case Some(sym) => tp baseType unspecializedSymbol(sym) + case _ => tp } def isPartialFunctionType(tp: Type): Boolean = { @@ -703,11 +783,42 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } - def isSeqType(tp: Type) = elementType(SeqClass, tp.normalize) != NoType + /** The single abstract method declared by type `tp` (or `NoSymbol` if it cannot be found). + * + * The method must be monomorphic and have exactly one parameter list. + * The class defining the method is a supertype of `tp` that + * has a public no-arg primary constructor. + */ + def samOf(tp: Type): Symbol = { + // if tp has a constructor, it must be public and must not take any arguments + // (not even an implicit argument list -- to keep it simple for now) + val tpSym = tp.typeSymbol + val ctor = tpSym.primaryConstructor + val ctorOk = !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1) + + if (tpSym.exists && ctorOk) { + // find the single abstract member, if there is one + // don't go out requiring DEFERRED members, as you will get them even if there's a concrete override: + // scala> abstract class X { def m: Int } + // scala> class Y extends X { def m: Int = 1} + // scala> typeOf[Y].deferredMembers + // Scopes(method m, method getClass) + // + // scala> typeOf[Y].members.filter(_.isDeferred) + // Scopes() + // must filter out "universal" members (getClass is deferred for some reason) + val deferredMembers = ( + tp membersBasedOnFlags (excludedFlags = BridgeAndPrivateFlags, requiredFlags = METHOD) + filter (mem => mem.isDeferredNotDefault && !isUniversalMember(mem)) // TODO: test + ) - def elementType(container: Symbol, tp: Type): Type = tp match { - case TypeRef(_, `container`, arg :: Nil) => arg - case _ => NoType + // if there is only one, it's monomorphic and has a single argument list + if (deferredMembers.size == 1 && + deferredMembers.head.typeParams.isEmpty && + deferredMembers.head.info.paramSectionCount == 1) + deferredMembers.head + else NoSymbol + } else NoSymbol } def arrayType(arg: Type) = appliedType(ArrayClass, arg) @@ -717,14 +828,75 @@ trait Definitions extends api.StandardDefinitions { 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) + // FYI the long clunky name is because it's really hard to put "get" into the + // name of a method without it sounding like the method "get"s something, whereas + // this method is about a type member which just happens to be named get. + def typeOfMemberNamedGet(tp: Type) = typeArgOfBaseTypeOr(tp, OptionClass)(resultOfMatchingMethod(tp, nme.get)()) + def typeOfMemberNamedHead(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.head)()) + def typeOfMemberNamedApply(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.apply)(IntTpe)) + def typeOfMemberNamedDrop(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.drop)(IntTpe)) + def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp)) + // SI-8128 Still using the type argument of the base type at Seq/Option if this is an old-style (2.10 compatible) + // extractor to limit exposure to regressions like the reported problem with existentials. + // TODO fix the existential problem in the general case, see test/pending/pos/t8128.scala + private def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp baseType baseClass).typeArgs match { + case x :: Nil => x + case _ => or + } - def ClassType(arg: Type) = - if (phase.erasedTypes || forMSIL) ClassClass.tpe - else appliedType(ClassClass, arg) + // Can't only check for _1 thanks to pos/t796. + def hasSelectors(tp: Type) = ( + (tp.members containsName nme._1) + && (tp.members containsName nme._2) + ) + + /** Returns the method symbols for members _1, _2, ..., _N + * which exist in the given type. + */ + def productSelectors(tpe: Type): List[Symbol] = { + def loop(n: Int): List[Symbol] = tpe member TermName("_" + n) match { + case NoSymbol => Nil + case m if m.paramss.nonEmpty => Nil + case m => m :: loop(n + 1) + } + // Since ErrorType always returns a symbol from a call to member, we + // had better not start looking for _1, _2, etc. expecting it to run out. + if (tpe.isErroneous) Nil else loop(1) + } + + /** If `tp` has a term member `name`, the first parameter list of which + * matches `paramTypes`, and which either has no further parameter + * lists or only an implicit one, then the result type of the matching + * method. Otherwise, NoType. + */ + def resultOfMatchingMethod(tp: Type, name: TermName)(paramTypes: Type*): Type = { + def matchesParams(member: Symbol) = member.paramss match { + case Nil => paramTypes.isEmpty + case ps :: rest => (rest.isEmpty || isImplicitParamss(rest)) && (ps corresponds paramTypes)(_.tpe =:= _) + } + tp member name filter matchesParams match { + case NoSymbol => NoType + case member => (tp memberType member).finalResultType + } + } + + def ClassType(arg: Type) = if (phase.erasedTypes) ClassClass.tpe else appliedType(ClassClass, arg) + + /** Can we tell by inspecting the symbol that it will never + * at any phase have type parameters? + */ + def neverHasTypeParameters(sym: Symbol) = sym match { + case _: RefinementClassSymbol => true + case _: ModuleClassSymbol => true + case _: ImplClassSymbol => true + case _ => + ( + sym.isPrimitiveValueClass + || sym.isAnonymousClass + || sym.initialize.isMonomorphicType + ) + } def EnumType(sym: Symbol) = // given (in java): "class A { enum E { VAL1 } }" @@ -733,66 +905,34 @@ trait Definitions extends api.StandardDefinitions { // - .linkedClassOfClass: the ClassSymbol of the enumeration (class E) sym.owner.linkedClassOfClass.tpe - 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 }. */ + // TODO Review the way this is used. I see two potential problems: + // 1. `existentialAbstraction` here doesn't create fresh existential type symbols, it just + // uses the class type parameter symbols directly as the list of quantified symbols. + // See SI-8244 for the trouble that this can cause. + // Compare with callers of `typeParamsToExistentials` (used in Java raw type handling) + // 2. Why don't we require a prefix? Could its omission lead to wrong results in CheckabilityChecker? 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) + existentialAbstraction(clazz.typeParams, clazz.tpe_*) - // - // .NET backend - // + // members of class scala.Any - 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 - }) - } + // TODO these aren't final! They are now overriden in AnyRef/Object. Prior to the fix + // for SI-8129, they were actually *overloaded* by the members in AnyRef/Object. + // We should unfinalize these, override in AnyValClass, and make the overrides final. + // Refchecks never actually looks at these, so its just for consistency. + lazy val Any_== = enterNewMethod(AnyClass, nme.EQ, AnyTpe :: Nil, BooleanTpe, FINAL) + lazy val Any_!= = enterNewMethod(AnyClass, nme.NE, AnyTpe :: Nil, BooleanTpe, FINAL) - // 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) + lazy val Any_equals = enterNewMethod(AnyClass, nme.equals_, AnyTpe :: Nil, BooleanTpe) + lazy val Any_hashCode = enterNewMethod(AnyClass, nme.hashCode_, Nil, IntTpe) + lazy val Any_toString = enterNewMethod(AnyClass, nme.toString_, Nil, StringTpe) + lazy val Any_## = enterNewMethod(AnyClass, nme.HASHHASH, Nil, IntTpe, 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: @@ -804,7 +944,7 @@ trait Definitions extends api.StandardDefinitions { // 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_isInstanceOf = newT1NullaryMethod(AnyClass, nme.isInstanceOf_, FINAL)(_ => BooleanTpe) lazy val Any_asInstanceOf = newT1NullaryMethod(AnyClass, nme.asInstanceOf_, FINAL)(_.typeConstructor) lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( @@ -834,7 +974,7 @@ trait Definitions extends api.StandardDefinitions { else { val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) val upperBound = ( - if (isPhantomClass(sym)) AnyClass.tpe + if (isPhantomClass(sym)) AnyTpe else if (sym.isLocalClass) erasure.intersectionDominator(tp.parents) else tp.widen ) @@ -860,12 +1000,7 @@ trait Definitions extends api.StandardDefinitions { 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 @@ -893,27 +1028,23 @@ trait Definitions extends api.StandardDefinitions { 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_## = enterNewMethod(ObjectClass, nme.HASHHASH, Nil, IntTpe, FINAL) + lazy val Object_== = enterNewMethod(ObjectClass, nme.EQ, AnyTpe :: Nil, BooleanTpe, FINAL) + lazy val Object_!= = enterNewMethod(ObjectClass, nme.NE, AnyTpe :: Nil, BooleanTpe, FINAL) + lazy val Object_eq = enterNewMethod(ObjectClass, nme.eq, AnyRefTpe :: Nil, BooleanTpe, FINAL) + lazy val Object_ne = enterNewMethod(ObjectClass, nme.ne, AnyRefTpe :: Nil, BooleanTpe, FINAL) + lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_ => BooleanTpe) + lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC | ARTIFACT)(_.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) + lazy val String_+ = enterNewMethod(StringClass, nme.raw.PLUS, AnyTpe :: Nil, StringTpe, FINAL) def Object_getClass = getMemberMethod(ObjectClass, nme.getClass_) def Object_clone = getMemberMethod(ObjectClass, nme.clone_) @@ -940,9 +1071,6 @@ trait Definitions extends api.StandardDefinitions { 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) @@ -959,7 +1087,6 @@ trait Definitions extends api.StandardDefinitions { 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] @@ -968,8 +1095,7 @@ trait Definitions extends api.StandardDefinitions { lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty] lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty] - lazy val CloneableAttr = requiredClass[scala.annotation.cloneable] - lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.internal.annotations.compileTimeOnly") + lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.annotation.compileTimeOnly") lazy val DeprecatedAttr = requiredClass[scala.deprecated] lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance] @@ -979,6 +1105,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ScalaInlineClass = requiredClass[scala.inline] lazy val ScalaNoInlineClass = requiredClass[scala.noinline] lazy val SerialVersionUIDAttr = requiredClass[scala.SerialVersionUID] + lazy val SerialVersionUIDAnnotation = AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List()) lazy val SpecializedClass = requiredClass[scala.specialized] lazy val ThrowsClass = requiredClass[scala.throws[_]] lazy val TransientAttr = requiredClass[scala.transient] @@ -994,53 +1121,56 @@ trait Definitions extends api.StandardDefinitions { 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 ClassTargetClass = requiredClass[meta.companionClass] 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 metaAnnotations: Set[Symbol] = getPackage("scala.annotation.meta").info.members filter (_ isSubClass StaticAnnotationClass) toSet + + // According to the scala.annotation.meta package object: + // * By default, annotations on (`val`-, `var`- or plain) constructor parameters + // * end up on the parameter, not on any other entity. Annotations on fields + // * by default only end up on the field. + def defaultAnnotationTarget(t: Tree): Symbol = t match { + case ClassDef(_, _, _, _) => ClassTargetClass + case ModuleDef(_, _, _) => ObjectTargetClass + case vd @ ValDef(_, _, _, _) if vd.symbol.isParamAccessor => ParamTargetClass + case vd @ ValDef(_, _, _, _) if vd.symbol.isValueParameter => ParamTargetClass + case ValDef(_, _, _, _) => FieldTargetClass + case DefDef(_, _, _, _, _, _) => MethodTargetClass + case _ => GetterTargetClass + } 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 + val sym = RuntimePackageClass.newClassSymbol(tpnme.AnnotationDefaultATTR, NoPosition, 0L) + sym setInfo ClassInfoType(List(AnnotationClass.tpe), newScope, sym) + markAllCompleted(sym) + RuntimePackageClass.info.decls.toList.filter(_.name == sym.name) match { + case existing :: _ => + existing.asInstanceOf[ClassSymbol] + case _ => + RuntimePackageClass.info.decls enter sym + // This attribute needs a constructor so that modifiers in parsed Java code make sense + sym.info.decls enter sym.newClassConstructor(NoPosition) + sym + } } - @deprecated("Moved to rootMirror.getClass", "2.10.0") - def getClass(fullname: Name): ClassSymbol = rootMirror.getClassByName(fullname) - - @deprecated("Moved to rootMirror.getModule", "2.10.0") - def getModule(fullname: Name): ModuleSymbol = rootMirror.getModule(fullname) - - private def fatalMissingSymbol(owner: Symbol, name: Name, what: String = "member") = { - throw new FatalError(owner + " does not have a " + what + " " + name) + private def fatalMissingSymbol(owner: Symbol, name: Name, what: String = "member", addendum: String = "") = { + throw new FatalError(owner + " does not have a " + what + " " + name + addendum) } def getLanguageFeature(name: String, owner: Symbol = languageFeatureModule): Symbol = 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) @@ -1070,7 +1200,8 @@ trait Definitions extends api.StandardDefinitions { def getMemberModule(owner: Symbol, name: Name): ModuleSymbol = { getMember(owner, name.toTermName) match { case x: ModuleSymbol => x - case _ => fatalMissingSymbol(owner, name, "member object") + case NoSymbol => fatalMissingSymbol(owner, name, "member object") + case other => fatalMissingSymbol(owner, name, "member object", addendum = s". A symbol ${other} of kind ${other.accurateKindString} already exists.") } } def getTypeMember(owner: Symbol, name: Name): TypeSymbol = { @@ -1080,7 +1211,6 @@ trait Definitions extends api.StandardDefinitions { } } def getMemberClass(owner: Symbol, name: Name): ClassSymbol = { - val y = getMember(owner, name.toTypeName) getMember(owner, name.toTypeName) match { case x: ClassSymbol => x case _ => fatalMissingSymbol(owner, name, "member class") @@ -1088,15 +1218,21 @@ trait Definitions extends api.StandardDefinitions { } def getMemberMethod(owner: Symbol, name: Name): TermSymbol = { getMember(owner, name.toTermName) match { - // todo. member symbol becomes a term symbol in cleanup. is this a bug? - // case x: MethodSymbol => x case x: TermSymbol => x case _ => fatalMissingSymbol(owner, name, "method") } } + private lazy val erasurePhase = findPhaseWithName("erasure") def getMemberIfDefined(owner: Symbol, name: Name): Symbol = - owner.info.nonPrivateMember(name) + // findMember considered harmful after erasure; e.g. + // + // scala> exitingErasure(Symbol_apply).isOverloaded + // res27: Boolean = true + // + enteringPhaseNotLaterThan(erasurePhase )( + owner.info.nonPrivateMember(name) + ) /** Using getDecl rather than getMember may avoid issues with * OverloadedTypes turning up when you don't want them, if you @@ -1108,18 +1244,15 @@ trait Definitions extends api.StandardDefinitions { 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)) + val parents = List(AnyRefTpe, parentFn(tparam)) - clazz setInfo GenPolyType(List(tparam), ClassInfoType(parents, newScope, clazz)) + clazz setInfo GenPolyType(List(tparam), ClassInfoType(parents, newScope, clazz)) markAllCompleted } def newPolyMethod(typeParamCount: Int, owner: Symbol, name: TermName, flags: Long)(createFn: PolyMethodCreator): MethodSymbol = { @@ -1130,7 +1263,7 @@ trait Definitions extends api.StandardDefinitions { case (_, restpe) => NullaryMethodType(restpe) } - msym setInfoAndEnter genPolyType(tparams, mtpe) + msym setInfoAndEnter genPolyType(tparams, mtpe) markAllCompleted } /** T1 means one type parameter. @@ -1142,10 +1275,6 @@ trait Definitions extends api.StandardDefinitions { 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) /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ @@ -1159,8 +1288,7 @@ trait Definitions extends api.StandardDefinitions { AnyValClass, NullClass, NothingClass, - SingletonClass, - EqualsPatternClass + SingletonClass ) /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticCoreMethods = List( @@ -1196,14 +1324,12 @@ trait Definitions extends api.StandardDefinitions { lazy val symbolsNotPresentInBytecode = syntheticCoreClasses ++ syntheticCoreMethods ++ hijackedCoreClasses /** Is the symbol that of a parent which is added during parsing? */ - lazy val isPossibleSyntheticParent = ProductClass.toSet[Symbol] + ProductRootClass + SerializableClass + lazy val isPossibleSyntheticParent = ProductClass.seq.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? */ @@ -1220,7 +1346,7 @@ trait Definitions extends api.StandardDefinitions { /** Is type's symbol a numeric value class? */ def isNumericValueType(tp: Type): Boolean = tp match { case TypeRef(_, sym, _) => isNumericValueClass(sym) - case _ => false + case _ => false } // todo: reconcile with javaSignature!!! @@ -1232,10 +1358,10 @@ trait Definitions extends api.StandardDefinitions { } 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 if (sym.isTopLevel) 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)) + if (etp.typeSymbol == ArrayClass) "[" + signature1(erasure(etp.dealiasWiden.typeArgs.head)) else if (isPrimitiveValueClass(etp.typeSymbol)) abbrvTag(etp.typeSymbol).toString() else "L" + flatNameString(etp.typeSymbol, '/') + ";" } @@ -1244,49 +1370,127 @@ trait Definitions extends api.StandardDefinitions { 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 - } - + // documented in JavaUniverse.init def init() { if (isInitialized) return - // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = symbolsNotPresentInBytecode + ObjectClass.initialize + ScalaPackageClass.initialize + val forced1 = symbolsNotPresentInBytecode + val forced2 = NoSymbol 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 + class UniverseDependentTypes(universe: Tree) { + lazy val nameType = universeMemberType(tpnme.Name) + lazy val modsType = universeMemberType(tpnme.Modifiers) + lazy val flagsType = universeMemberType(tpnme.FlagSet) + lazy val symbolType = universeMemberType(tpnme.Symbol) + lazy val treeType = universeMemberType(tpnme.Tree) + lazy val caseDefType = universeMemberType(tpnme.CaseDef) + lazy val liftableType = universeMemberType(tpnme.Liftable) + lazy val unliftableType = universeMemberType(tpnme.Unliftable) + lazy val iterableTreeType = appliedType(IterableClass, treeType) + lazy val listTreeType = appliedType(ListClass, treeType) + lazy val listListTreeType = appliedType(ListClass, listTreeType) + + def universeMemberType(name: TypeName) = universe.tpe.memberType(getTypeMember(universe.symbol, name)) } - // def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol, delType: Type) { - // assert(Delegate_scalaCallers contains scalaCaller) - // Delegate_scalaCallerInfos += (scalaCaller -> (methSym, delType)) - // } + /** Efficient access to member symbols which must be looked up each run. Access via `currentRun.runDefinitions` */ + final class RunDefinitions { + lazy val StringAdd_+ = getMemberMethod(StringAddClass, nme.PLUS) + + // The given symbol represents either String.+ or StringAdd.+ + def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+ + + lazy val StringContext_f = getMemberMethod(StringContextClass, nme.f) + + lazy val ArrowAssocClass = getMemberClass(PredefModule, TypeName("ArrowAssoc")) // SI-5731 + def isArrowAssoc(sym: Symbol) = sym.owner == ArrowAssocClass + + lazy val Boxes_isNumberOrBool = getDecl(BoxesRunTimeClass, nme.isBoxedNumberOrBoolean) + lazy val Boxes_isNumber = getDecl(BoxesRunTimeClass, nme.isBoxedNumber) - def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol) { - assert(Delegate_scalaCallers contains scalaCaller) - Delegate_scalaCallerTargets += (scalaCaller -> methSym) + 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) + + lazy val boxMethod = classesMap(x => valueCompanionMember(x, nme.box)) + lazy val unboxMethod = classesMap(x => valueCompanionMember(x, nme.unbox)) + lazy val isUnbox = unboxMethod.values.toSet[Symbol] + lazy val isBox = boxMethod.values.toSet[Symbol] + + lazy val Option_apply = getMemberMethod(OptionModule, nme.apply) + lazy val List_apply = DefinitionsClass.this.List_apply + + /** + * Is the given symbol `List.apply`? + * To to avoid bootstrapping cycles, this return false if the given symbol or List itself is not initialized. + */ + def isListApply(sym: Symbol) = sym.isInitialized && ListModule.hasCompleteInfo && sym == List_apply + def isPredefClassOf(sym: Symbol) = if (PredefModule.hasCompleteInfo) sym == Predef_classOf else isPredefMemberNamed(sym, nme.classOf) + + lazy val TagMaterializers = Map[Symbol, Symbol]( + ClassTagClass -> materializeClassTag, + WeakTypeTagClass -> materializeWeakTypeTag, + TypeTagClass -> materializeTypeTag + ) + lazy val TagSymbols = TagMaterializers.keySet + lazy val Predef_conforms = (getMemberIfDefined(PredefModule, nme.conforms) + orElse getMemberMethod(PredefModule, "conforms": TermName)) // TODO: predicate on -Xsource:2.10 (for now, needed for transition from M8 -> RC1) + lazy val Predef_classOf = getMemberMethod(PredefModule, nme.classOf) + lazy val Predef_implicitly = getMemberMethod(PredefModule, nme.implicitly) + lazy val Predef_wrapRefArray = getMemberMethod(PredefModule, nme.wrapRefArray) + lazy val Predef_??? = DefinitionsClass.this.Predef_??? + + lazy val arrayApplyMethod = getMemberMethod(ScalaRunTimeModule, nme.array_apply) + lazy val arrayUpdateMethod = getMemberMethod(ScalaRunTimeModule, nme.array_update) + lazy val arrayLengthMethod = getMemberMethod(ScalaRunTimeModule, nme.array_length) + lazy val arrayCloneMethod = getMemberMethod(ScalaRunTimeModule, nme.array_clone) + lazy val ensureAccessibleMethod = getMemberMethod(ScalaRunTimeModule, nme.ensureAccessible) + lazy val arrayClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayClass) + lazy val traversableDropMethod = getMemberMethod(ScalaRunTimeModule, nme.drop) + + lazy val GroupOfSpecializable = getMemberClass(SpecializableModule, tpnme.Group) + + lazy val WeakTypeTagClass = TypeTagsClass.map(sym => getMemberClass(sym, tpnme.WeakTypeTag)) + lazy val WeakTypeTagModule = TypeTagsClass.map(sym => getMemberModule(sym, nme.WeakTypeTag)) + lazy val TypeTagClass = TypeTagsClass.map(sym => getMemberClass(sym, tpnme.TypeTag)) + lazy val TypeTagModule = TypeTagsClass.map(sym => getMemberModule(sym, nme.TypeTag)) + lazy val MacroContextUniverse = DefinitionsClass.this.MacroContextUniverse + + lazy val materializeClassTag = getMemberMethod(ReflectPackage, nme.materializeClassTag) + lazy val materializeWeakTypeTag = ReflectApiPackage.map(sym => getMemberMethod(sym, nme.materializeWeakTypeTag)) + lazy val materializeTypeTag = ReflectApiPackage.map(sym => getMemberMethod(sym, nme.materializeTypeTag)) + + 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") + + lazy val ApiUniverseReify = ApiUniverseClass.map(sym => getMemberMethod(sym, nme.reify)) + + lazy val ReflectRuntimeUniverse = DefinitionsClass.this.ReflectRuntimeUniverse + lazy val ReflectRuntimeCurrentMirror = DefinitionsClass.this.ReflectRuntimeCurrentMirror + + lazy val TreesTreeType = TreesClass.map(sym => getTypeMember(sym, tpnme.Tree)) + object TreeType { def unapply(tpe: Type): Boolean = tpe.typeSymbol.overrideChain contains TreesTreeType } + object SubtreeType { def unapply(tpe: Type): Boolean = tpe.typeSymbol.overrideChain exists (_.tpe <:< TreesTreeType.tpe) } + + object ExprClassOf { def unapply(tp: Type): Option[Type] = elementExtractOption(ExprClass, tp) } + + lazy val PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest) + lazy val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass) } } } diff --git a/src/reflect/scala/reflect/internal/Depth.scala b/src/reflect/scala/reflect/internal/Depth.scala new file mode 100644 index 0000000000..357abf765f --- /dev/null +++ b/src/reflect/scala/reflect/internal/Depth.scala @@ -0,0 +1,28 @@ +package scala +package reflect +package internal + +import Depth._ + +final class Depth private (val depth: Int) extends AnyVal with Ordered[Depth] { + def max(that: Depth): Depth = if (this < that) that else this + def decr(n: Int): Depth = if (isAnyDepth) this else Depth(depth - n) + def incr(n: Int): Depth = if (isAnyDepth) this else Depth(depth + n) + def decr: Depth = decr(1) + def incr: Depth = incr(1) + + def isNegative = depth < 0 + def isZero = depth == 0 + def isAnyDepth = this == AnyDepth + + def compare(that: Depth): Int = if (depth < that.depth) -1 else if (this == that) 0 else 1 + override def toString = s"Depth($depth)" +} + +object Depth { + // A don't care value for the depth parameter in lubs/glbs and related operations. + final val AnyDepth = new Depth(Int.MinValue) + final val Zero = new Depth(0) + + @inline final def apply(depth: Int): Depth = if (depth < 0) AnyDepth else new Depth(depth) +} diff --git a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala index 2c2ed351c9..0eeca4aace 100644 --- a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala +++ b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -3,11 +3,11 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package 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. @@ -84,14 +84,14 @@ trait ExistentialsAndSkolems { * also replaced, except for term symbols of an Ident tree, where * only the type of the Ident is changed. */ - final def existentialTransform[T](rawSyms: List[Symbol], tp: Type, rawOwner: Option[Symbol] = None)(creator: (List[Symbol], Type) => T): T = { + final def existentialTransform[T](rawSyms: List[Symbol], tp: Type, rawOwner: Symbol = NoSymbol)(creator: (List[Symbol], Type) => T): T = { val allBounds = existentialBoundsExcludingHidden(rawSyms) val typeParams: List[Symbol] = rawSyms map { sym => val name = sym.name match { case x: TypeName => x case x => tpnme.singletonName(x) } - def rawOwner0 = rawOwner.getOrElse(abort(s"no owner provided for existential transform over raw parameter: $sym")) + def rawOwner0 = rawOwner orElse abort(s"no owner provided for existential transform over raw parameter: $sym") val bound = allBounds(sym) val sowner = if (isRawParameter(sym)) rawOwner0 else sym.owner val quantified = sowner.newExistential(name, sym.pos) @@ -113,7 +113,7 @@ trait ExistentialsAndSkolems { * @param hidden The original type * @param rawOwner The owner for Java raw types. */ - final def packSymbols(hidden: List[Symbol], tp: Type, rawOwner: Option[Symbol] = None): Type = + final def packSymbols(hidden: List[Symbol], tp: Type, rawOwner: Symbol = NoSymbol): Type = if (hidden.isEmpty) tp else existentialTransform(hidden, tp, rawOwner)(existentialAbstraction) } diff --git a/src/reflect/scala/reflect/internal/FatalError.scala b/src/reflect/scala/reflect/internal/FatalError.scala index a084fc24f3..08a9a635af 100644 --- a/src/reflect/scala/reflect/internal/FatalError.scala +++ b/src/reflect/scala/reflect/internal/FatalError.scala @@ -2,5 +2,6 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect.internal +package scala +package 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 index 6a3b6870a0..ef9c77878f 100644 --- a/src/reflect/scala/reflect/internal/FlagSets.scala +++ b/src/reflect/scala/reflect/internal/FlagSets.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal import scala.language.implicitConversions @@ -41,5 +42,11 @@ trait FlagSets extends api.FlagSets { self: SymbolTable => val DEFAULTPARAM : FlagSet = Flags.DEFAULTPARAM val PRESUPER : FlagSet = Flags.PRESUPER val DEFAULTINIT : FlagSet = Flags.DEFAULTINIT + val ENUM : FlagSet = Flags.ENUM + val PARAMACCESSOR : FlagSet = Flags.PARAMACCESSOR + val CASEACCESSOR : FlagSet = Flags.CASEACCESSOR + val SYNTHETIC : FlagSet = Flags.SYNTHETIC + val ARTIFACT : FlagSet = Flags.ARTIFACT + val STABLE : FlagSet = Flags.STABLE } } diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 5ebe02d95d..1707061817 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.collection.{ mutable, immutable } @@ -62,7 +63,7 @@ import scala.collection.{ mutable, immutable } // 45: SYNCHRONIZED/M // 46: ARTIFACT // 47: DEFAULTMETHOD/M -// 48: +// 48: ENUM // 49: // 50: // 51: lateDEFERRED @@ -116,8 +117,10 @@ class ModifierFlags { 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 - // ARTIFACT at #46 in 2.11+ + final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode + // to see which symbols are marked as ARTIFACT, see scaladocs for FlagValues.ARTIFACT final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method + final val ENUM = 1L << 48 // symbol is an enum // Overridden. def flagToString(flag: Long): String = "" @@ -128,7 +131,7 @@ class ModifierFlags { } object ModifierFlags extends ModifierFlags -/** All flags and associated operatins */ +/** All flags and associated operations */ class Flags extends ModifierFlags { final val METHOD = 1 << 6 // a method final val MODULE = 1 << 8 // symbol is module or class implementing a module @@ -163,11 +166,10 @@ class Flags extends ModifierFlags { 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 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 - final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode // ------- shift definitions ------------------------------------------------------- @@ -250,7 +252,7 @@ class Flags extends ModifierFlags { /** These modifiers appear in TreePrinter output. */ final val PrintableFlags = ExplicitFlags | BridgeFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR | MACRO | - ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED + ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | STATIC | SPECIALIZED | SYNCHRONIZED | ARTIFACT /** When a symbol for a field is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -266,11 +268,13 @@ class Flags extends ModifierFlags { final val GetterFlags = ~(PRESUPER | MUTABLE) final val SetterFlags = ~(PRESUPER | MUTABLE | STABLE | CASEACCESSOR | IMPLICIT) - /** 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. + /** Since DEFAULTPARAM is overloaded with TRAIT, we need some additional + * means of determining what that bit means. Usually DEFAULTPARAM is coupled + * with PARAM, which suffices. Default getters get METHOD instead. + * This constant is the mask of flags which can survive from the parameter modifiers. + * See paramFlagsToDefaultGetter for the full logic. */ - final val DefaultGetterFlags = PRIVATE | PROTECTED | FINAL + final val DefaultGetterFlags = PRIVATE | PROTECTED | FINAL | PARAMACCESSOR /** When a symbol for a method parameter is created, only these flags survive * from Modifiers. Others which may be applied at creation time are: @@ -296,7 +300,11 @@ class Flags extends ModifierFlags { assert((OverloadedFlagsMask & FlagsNotPickled) == 0, flagsToString(OverloadedFlagsMask & FlagsNotPickled)) /** These flags are pickled */ - final val PickledFlags = InitialFlags & ~FlagsNotPickled + final val PickledFlags = ( + (InitialFlags & ~FlagsNotPickled) + | notPRIVATE // for value class constructors (SI-6601), and private members referenced + // in @inline-marked methods publicized in SuperAccessors (see SI-6608, e6b4204604) + ) /** If we have a top-level class or module * and someone asks us for a flag not in TopLevelPickledFlags, @@ -304,6 +312,9 @@ class Flags extends ModifierFlags { */ final val TopLevelPickledFlags = PickledFlags & ~(MODULE | METHOD | PACKAGE | PARAM | EXISTENTIAL) + def paramFlagsToDefaultGetter(paramFlags: Long): Long = + (paramFlags & DefaultGetterFlags) | SYNTHETIC | METHOD | DEFAULTPARAM + def getterFlags(fieldFlags: Long): Long = ACCESSOR + ( if ((fieldFlags & MUTABLE) != 0) fieldFlags & ~MUTABLE & ~PRESUPER else fieldFlags & ~PRESUPER | STABLE @@ -422,9 +433,9 @@ class Flags extends ModifierFlags { case VARARGS => "<varargs>" // (1L << 43) case TRIEDCOOKING => "<triedcooking>" // (1L << 44) case SYNCHRONIZED => "<synchronized>" // (1L << 45) - case 0x400000000000L => "" // (1L << 46) + case ARTIFACT => "<artifact>" // (1L << 46) case DEFAULTMETHOD => "<defaultmethod>" // (1L << 47) - case 0x1000000000000L => "" // (1L << 48) + case ENUM => "<enum>" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) case 0x4000000000000L => "" // (1L << 50) case `lateDEFERRED` => "<latedeferred>" // (1L << 51) @@ -456,7 +467,7 @@ class Flags extends ModifierFlags { ) @deprecated("Use flagString on the flag-carrying member", "2.10.0") - def flagsToString(flags: Long, privateWithin: String): String = { + private[scala] def flagsToString(flags: Long, privateWithin: String): String = { val access = accessString(flags, privateWithin) val nonAccess = flagsToString(flags & ~AccessFlags) @@ -464,7 +475,7 @@ class Flags extends ModifierFlags { } @deprecated("Use flagString on the flag-carrying member", "2.10.0") - def flagsToString(flags: Long): String = { + private[scala] def flagsToString(flags: Long): String = { // Fast path for common case if (flags == 0L) "" else { var sb: StringBuilder = null @@ -497,4 +508,4 @@ class Flags extends ModifierFlags { final val rawFlagPickledOrder: Array[Long] = pickledListOrder.toArray } -object Flags extends Flags { } +object Flags extends Flags diff --git a/src/reflect/scala/reflect/internal/FreshNames.scala b/src/reflect/scala/reflect/internal/FreshNames.scala new file mode 100644 index 0000000000..7e9a568266 --- /dev/null +++ b/src/reflect/scala/reflect/internal/FreshNames.scala @@ -0,0 +1,39 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala +package reflect +package internal + +import scala.reflect.internal.util.FreshNameCreator + +trait FreshNames { self: Names with StdNames => + // SI-6879 Keeps track of counters that are supposed to be globally unique + // as opposed to traditional freshers that are unique to compilation units. + val globalFreshNameCreator = new FreshNameCreator + + // default fresh name creator used to abstract over currentUnit.fresh and runtime fresh name creator + def currentFreshNameCreator: FreshNameCreator + + // create fresh term/type name using implicit fresh name creator + def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX)(implicit creator: FreshNameCreator): TermName = newTermName(creator.newName(prefix)) + def freshTypeName(prefix: String)(implicit creator: FreshNameCreator): TypeName = newTypeName(creator.newName(prefix)) + + // Extractor that matches names which were generated by some + // FreshNameCreator with known prefix. Extracts user-specified + // prefix that was used as a parameter to newName by stripping + // global creator prefix and unique number in the end of the name. + class FreshNameExtractor(creatorPrefix: String = "") { + // quote prefix so that it can be used with replaceFirst + // which expects regExp rather than simple string + val quotedCreatorPrefix = java.util.regex.Pattern.quote(creatorPrefix) + + def unapply(name: Name): Option[String] = { + val sname = name.toString + // name should start with creatorPrefix and end with number + if (!sname.startsWith(creatorPrefix) || !sname.matches("^.*\\d*$")) None + else Some(NameTransformer.decode(sname.replaceFirst(quotedCreatorPrefix, "").replaceAll("\\d*$", ""))) + } + } +} diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 12fd3a31cd..aa8f4c532e 100644 --- a/src/reflect/scala/reflect/internal/HasFlags.scala +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal import Flags._ @@ -80,8 +81,11 @@ trait HasFlags { // identically, testing for a single flag. def hasAbstractFlag = hasFlag(ABSTRACT) def hasAccessorFlag = hasFlag(ACCESSOR) - def hasDefault = hasAllFlags(DEFAULTPARAM | PARAM) + def hasDefault = hasFlag(DEFAULTPARAM) && hasFlag(METHOD | PARAM) // Second condition disambiguates with TRAIT + def hasEnumFlag = hasFlag(ENUM) + @deprecated("Use isLocalToThis instead", "2.11.0") def hasLocalFlag = hasFlag(LOCAL) + def isLocalToThis = hasFlag(LOCAL) def hasModuleFlag = hasFlag(MODULE) def hasPackageFlag = hasFlag(PACKAGE) def hasStableFlag = hasFlag(STABLE) @@ -104,6 +108,7 @@ trait HasFlags { def isOverride = hasFlag(OVERRIDE) def isParamAccessor = hasFlag(PARAMACCESSOR) def isPrivate = hasFlag(PRIVATE) + @deprecated ("Use `hasPackageFlag` instead", "2.11.0") def isPackage = hasFlag(PACKAGE) def isPrivateLocal = hasAllFlags(PrivateLocal) def isProtected = hasFlag(PROTECTED) @@ -161,15 +166,6 @@ trait HasFlags { 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") + // Guess this can't be deprecated seeing as it's in the reflect API. 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 index 9a8dee1f15..dc4ad25ef2 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -1,21 +1,23 @@ -package scala.reflect +package scala +package reflect package internal import scala.collection.mutable.WeakHashMap import scala.ref.WeakReference +import scala.reflect.internal.Flags._ // SI-6241: move importers to a mirror -trait Importers extends api.Importers { self: SymbolTable => +trait Importers { to: SymbolTable => - def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( - if (self eq from0) { + override def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( + if (to 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] - def importPosition(pos: from.Position) = pos.asInstanceOf[self.Position] + val reverse = this.asInstanceOf[from.Importer{ val from: to.type }] + def importSymbol(their: from.Symbol) = their.asInstanceOf[to.Symbol] + def importType(their: from.Type) = their.asInstanceOf[to.Type] + def importTree(their: from.Tree) = their.asInstanceOf[to.Tree] + def importPosition(their: from.Position) = their.asInstanceOf[to.Position] } } else { // todo. fix this loophole @@ -28,8 +30,8 @@ trait Importers extends api.Importers { self: SymbolTable => val from: SymbolTable - protected lazy val symMap = new Cache[from.Symbol, Symbol]() - protected lazy val tpeMap = new Cache[from.Type, Type]() + protected lazy val symMap = new Cache[from.Symbol, to.Symbol]() + protected lazy val tpeMap = new Cache[from.Type, to.Type]() protected class Cache[K <: AnyRef, V <: AnyRef] extends WeakHashMap[K, WeakReference[V]] { def weakGet(key: K): Option[V] = this get key flatMap WeakReference.unapply def weakUpdate(key: K, value: V) = this.update(key, WeakReference(value)) @@ -49,158 +51,164 @@ trait Importers extends api.Importers { self: SymbolTable => } object reverse extends from.StandardImporter { - val from: self.type = self + val from: to.type = to // FIXME this and reverse should be constantly kept in sync // not just synced once upon the first usage of reverse - for ((fromsym, WeakReference(mysym)) <- StandardImporter.this.symMap) symMap += ((mysym, WeakReference(fromsym))) - for ((fromtpe, WeakReference(mytpe)) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, WeakReference(fromtpe))) + for ((theirsym, WeakReference(mysym)) <- StandardImporter.this.symMap) symMap += ((mysym, WeakReference(theirsym))) + for ((theirtpe, WeakReference(mytpe)) <- StandardImporter.this.tpeMap) tpeMap += ((mytpe, WeakReference(theirtpe))) } - // todo. careful import of positions - def importPosition(pos: from.Position): Position = - pos.asInstanceOf[Position] + // ============== SYMBOLS ============== + + protected def recreatedSymbolCompleter(my: to.Symbol, their: from.Symbol) = { + // we lock the symbol that is imported for a very short period of time + // i.e. only for when type parameters of the symbol are being imported + // the lock is used to communicate to the recursive importSymbol calls + // that type parameters need to be created from scratch + // because otherwise type parameters are imported by looking into owner.typeParams + // which is obviously unavailable while the completer is being created + try { + my setFlag Flags.LOCKED + val mytypeParams = their.typeParams map importSymbol + new LazyPolyType(mytypeParams) with FlagAgnosticCompleter { + override def complete(my: to.Symbol): Unit = { + val theirCore = their.info match { + case from.PolyType(_, core) => core + case core => core + } + my setInfo GenPolyType(mytypeParams, importType(theirCore)) + my setAnnotations (their.annotations map importAnnotationInfo) + markAllCompleted(my) + } + } + } finally { + my resetFlag Flags.LOCKED + } + } + + protected def recreateSymbol(their: from.Symbol): to.Symbol = { + val myowner = importSymbol(their.owner) + val mypos = importPosition(their.pos) + val myname = importName(their.name) + val myflags = their.flags + def linkReferenced(my: TermSymbol, their: from.TermSymbol, op: from.Symbol => Symbol): Symbol = { + symMap.weakUpdate(their, my) + my.referenced = op(their.referenced) + my + } + val my = their match { + case their: from.MethodSymbol => + linkReferenced(myowner.newMethod(myname.toTermName, mypos, myflags), their, importSymbol) + case their: from.ModuleSymbol => + val ret = linkReferenced(myowner.newModuleSymbol(myname.toTermName, mypos, myflags), their, importSymbol) + ret.associatedFile = their.associatedFile + ret + case their: from.FreeTermSymbol => + newFreeTermSymbol(myname.toTermName, their.value, their.flags, their.origin) setInfo importType(their.info) + case their: from.FreeTypeSymbol => + newFreeTypeSymbol(myname.toTypeName, their.flags, their.origin) + case their: from.TermSymbol => + linkReferenced(myowner.newValue(myname.toTermName, mypos, myflags), their, importSymbol) + case their: from.TypeSkolem => + val origin = their.unpackLocation match { + case null => null + case theirloc: from.Tree => importTree(theirloc) + case theirloc: from.Symbol => importSymbol(theirloc) + } + myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) + case their: from.ModuleClassSymbol => + val my = myowner.newModuleClass(myname.toTypeName, mypos, myflags) + symMap.weakUpdate(their, my) + my.sourceModule = importSymbol(their.sourceModule) + my + case their: from.ClassSymbol => + val my = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) + symMap.weakUpdate(their, my) + if (their.thisSym != their) { + my.typeOfThis = importType(their.typeOfThis) + my.thisSym setName importName(their.thisSym.name) + } + my.associatedFile = their.associatedFile + my + case their: from.TypeSymbol => + myowner.newTypeSymbol(myname.toTypeName, mypos, myflags) + } + symMap.weakUpdate(their, my) + markFlagsCompleted(my)(mask = AllFlags) + my setInfo recreatedSymbolCompleter(my, their) + } - def importSymbol(sym0: from.Symbol): Symbol = { - def doImport(sym: from.Symbol): Symbol = - symMap weakGet sym match { + def importSymbol(their0: from.Symbol): Symbol = { + def cachedRecreateSymbol(their: from.Symbol): Symbol = + symMap weakGet their match { case Some(result) => result - case _ => - 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.weakUpdate(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, x.value, x.flags, x.origin) setInfo importType(x.info) - case x: from.FreeTypeSymbol => - newFreeTypeSymbol(importName(x.name).toTypeName, 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.weakUpdate(x, mysym) - mysym.sourceModule = importSymbol(x.sourceModule) - mysym - case x: from.ClassSymbol => - val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) - symMap.weakUpdate(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.weakUpdate(sym, mysym) - mysym setFlag Flags.LOCKED - mysym setInfo { - val mytypeParams = sym.typeParams map importSymbol - new LazyPolyType(mytypeParams) with FlagAgnosticCompleter { - 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 + case _ => recreateSymbol(their) + } - def importOrRelink: Symbol = { - val sym = sym0 // makes sym visible in the debugger - if (sym == null) + def recreateOrRelink: Symbol = { + val their = their0 // makes their visible in the debugger + if (their == null) null - else if (sym == from.NoSymbol) + else if (their == from.NoSymbol) NoSymbol - else if (sym.isRoot) + else if (their.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) + val isModuleClass = their.isModuleClass + val isTparam = their.isTypeParameter && their.paramPos >= 0 + val isOverloaded = their.isOverloaded + + var theirscope = if (their.owner.isClass && !their.owner.isRefinementClass) their.owner.info else from.NoType + val theirexisting = if (isModuleClass) theirscope.decl(their.name).moduleClass else theirscope.decl(their.name) + if (!theirexisting.exists) theirscope = from.NoType + + val myname = importName(their.name) + val myowner = importSymbol(their.owner) + val myscope = if (theirscope != from.NoType && !(myowner hasFlag Flags.LOCKED)) myowner.info else NoType + val myexisting = { + if (isModuleClass) importSymbol(their.sourceModule).moduleClass + else if (isTparam) (if (myowner hasFlag Flags.LOCKED) NoSymbol else myowner.typeParams(their.paramPos)) + else if (isOverloaded) myowner.newOverloaded(myowner.thisType, their.alternatives map importSymbol) + else { + def disambiguate(my: Symbol) = { + val result = + if (their.isMethod) { + val localCopy = cachedRecreateSymbol(their) + my filter (_.tpe matches localCopy.tpe) + } else { + my filter (!_.isMethod) + } + assert(!result.isOverloaded, + "import failure: cannot determine unique overloaded method alternative from\n "+ + (result.alternatives map (_.defString) mkString "\n")+"\n that matches "+their+":"+their.tpe) + result } - 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 - } + val myexisting = if (myscope != NoType) myscope.decl(myname) else NoSymbol + if (myexisting.isOverloaded) disambiguate(myexisting) + else myexisting } } - mysym + myexisting.orElse { + val my = cachedRecreateSymbol(their) + if (myscope != NoType) { + assert(myscope.decls.lookup(myname) == NoSymbol, myname+" "+myscope.decl(myname)+" "+myexisting) + myscope.decls enter my + } + my + } } - } // end importOrRelink + } // end recreateOrRelink - val sym = sym0 - symMap.weakGet(sym) match { + val their = their0 + symMap.weakGet(their) match { case Some(result) => result case None => pendingSyms += 1 try { - val result = importOrRelink - symMap.weakUpdate(sym, result) + val result = recreateOrRelink + symMap.weakUpdate(their, result) result } finally { pendingSyms -= 1 @@ -209,71 +217,72 @@ trait Importers extends api.Importers { self: SymbolTable => } } - 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.constr), 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) + // ============== TYPES ============== + + def recreateType(their: from.Type): Type = their 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, result) => + MethodType(params map importSymbol, importType(result)) + case from.PolyType(tparams, result) => + PolyType(tparams map importSymbol, importType(result)) + case from.NullaryMethodType(result) => + NullaryMethodType(importType(result)) + 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(importType(bounds).asInstanceOf[TypeBounds]) + 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(their.typeSymbol)) + case from.ExistentialType(tparams, result) => + newExistentialType(tparams map importSymbol, importType(result)) + case from.OverloadedType(pre, alts) => + OverloadedType(importType(pre), alts map importSymbol) + case from.ImportType(qual) => + ImportType(importTree(qual)) + case from.AntiPolyType(pre, targs) => + AntiPolyType(importType(pre), targs map importType) + case their: from.TypeVar => + val myconstr = new TypeConstraint(their.constr.loBounds map importType, their.constr.hiBounds map importType) + myconstr.inst = importType(their.constr.inst) + TypeVar(importType(their.origin), myconstr, their.typeArgs map importType, their.params map importSymbol) + case from.AnnotatedType(annots, result) => + AnnotatedType(annots map importAnnotationInfo, importType(result)) + case from.ErrorType => + ErrorType + case from.WildcardType => + WildcardType + case from.NoType => + NoType + case from.NoPrefix => + NoPrefix + case null => + null + } - tpeMap.weakGet(tpe) match { + def importType(their: from.Type): Type = { + tpeMap.weakGet(their) match { case Some(result) => result case None => pendingTpes += 1 try { - val result = importOrRelink - tpeMap.weakUpdate(tpe, result) + val result = recreateType(their) + tpeMap.weakUpdate(their, result) result } finally { pendingTpes -= 1 @@ -282,7 +291,145 @@ trait Importers extends api.Importers { self: SymbolTable => } } - def importTypeBounds(bounds: from.TypeBounds) = importType(bounds).asInstanceOf[TypeBounds] + // ============== TREES ============== + + def recreatedTreeCompleter(their: from.Tree, my: to.Tree): Unit = { + if (their.canHaveAttrs) { + if (my.hasSymbolField) my.symbol = importSymbol(their.symbol) + my.pos = importPosition(their.pos) + (their, my) match { + case (their: from.TypeTree, my: to.TypeTree) => + if (their.wasEmpty) my.defineType(importType(their.tpe)) else my.setType(importType(their.tpe)) + case (_, _) => + my.tpe = importType(their.tpe) + } + } + } + + def recreateTree(their: from.Tree): to.Tree = their 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.noSelfType => + noSelfType + case from.pendingSuperCall => + pendingSuperCall + case from.ValDef(mods, name, tpt, rhs) => + new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs)) + case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) => + 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) => their 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), importName(mix).toTypeName) + 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 theirtt @ from.TypeTree() => + val mytt = TypeTree() + if (theirtt.original != null) mytt.setOriginal(importTree(theirtt.original)) + mytt + 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 importMemberDef) + case from.EmptyTree => + EmptyTree + case null => + null + } + + def importTree(their: from.Tree): Tree = { + val my = recreateTree(their) + if (my != null) { + addFixup(recreatedTreeCompleter(their, my)) + tryFixup() + // we have to be careful with position import as some shared trees + // like EmptyTree, noSelfType don't support position assignment + if (their.pos != NoPosition) { + my.setPos(importPosition(their.pos)) + } + } + importAttachments(their.attachments.all).foreach { my.updateAttachment(_) } + my + } + + // ============== MISCELLANEOUS ============== + + def importAttachments(attachments: Set[Any]): Set[Any] = + attachments.collect { case ia: ImportableAttachment => ia.importAttachment(this) } def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = { val atp1 = importType(ann.atp) @@ -301,13 +448,13 @@ trait Importers extends api.Importers { self: SymbolTable => ScalaSigBytes(bytes) case from.NestedAnnotArg(annInfo) => NestedAnnotArg(importAnnotationInfo(annInfo)) + case from.UnmappableAnnotArg => + UnmappableAnnotArg } - 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. careful import of positions + def importPosition(their: from.Position): to.Position = + their.asInstanceOf[Position] // !!! todo: override to cater for PackageScopes def importScope(decls: from.Scope): Scope = @@ -315,144 +462,15 @@ trait Importers extends api.Importers { self: SymbolTable => 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() - // we have to be careful with position import as some shared trees - // like EmptyTree, emptyValDef don't support position assignment - if (tree.pos != NoPosition) - mytree.setPos(importPosition(tree.pos)) - else - mytree - } - def importValDef(tree: from.ValDef): ValDef = importTree(tree).asInstanceOf[ValDef] def importTypeDef(tree: from.TypeDef): TypeDef = importTree(tree).asInstanceOf[TypeDef] + def importMemberDef(tree: from.MemberDef): MemberDef = importTree(tree).asInstanceOf[MemberDef] 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] diff --git a/src/reflect/scala/reflect/internal/InfoTransformers.scala b/src/reflect/scala/reflect/internal/InfoTransformers.scala index 82904b0b68..3814259e22 100644 --- a/src/reflect/scala/reflect/internal/InfoTransformers.scala +++ b/src/reflect/scala/reflect/internal/InfoTransformers.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal trait InfoTransformers { @@ -43,7 +44,7 @@ trait InfoTransformers { if (from == this.pid) this else if (from < this.pid) if (prev.pid < from) this - else prev.nextFrom(from); + else prev.nextFrom(from) else if (next.pid == NoPhase.id) next else next.nextFrom(from) } diff --git a/src/reflect/scala/reflect/internal/Internals.scala b/src/reflect/scala/reflect/internal/Internals.scala new file mode 100644 index 0000000000..e9916cf7d1 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Internals.scala @@ -0,0 +1,174 @@ +package scala +package reflect +package internal + +import scala.language.implicitConversions +import scala.language.higherKinds +import scala.collection.mutable.WeakHashMap +import scala.ref.WeakReference +import scala.reflect.api.Universe +import scala.reflect.macros.Attachments +import scala.reflect.internal.util.FreshNameCreator +import scala.reflect.internal.Flags._ +import scala.reflect.internal.util.ListOfNil + +trait Internals extends api.Internals { + self: SymbolTable => + + type Internal = MacroInternalApi + lazy val internal: Internal = new SymbolTableInternal {} + + type Compat = MacroCompatApi + lazy val compat: Compat = new Compat {} + + trait SymbolTableInternal extends MacroInternalApi { + lazy val reificationSupport: ReificationSupportApi = self.build + + def createImporter(from0: Universe): Importer { val from: from0.type } = self.mkImporter(from0) + + def newScopeWith(elems: Symbol*): Scope = self.newScopeWith(elems: _*) + def enter(scope: Scope, sym: Symbol): scope.type = { scope.enter(sym); scope } + def unlink(scope: Scope, sym: Symbol): scope.type = { scope.unlink(sym); scope } + + def freeTerms(tree: Tree): List[FreeTermSymbol] = tree.freeTerms + def freeTypes(tree: Tree): List[FreeTypeSymbol] = tree.freeTypes + def substituteSymbols(tree: Tree, from: List[Symbol], to: List[Symbol]): Tree = tree.substituteSymbols(from, to) + def substituteTypes(tree: Tree, from: List[Symbol], to: List[Type]): Tree = tree.substituteTypes(from, to) + def substituteThis(tree: Tree, clazz: Symbol, to: Tree): Tree = tree.substituteThis(clazz, to) + def attachments(tree: Tree): Attachments { type Pos = Position } = tree.attachments + def updateAttachment[T: ClassTag](tree: Tree, attachment: T): tree.type = tree.updateAttachment(attachment) + def removeAttachment[T: ClassTag](tree: Tree): tree.type = tree.removeAttachment[T] + def setPos(tree: Tree, newpos: Position): tree.type = tree.setPos(newpos) + def setType(tree: Tree, tp: Type): tree.type = tree.setType(tp) + def defineType(tree: Tree, tp: Type): tree.type = tree.defineType(tp) + def setSymbol(tree: Tree, sym: Symbol): tree.type = tree.setSymbol(sym) + def setOriginal(tt: TypeTree, tree: Tree): TypeTree = tt.setOriginal(tree) + + def captureVariable(vble: Symbol): Unit = self.captureVariable(vble) + def referenceCapturedVariable(vble: Symbol): Tree = self.referenceCapturedVariable(vble) + def capturedVariableType(vble: Symbol): Type = self.capturedVariableType(vble) + + def classDef(sym: Symbol, impl: Template): ClassDef = self.ClassDef(sym, impl) + def moduleDef(sym: Symbol, impl: Template): ModuleDef = self.ModuleDef(sym, impl) + def valDef(sym: Symbol, rhs: Tree): ValDef = self.ValDef(sym, rhs) + def valDef(sym: Symbol): ValDef = self.ValDef(sym) + def defDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = self.DefDef(sym, mods, vparamss, rhs) + def defDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = self.DefDef(sym, vparamss, rhs) + def defDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = self.DefDef(sym, mods, rhs) + def defDef(sym: Symbol, rhs: Tree): DefDef = self.DefDef(sym, rhs) + def defDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = self.DefDef(sym, rhs) + def typeDef(sym: Symbol, rhs: Tree): TypeDef = self.TypeDef(sym, rhs) + def typeDef(sym: Symbol): TypeDef = self.TypeDef(sym) + def labelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = self.LabelDef(sym, params, rhs) + + def changeOwner(tree: Tree, prev: Symbol, next: Symbol): tree.type = { + object changeOwnerAndModuleClassTraverser extends ChangeOwnerTraverser(prev, next) { + override def traverse(tree: Tree) { + tree match { + case _: DefTree => change(tree.symbol.moduleClass) + case _ => // do nothing + } + super.traverse(tree) + } + } + changeOwnerAndModuleClassTraverser.traverse(tree) + tree + } + + lazy val gen = self.treeBuild + + def isFreeTerm(symbol: Symbol): Boolean = symbol.isFreeTerm + def asFreeTerm(symbol: Symbol): FreeTermSymbol = symbol.asFreeTerm + def isFreeType(symbol: Symbol): Boolean = symbol.isFreeType + def asFreeType(symbol: Symbol): FreeTypeSymbol = symbol.asFreeType + def newTermSymbol(symbol: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol = symbol.newTermSymbol(name, pos, flags) + def newModuleAndClassSymbol(symbol: Symbol, name: Name, pos: Position = NoPosition, flags: FlagSet = NoFlags): (ModuleSymbol, ClassSymbol) = symbol.newModuleAndClassSymbol(name, pos, flags) + def newMethodSymbol(symbol: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): MethodSymbol = symbol.newMethodSymbol(name, pos, flags) + def newTypeSymbol(symbol: Symbol, name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TypeSymbol = symbol.newTypeSymbol(name, pos, flags) + def newClassSymbol(symbol: Symbol, name: TypeName, pos: Position = NoPosition, flags: FlagSet = NoFlags): ClassSymbol = symbol.newClassSymbol(name, pos, flags) + def newFreeTerm(name: String, value: => Any, flags: FlagSet = NoFlags, origin: String = null): FreeTermSymbol = reificationSupport.newFreeTerm(name, value, flags, origin) + def newFreeType(name: String, flags: FlagSet = NoFlags, origin: String = null): FreeTypeSymbol = reificationSupport.newFreeType(name, flags, origin) + def isErroneous(symbol: Symbol): Boolean = symbol.isErroneous + def isSkolem(symbol: Symbol): Boolean = symbol.isSkolem + def deSkolemize(symbol: Symbol): Symbol = symbol.deSkolemize + def initialize(symbol: Symbol): symbol.type = symbol.initialize + def fullyInitialize(symbol: Symbol): symbol.type = definitions.fullyInitializeSymbol(symbol).asInstanceOf[symbol.type] + def fullyInitialize(tp: Type): tp.type = definitions.fullyInitializeType(tp).asInstanceOf[tp.type] + def fullyInitialize(scope: Scope): scope.type = definitions.fullyInitializeScope(scope).asInstanceOf[scope.type] + def flags(symbol: Symbol): FlagSet = symbol.flags + def attachments(symbol: Symbol): Attachments { type Pos = Position } = symbol.attachments + def updateAttachment[T: ClassTag](symbol: Symbol, attachment: T): symbol.type = symbol.updateAttachment(attachment) + def removeAttachment[T: ClassTag](symbol: Symbol): symbol.type = symbol.removeAttachment[T] + def setOwner(symbol: Symbol, newowner: Symbol): symbol.type = { symbol.owner = newowner; symbol } + def setInfo(symbol: Symbol, tpe: Type): symbol.type = symbol.setInfo(tpe) + def setAnnotations(symbol: Symbol, annots: Annotation*): symbol.type = symbol.setAnnotations(annots: _*) + def setName(symbol: Symbol, name: Name): symbol.type = symbol.setName(name) + def setPrivateWithin(symbol: Symbol, sym: Symbol): symbol.type = symbol.setPrivateWithin(sym) + def setFlag(symbol: Symbol, flags: FlagSet): symbol.type = symbol.setFlag(flags) + def resetFlag(symbol: Symbol, flags: FlagSet): symbol.type = symbol.resetFlag(flags) + + def thisType(sym: Symbol): Type = self.ThisType(sym) + def singleType(pre: Type, sym: Symbol): Type = self.SingleType(pre, sym) + def superType(thistpe: Type, supertpe: Type): Type = self.SuperType(thistpe, supertpe) + def constantType(value: Constant): ConstantType = self.ConstantType(value) + def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type = self.TypeRef(pre, sym, args) + def refinedType(parents: List[Type], decls: Scope): RefinedType = self.RefinedType(parents, decls) + def refinedType(parents: List[Type], decls: Scope, clazz: Symbol): RefinedType = self.RefinedType(parents, decls, clazz) + def refinedType(parents: List[Type], owner: Symbol): Type = self.refinedType(parents, owner) + def refinedType(parents: List[Type], owner: Symbol, decls: Scope): Type = self.RefinedType(parents, decls, owner) + def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos: Position): Type = self.refinedType(parents, owner, decls, pos) + def intersectionType(tps: List[Type]): Type = self.intersectionType(tps) + def intersectionType(tps: List[Type], owner: Symbol): Type = self.intersectionType(tps, owner) + def classInfoType(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType = self.ClassInfoType(parents, decls, typeSymbol) + def methodType(params: List[Symbol], resultType: Type): MethodType = self.MethodType(params, resultType) + def nullaryMethodType(resultType: Type): NullaryMethodType = self.NullaryMethodType(resultType) + def polyType(typeParams: List[Symbol], resultType: Type): PolyType = self.PolyType(typeParams, resultType) + def existentialType(quantified: List[Symbol], underlying: Type): ExistentialType = self.ExistentialType(quantified, underlying) + def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type = self.existentialAbstraction(tparams, tpe0) + def annotatedType(annotations: List[Annotation], underlying: Type): AnnotatedType = self.AnnotatedType(annotations, underlying) + def typeBounds(lo: Type, hi: Type): TypeBounds = self.TypeBounds(lo, hi) + def boundedWildcardType(bounds: TypeBounds): BoundedWildcardType = self.BoundedWildcardType(bounds) + + def subpatterns(tree: Tree): Option[List[Tree]] = tree.attachments.get[SubpatternsAttachment].map(_.patterns.map(_.duplicate)) + + type Decorators = MacroDecoratorApi + lazy val decorators: Decorators = new MacroDecoratorApi { + override type ScopeDecorator[T <: Scope] = MacroScopeDecoratorApi[T] + override implicit def scopeDecorator[T <: Scope](scope: T): ScopeDecorator[T] = new MacroScopeDecoratorApi[T](scope) + override type TreeDecorator[T <: Tree] = MacroTreeDecoratorApi[T] + override implicit def treeDecorator[T <: Tree](tree: T): TreeDecorator[T] = new MacroTreeDecoratorApi[T](tree) + override type TypeTreeDecorator[T <: TypeTree] = MacroTypeTreeDecoratorApi[T] + override implicit def typeTreeDecorator[T <: TypeTree](tt: T): TypeTreeDecorator[T] = new MacroTypeTreeDecoratorApi[T](tt) + override type SymbolDecorator[T <: Symbol] = MacroSymbolDecoratorApi[T] + override implicit def symbolDecorator[T <: Symbol](symbol: T): SymbolDecorator[T] = new MacroSymbolDecoratorApi[T](symbol) + override type TypeDecorator[T <: Type] = TypeDecoratorApi[T] + override implicit def typeDecorator[T <: Type](tp: T): TypeDecorator[T] = new TypeDecoratorApi[T](tp) + } + } + + lazy val treeBuild = new self.TreeGen { + def mkAttributedQualifier(tpe: Type): Tree = self.gen.mkAttributedQualifier(tpe) + def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = self.gen.mkAttributedQualifier(tpe, termSym) + def mkAttributedRef(pre: Type, sym: Symbol): RefTree = self.gen.mkAttributedRef(pre, sym) + def mkAttributedRef(sym: Symbol): RefTree = self.gen.mkAttributedRef(sym) + def stabilize(tree: Tree): Tree = self.gen.stabilize(tree) + def mkAttributedStableRef(pre: Type, sym: Symbol): Tree = self.gen.mkAttributedStableRef(pre, sym) + def mkAttributedStableRef(sym: Symbol): Tree = self.gen.mkAttributedStableRef(sym) + def mkUnattributedRef(sym: Symbol): RefTree = self.gen.mkUnattributedRef(sym) + def mkUnattributedRef(fullName: Name): RefTree = self.gen.mkUnattributedRef(fullName) + def mkAttributedThis(sym: Symbol): This = self.gen.mkAttributedThis(sym) + def mkAttributedIdent(sym: Symbol): RefTree = self.gen.mkAttributedIdent(sym) + def mkAttributedSelect(qual: Tree, sym: Symbol): RefTree = self.gen.mkAttributedSelect(qual, sym) + def mkMethodCall(receiver: Symbol, methodName: Name, targs: List[Type], args: List[Tree]): Tree = self.gen.mkMethodCall(receiver, methodName, targs, args) + def mkMethodCall(method: Symbol, targs: List[Type], args: List[Tree]): Tree = self.gen.mkMethodCall(method, targs, args) + def mkMethodCall(method: Symbol, args: List[Tree]): Tree = self.gen.mkMethodCall(method, args) + def mkMethodCall(target: Tree, args: List[Tree]): Tree = self.gen.mkMethodCall(target, args) + def mkMethodCall(receiver: Symbol, methodName: Name, args: List[Tree]): Tree = self.gen.mkMethodCall(receiver, methodName, args) + def mkMethodCall(receiver: Tree, method: Symbol, targs: List[Type], args: List[Tree]): Tree = self.gen.mkMethodCall(receiver, method, targs, args) + def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = self.gen.mkMethodCall(target, targs, args) + def mkNullaryCall(method: Symbol, targs: List[Type]): Tree = self.gen.mkNullaryCall(method, targs) + def mkRuntimeUniverseRef: Tree = self.gen.mkRuntimeUniverseRef + def mkZero(tp: Type): Tree = self.gen.mkZero(tp) + def mkCast(tree: Tree, pt: Type): Tree = self.gen.mkCast(tree, pt) + } +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala new file mode 100644 index 0000000000..fb1cdb34e1 --- /dev/null +++ b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala @@ -0,0 +1,47 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala +package reflect +package internal + +import scala.language.implicitConversions +import java.lang.{ Class => jClass } +import java.lang.annotation.{ Annotation => jAnnotation } +import java.lang.reflect.{ + Member => jMember, Constructor => jConstructor, Method => jMethod, + AnnotatedElement => jAnnotatedElement, Type => jType, + TypeVariable => jTypeVariable +} + +/** This class tries to abstract over some of the duplication + * in java.lang.reflect.{ Method, Constructor }. + */ +class JMethodOrConstructor(val member: jMember with jAnnotatedElement) { + def isVarArgs: Boolean = member match { + case m: jMethod => m.isVarArgs + case m: jConstructor[_] => m.isVarArgs + } + def typeParams: Array[_ <: jTypeVariable[_]] = member match { + case m: jMethod => m.getTypeParameters + case m: jConstructor[_] => m.getTypeParameters + } + def paramTypes: Array[jType] = member match { + case m: jMethod => m.getGenericParameterTypes + case m: jConstructor[_] => m.getGenericParameterTypes + } + def paramAnnotations: Array[Array[jAnnotation]] = member match { + case m: jMethod => m.getParameterAnnotations + case m: jConstructor[_] => m.getParameterAnnotations + } + def resultType: jType = member match { + case m: jMethod => m.getGenericReturnType + case m: jConstructor[_] => classOf[Unit] + } +} + +object JMethodOrConstructor { + implicit def liftMethodToJmoc(m: jMethod): JMethodOrConstructor = new JMethodOrConstructor(m) + implicit def liftConstructorToJmoc(m: jConstructor[_]): JMethodOrConstructor = new JMethodOrConstructor(m) +} diff --git a/src/reflect/scala/reflect/internal/JavaAccFlags.scala b/src/reflect/scala/reflect/internal/JavaAccFlags.scala new file mode 100644 index 0000000000..0a33b8cf0d --- /dev/null +++ b/src/reflect/scala/reflect/internal/JavaAccFlags.scala @@ -0,0 +1,84 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala +package reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember, Constructor => jConstructor, Field => jField, Method => jMethod } +import JavaAccFlags._ +import ClassfileConstants._ + +/** A value class which encodes the access_flags (JVMS 4.1) + * for a field, method, or class. The low 16 bits are the same + * as those returned by java.lang.reflect.Member#getModifiers + * and found in the bytecode. + * + * The high bits encode whether the access flags are directly + * associated with a class, constructor, field, or method. + */ +final class JavaAccFlags private (val coded: Int) extends AnyVal { + private def has(mask: Int) = (flags & mask) != 0 + private def flagCarrierId = coded >>> 16 + private def flags = coded & 0xFFFF + + def isAbstract = has(JAVA_ACC_ABSTRACT) + def isAnnotation = has(JAVA_ACC_ANNOTATION) + def isBridge = has(JAVA_ACC_BRIDGE) + def isEnum = has(JAVA_ACC_ENUM) + def isFinal = has(JAVA_ACC_FINAL) + def isInterface = has(JAVA_ACC_INTERFACE) + def isNative = has(JAVA_ACC_NATIVE) + def isPrivate = has(JAVA_ACC_PRIVATE) + def isProtected = has(JAVA_ACC_PROTECTED) + def isPublic = has(JAVA_ACC_PUBLIC) + def isStatic = has(JAVA_ACC_STATIC) + def isStrictFp = has(JAVA_ACC_STRICT) + def isSuper = has(JAVA_ACC_SUPER) + def isSynchronized = has(JAVA_ACC_SYNCHRONIZED) + def isSynthetic = has(JAVA_ACC_SYNTHETIC) + def isTransient = has(JAVA_ACC_TRANSIENT) + def isVarargs = has(JAVA_ACC_VARARGS) + def isVolatile = has(JAVA_ACC_VOLATILE) + + /** Do these flags describe a member which has either protected or package access? + * Such access in java is encoded in scala as protected[foo] or private[foo], where + * `foo` is the defining package. + */ + def hasPackageAccessBoundary = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC) // equivalently, allows protected or package level access + def isPackageProtected = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC) + + def toJavaFlags: Int = flags + def toScalaFlags: Long = flagCarrierId match { + case Method | Constructor => FlagTranslation methodFlags flags + case Class => FlagTranslation classFlags flags + case _ => FlagTranslation fieldFlags flags + } +} + +object JavaAccFlags { + private val Unknown = 0 + private val Class = 1 + private val Field = 2 + private val Method = 3 + private val Constructor = 4 + + private def create(flagCarrier: Int, access_flags: Int): JavaAccFlags = + new JavaAccFlags((flagCarrier << 16) | (access_flags & 0xFFFF)) + + def classFlags(flags: Int): JavaAccFlags = create(Class, flags) + def methodFlags(flags: Int): JavaAccFlags = create(Method, flags) + def fieldFlags(flags: Int): JavaAccFlags = create(Field, flags) + def constructorFlags(flags: Int): JavaAccFlags = create(Constructor, flags) + + def apply(access_flags: Int): JavaAccFlags = create(Unknown, access_flags) + def apply(clazz: jClass[_]): JavaAccFlags = classFlags(clazz.getModifiers) + def apply(member: jMember): JavaAccFlags = member match { + case x: jConstructor[_] => constructorFlags(x.getModifiers) + case x: jMethod => methodFlags(x.getModifiers) + case x: jField => fieldFlags(x.getModifiers) + case _ => apply(member.getModifiers) + } +} diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index 3ab7b20ecd..8ae201f045 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.collection.{ mutable, immutable } @@ -36,7 +37,7 @@ trait Kinds { private def varStr(s: Symbol): String = if (s.isCovariant) "covariant" else if (s.isContravariant) "contravariant" - else "invariant"; + 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)) "" @@ -86,15 +87,15 @@ trait Kinds { // 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 + checkKindBounds0(tparams, targs, pre, owner, explainErrors = 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 + sym2.variance.isInvariant + || sym1.variance == sym2.variance ) /** Check well-kindedness of type application (assumes arities are already checked) -- @M @@ -145,7 +146,7 @@ trait Kinds { kindErrors = f(kindErrors) } - if (settings.debug.value) { + if (settings.debug) { 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) @@ -202,7 +203,7 @@ trait Kinds { else NoKindErrors } - if (settings.debug.value && (tparams.nonEmpty || targs.nonEmpty)) log( + if (settings.debug && (tparams.nonEmpty || targs.nonEmpty)) log( "checkKindBounds0(" + tparams + ", " + targs + ", " + pre + ", " + owner + ", " + explainErrors + ")" ) @@ -230,4 +231,182 @@ trait Kinds { } } } + + /** + * The data structure describing the kind of a given type. + * + * Proper types are represented using ProperTypeKind. + * + * Type constructors are reprented using TypeConKind. + */ + abstract class Kind { + import Kind.StringState + def description: String + def order: Int + def bounds: TypeBounds + + /** Scala syntax notation of this kind. + * Proper types are expresses as A. + * Type constructors are expressed as F[k1 >: lo <: hi, k2, ...] where k1, k2, ... are parameter kinds. + * If the bounds exists at any level, it preserves the type variable names. Otherwise, + * it uses prescribed letters for each level: A, F, X, Y, Z. + */ + def scalaNotation: String + + /** Kind notation used in http://adriaanm.github.com/files/higher.pdf. + * Proper types are expressed as *. + * Type constructors are expressed * -> *(lo, hi) -(+)-> *. + */ + def starNotation: String + + /** Contains bounds either as part of itself or its arguments. + */ + def hasBounds: Boolean = !bounds.isEmptyBounds + + private[internal] def buildState(sym: Symbol, v: Variance)(s: StringState): StringState + } + object Kind { + private[internal] sealed trait ScalaNotation + private[internal] sealed case class Head(order: Int, n: Option[Int], alias: Option[String]) extends ScalaNotation { + override def toString: String = { + alias getOrElse { + typeAlias(order) + n.map(_.toString).getOrElse("") + } + } + private def typeAlias(x: Int): String = + x match { + case 0 => "A" + case 1 => "F" + case 2 => "X" + case 3 => "Y" + case 4 => "Z" + case n if n < 12 => ('O'.toInt - 5 + n).toChar.toString + case _ => "V" + } + } + private[internal] sealed case class Text(value: String) extends ScalaNotation { + override def toString: String = value + } + private[internal] case class StringState(tokens: Seq[ScalaNotation]) { + override def toString: String = tokens.mkString + def append(value: String): StringState = StringState(tokens :+ Text(value)) + def appendHead(order: Int, sym: Symbol): StringState = { + val n = countByOrder(order) + 1 + val alias = if (sym eq NoSymbol) None + else Some(sym.nameString) + StringState(tokens :+ Head(order, Some(n), alias)) + } + def countByOrder(o: Int): Int = tokens count { + case Head(`o`, _, _) => true + case t => false + } + // Replace Head(o, Some(1), a) with Head(o, None, a) if countByOrder(o) <= 1, so F1[A] becomes F[A] + def removeOnes: StringState = { + val maxOrder = (tokens map { + case Head(o, _, _) => o + case _ => 0 + }).max + StringState((tokens /: (0 to maxOrder)) { (ts: Seq[ScalaNotation], o: Int) => + if (countByOrder(o) <= 1) + ts map { + case Head(`o`, _, a) => Head(o, None, a) + case t => t + } + else ts + }) + } + // Replace Head(o, n, Some(_)) with Head(o, n, None), so F[F] becomes F[A]. + def removeAlias: StringState = { + StringState(tokens map { + case Head(o, n, Some(_)) => Head(o, n, None) + case t => t + }) + } + } + private[internal] object StringState { + def empty: StringState = StringState(Seq()) + } + def FromParams(tparams: List[Symbol]): Type = GenPolyType(tparams, AnyTpe) + def Wildcard: Type = WildcardType + } + class ProperTypeKind(val bounds: TypeBounds) extends Kind { + import Kind.StringState + val description: String = "This is a proper type." + val order = 0 + private[internal] def buildState(sym: Symbol, v: Variance)(s: StringState): StringState = { + s.append(v.symbolicString).appendHead(order, sym).append(bounds.scalaNotation(_.toString)) + } + def scalaNotation: String = Kind.Head(order, None, None) + bounds.scalaNotation(_.toString) + def starNotation: String = "*" + bounds.starNotation(_.toString) + } + object ProperTypeKind { + def apply: ProperTypeKind = this(TypeBounds.empty) + def apply(bounds: TypeBounds): ProperTypeKind = new ProperTypeKind(bounds) + def unapply(ptk: ProperTypeKind): Some[TypeBounds] = Some(ptk.bounds) + } + + class TypeConKind(val bounds: TypeBounds, val args: Seq[TypeConKind.Argument]) extends Kind { + import Kind.StringState + val order = (args map (_.kind.order)).max + 1 + def description: String = + if (order == 1) "This is a type constructor: a 1st-order-kinded type." + else "This is a type constructor that takes type constructor(s): a higher-kinded type." + override def hasBounds: Boolean = super.hasBounds || args.exists(_.kind.hasBounds) + def scalaNotation: String = { + val s = buildState(NoSymbol, Variance.Invariant)(StringState.empty).removeOnes + val s2 = if (hasBounds) s + else s.removeAlias + s2.toString + } + private[internal] def buildState(sym: Symbol, v: Variance)(s0: StringState): StringState = { + var s: StringState = s0 + s = s.append(v.symbolicString).appendHead(order, sym).append("[") + args.zipWithIndex foreach { case (arg, i) => + s = arg.kind.buildState(arg.sym, arg.variance)(s) + if (i != args.size - 1) { + s = s.append(",") + } + } + s = s.append("]").append(bounds.scalaNotation(_.toString)) + s + } + def starNotation: String = { + import Variance._ + (args map { arg => + (if (arg.kind.order == 0) arg.kind.starNotation + else "(" + arg.kind.starNotation + ")") + + (if (arg.variance == Invariant) " -> " + else " -(" + arg.variance.symbolicString + ")-> ") + }).mkString + "*" + bounds.starNotation(_.toString) + } + } + object TypeConKind { + def apply(args: Seq[TypeConKind.Argument]): TypeConKind = this(TypeBounds.empty, args) + def apply(bounds: TypeBounds, args: Seq[TypeConKind.Argument]): TypeConKind = new TypeConKind(bounds, args) + def unapply(tck: TypeConKind): Some[(TypeBounds, Seq[TypeConKind.Argument])] = Some((tck.bounds, tck.args)) + case class Argument(variance: Variance, kind: Kind)(val sym: Symbol) {} + } + + /** + * Starting from a Symbol (sym) or a Type (tpe), infer the kind that classifies it (sym.tpeHK/tpe). + */ + object inferKind { + import TypeConKind.Argument + + abstract class InferKind { + protected def infer(tpe: Type, owner: Symbol, topLevel: Boolean): Kind + protected def infer(sym: Symbol, topLevel: Boolean): Kind = infer(sym.tpeHK, sym.owner, topLevel) + def apply(sym: Symbol): Kind = infer(sym, true) + def apply(tpe: Type, owner: Symbol): Kind = infer(tpe, owner, true) + } + + def apply(pre: Type): InferKind = new InferKind { + protected def infer(tpe: Type, owner: Symbol, topLevel: Boolean): Kind = { + val bounds = if (topLevel) TypeBounds.empty + else tpe.asSeenFrom(pre, owner).bounds + if(!tpe.isHigherKinded) ProperTypeKind(bounds) + else TypeConKind(bounds, tpe.typeParams map { p => Argument(p.variance, infer(p, false))(p) }) + } + } + } } diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 0beb8e368f..4a35e024de 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import Flags._ @@ -19,6 +20,8 @@ trait Mirrors extends api.Mirrors { trait RootSymbol extends Symbol { def mirror: Mirror } abstract class RootsBase(rootOwner: Symbol) extends scala.reflect.api.Mirror[Mirrors.this.type] { thisMirror => + private[this] var initialized = false + def isMirrorInitialized = initialized protected[scala] def rootLoader: LazyType @@ -27,24 +30,26 @@ trait Mirrors extends api.Mirrors { val EmptyPackageClass: ClassSymbol val EmptyPackage: ModuleSymbol + def symbolOf[T: universe.WeakTypeTag]: universe.TypeSymbol = universe.weakTypeTag[T].in(this).tpe.typeSymbolDirect.asType + 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 */ + /** Todo: organize similar to mkStatic in scala.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) - var sym = owner.info member name + 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 + if (settings.debug) { log(sym.info); log(sym.info.members) }//debug thisMirror.missingHook(owner, name) orElse { MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path+" in "+thisMirror) } @@ -76,7 +81,9 @@ trait Mirrors extends api.Mirrors { protected def universeMissingHook(owner: Symbol, name: Name): Symbol = thisUniverse.missingHook(owner, name) - private[scala] def missingHook(owner: Symbol, name: Name): Symbol = mirrorMissingHook(owner, name) orElse universeMissingHook(owner, name) + private[scala] def missingHook(owner: Symbol, name: Name): Symbol = logResult(s"missingHook($owner, $name)")( + mirrorMissingHook(owner, name) orElse universeMissingHook(owner, name) + ) // todo: get rid of most the methods here and keep just staticClass/Module/Package @@ -91,10 +98,6 @@ trait Mirrors extends api.Mirrors { } } - @deprecated("Use getClassByName", "2.10.0") - def getClass(fullname: Name): ClassSymbol = - getClassByName(fullname) - def getClassByName(fullname: Name): ClassSymbol = ensureClassSymbol(fullname.toString, getModuleOrClass(fullname.toTypeName)) @@ -116,25 +119,22 @@ trait Mirrors extends api.Mirrors { * Compiler might ignore them, but they should be loadable with macros. */ override def staticClass(fullname: String): ClassSymbol = - ensureClassSymbol(fullname, staticModuleOrClass(newTypeNameCached(fullname))) + try ensureClassSymbol(fullname, staticModuleOrClass(newTypeNameCached(fullname))) + catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ loaders of module symbols ************************/ private def ensureModuleSymbol(fullname: String, sym: Symbol, allowPackages: Boolean): ModuleSymbol = sym match { - case x: ModuleSymbol if allowPackages || !x.isPackage => x - case _ => MissingRequirementError.notFound("object " + fullname) + case x: ModuleSymbol if allowPackages || !x.hasPackageFlag => x + case _ => MissingRequirementError.notFound("object " + fullname) } - @deprecated("Use getModuleByName", "2.10.0") - def getModule(fullname: Name): ModuleSymbol = - getModuleByName(fullname) - def getModuleByName(fullname: Name): ModuleSymbol = ensureModuleSymbol(fullname.toString, getModuleOrClass(fullname.toTermName), allowPackages = true) def getRequiredModule(fullname: String): ModuleSymbol = - getModule(newTermNameCached(fullname)) + getModuleByName(newTermNameCached(fullname)) // 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 @@ -150,7 +150,7 @@ trait Mirrors extends api.Mirrors { getModuleIfDefined(newTermNameCached(fullname)) def getModuleIfDefined(fullname: Name): Symbol = - wrapMissing(getModule(fullname.toTermName)) + wrapMissing(getModuleByName(fullname.toTermName)) /** @inheritdoc * @@ -158,24 +158,29 @@ trait Mirrors extends api.Mirrors { * Compiler might ignore them, but they should be loadable with macros. */ override def staticModule(fullname: String): ModuleSymbol = - ensureModuleSymbol(fullname, staticModuleOrClass(newTermNameCached(fullname)), allowPackages = false) + try ensureModuleSymbol(fullname, staticModuleOrClass(newTermNameCached(fullname)), allowPackages = false) + catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ loaders of package symbols ************************/ private def ensurePackageSymbol(fullname: String, sym: Symbol, allowModules: Boolean): ModuleSymbol = sym match { - case x: ModuleSymbol if allowModules || x.isPackage => x - case _ => MissingRequirementError.notFound("package " + fullname) + case x: ModuleSymbol if allowModules || x.hasPackageFlag => x + case _ => MissingRequirementError.notFound("package " + fullname) } - def getPackage(fullname: Name): ModuleSymbol = + def getPackage(fullname: TermName): ModuleSymbol = ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname), allowModules = true) - def getRequiredPackage(fullname: String): ModuleSymbol = + def getPackageIfDefined(fullname: TermName): Symbol = + wrapMissing(getPackage(fullname)) + + @deprecated("Use getPackage", "2.11.0") def getRequiredPackage(fullname: String): ModuleSymbol = getPackage(newTermNameCached(fullname)) - def getPackageObject(fullname: String): ModuleSymbol = - (getPackage(newTermName(fullname)).info member nme.PACKAGE) match { + def getPackageObject(fullname: String): ModuleSymbol = getPackageObject(newTermName(fullname)) + def getPackageObject(fullname: TermName): ModuleSymbol = + (getPackage(fullname).info member nme.PACKAGE) match { case x: ModuleSymbol => x case _ => MissingRequirementError.notFound("package object " + fullname) } @@ -183,17 +188,27 @@ trait Mirrors extends api.Mirrors { def getPackageObjectIfDefined(fullname: String): Symbol = getPackageObjectIfDefined(newTermNameCached(fullname)) - def getPackageObjectIfDefined(fullname: Name): Symbol = - wrapMissing(getPackageObject(fullname.toTermName)) + def getPackageObjectIfDefined(fullname: TermName): Symbol = + wrapMissing(getPackageObject(fullname)) + + final def getPackageObjectWithMember(pre: Type, sym: Symbol): Symbol = { + // The owner of a symbol which requires package qualification may be the + // package object iself, but it also could be any superclass of the package + // object. In the latter case, we must go through the qualifier's info + // to obtain the right symbol. + if (sym.owner.isModuleClass) sym.owner.sourceModule // fast path, if the member is owned by a module class, that must be linked to the package object + else pre member nme.PACKAGE // otherwise we have to findMember + } override def staticPackage(fullname: String): ModuleSymbol = - ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) + try ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) + catch { case mre: MissingRequirementError => throw new ScalaReflectionException(mre.msg) } /************************ helpers ************************/ 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. + /* 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) + "]" @@ -202,7 +217,7 @@ trait Mirrors extends api.Mirrors { erasureString(classTag[T].runtimeClass) } - @inline private def wrapMissing(body: => Symbol): Symbol = + @inline final def wrapMissing(body: => Symbol): Symbol = try body catch { case _: MissingRequirementError => NoSymbol } @@ -228,6 +243,7 @@ trait Mirrors extends api.Mirrors { // } def init() { + if (initialized) return // 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. @@ -239,6 +255,21 @@ trait Mirrors extends api.Mirrors { RootClass.info.decls enter EmptyPackage RootClass.info.decls enter RootPackage + + if (rootOwner != NoSymbol) { + // synthetic core classes are only present in root mirrors + // because Definitions.scala, which initializes and enters them, only affects rootMirror + // therefore we need to enter them manually for non-root mirrors + definitions.syntheticCoreClasses foreach (theirSym => { + val theirOwner = theirSym.owner + assert(theirOwner.isPackageClass, s"theirSym = $theirSym, theirOwner = $theirOwner") + val ourOwner = staticPackage(theirOwner.fullName).moduleClass + val ourSym = theirSym // just copy the symbol into our branch of the symbol table + ourOwner.info.decls enterIfNew ourSym + }) + } + + initialized = true } } @@ -262,34 +293,46 @@ trait Mirrors extends api.Mirrors { def mirror = thisMirror.asInstanceOf[Mirror] } - // 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 { + class RootPackage extends ModuleSymbol(rootOwner, NoPosition, nme.ROOTPKG) with RootSymbol { this setInfo NullaryMethodType(RootClass.tpe) - RootClass.sourceModule = this override def isRootPackage = true } + + // This is the package _root_. The actual root cannot be referenced at + // the source level, but _root_ is essentially a function => <root>. + lazy val RootPackage = new RootPackage + + class RootClass extends PackageClassSymbol(rootOwner, NoPosition, tpnme.ROOT) with RootSymbol { + this setInfo rootLoader + + override def isRoot = true + override def isEffectiveRoot = true + override def isNestedClass = false + override def sourceModule = RootPackage + } + // 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 + lazy val RootClass = new RootClass - override def isRoot = true - override def isEffectiveRoot = true - override def isNestedClass = false - } - // 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 { + class 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 { + + // The empty package, which holds all top level types without given packages. + lazy val EmptyPackage = new EmptyPackage + + class EmptyPackageClass extends PackageClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { override def isEffectiveRoot = true override def isEmptyPackageClass = true + override def sourceModule = EmptyPackage } + + lazy val EmptyPackageClass = new EmptyPackageClass } } diff --git a/src/reflect/scala/reflect/internal/MissingRequirementError.scala b/src/reflect/scala/reflect/internal/MissingRequirementError.scala index 48203caa83..66dbf535d7 100644 --- a/src/reflect/scala/reflect/internal/MissingRequirementError.scala +++ b/src/reflect/scala/reflect/internal/MissingRequirementError.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal class MissingRequirementError private (msg: String) extends FatalError(msg) { diff --git a/src/reflect/scala/reflect/internal/Mode.scala b/src/reflect/scala/reflect/internal/Mode.scala new file mode 100644 index 0000000000..557ec9c162 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Mode.scala @@ -0,0 +1,141 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala +package reflect +package internal + +import scala.language.implicitConversions + +object Mode { + private implicit def liftIntBitsToMode(bits: Int): Mode = apply(bits) + def apply(bits: Int): Mode = new Mode(bits) + + /** NOmode, EXPRmode and PATTERNmode are mutually exclusive. + */ + final val NOmode: Mode = 0x000 + final val EXPRmode: Mode = 0x001 + final val PATTERNmode: Mode = 0x002 + + /** TYPEmode needs a comment. <-- XXX. + */ + final val TYPEmode: Mode = 0x004 + + /** SCCmode is orthogonal to above. When set we are + * in the this or super constructor call of a constructor. + */ + final val SCCmode: Mode = 0x008 + + /** FUNmode is orthogonal to above. + * When set we are looking for a method or constructor. + */ + final val FUNmode: Mode = 0x010 + + /** POLYmode is orthogonal to above. + * When set expression types can be polymorphic. + */ + final val POLYmode: Mode = 0x020 + + /** QUALmode is orthogonal to above. When set + * expressions may be packages and Java statics modules. + */ + final val QUALmode: Mode = 0x040 + + /** TAPPmode is set for the function/type constructor + * part of a type application. When set we do not decompose PolyTypes. + */ + final val TAPPmode: Mode = 0x080 + + /** LHSmode is set for the left-hand side of an assignment. + */ + final val LHSmode: Mode = 0x400 + + /** BYVALmode is set when we are typing an expression + * that occurs in a by-value position. An expression e1 is in by-value + * position within expression e2 iff it will be reduced to a value at that + * position during the evaluation of e2. Examples are by-value function + * arguments or the conditional of an if-then-else clause. + * This mode has been added to support continuations. + */ + final val BYVALmode: Mode = 0x8000 + + /** TYPEPATmode is set when we are typing a type in a pattern. + */ + final val TYPEPATmode: Mode = 0x10000 + + private val StickyModes: Mode = EXPRmode | PATTERNmode | TYPEmode + private val StickyModesForFun: Mode = StickyModes | SCCmode + final val MonoQualifierModes: Mode = EXPRmode | QUALmode + final val PolyQualifierModes: Mode = EXPRmode | QUALmode | POLYmode + final val OperatorModes: Mode = EXPRmode | POLYmode | TAPPmode | FUNmode + + /** Translates a mask of mode flags into something readable. + */ + private val modeNameMap = Map[Int, String]( // TODO why duplicate the bitmasks here, rather than just referring to this.EXPRmode etc? + (1 << 0) -> "EXPRmode", + (1 << 1) -> "PATTERNmode", + (1 << 2) -> "TYPEmode", + (1 << 3) -> "SCCmode", + (1 << 4) -> "FUNmode", + (1 << 5) -> "POLYmode", + (1 << 6) -> "QUALmode", + (1 << 7) -> "TAPPmode", + (1 << 8) -> "<>", // formerly SUPERCONSTRmode + (1 << 9) -> "<>", // formerly SNDTRYmode + (1 << 10) -> "LHSmode", + (1 << 11) -> "<>", + (1 << 12) -> "<>", // formerly STARmode + (1 << 13) -> "<>", // formerly ALTmode + (1 << 14) -> "<>", // formerly HKmode + (1 << 15) -> "BYVALmode", + (1 << 16) -> "TYPEPATmode" + ).map({ case (k, v) => Mode(k) -> v }) +} +import Mode._ + +final class Mode private (val bits: Int) extends AnyVal { + def &(other: Mode): Mode = new Mode(bits & other.bits) + def |(other: Mode): Mode = new Mode(bits | other.bits) + def &~(other: Mode): Mode = new Mode(bits & ~(other.bits)) + + def onlyTypePat = this & TYPEPATmode + def onlySticky = this & Mode.StickyModes + def forFunMode = this & Mode.StickyModesForFun | FUNmode | POLYmode | BYVALmode + def forTypeMode = if (typingPatternOrTypePat) TYPEmode | TYPEPATmode else TYPEmode + + def inAll(required: Mode) = (this & required) == required + def inAny(required: Mode) = (this & required) != NOmode + def inNone(prohibited: Mode) = (this & prohibited) == NOmode + + /** True if this mode matches every mode in the 'all' Mode, + * and no modes in the 'none' Mode. + */ + def in(all: Mode = NOmode, none: Mode = NOmode) = inAll(all) && inNone(none) + + def inByValMode = inAll(BYVALmode) + def inExprMode = inAll(EXPRmode) + def inFunMode = inAll(FUNmode) + def inPatternMode = inAll(PATTERNmode) + def inPolyMode = inAll(POLYmode) + def inQualMode = inAll(QUALmode) + def inSccMode = inAll(SCCmode) + def inTappMode = inAll(TAPPmode) + def inTypeMode = inAll(TYPEmode) + + def typingExprByValue = inAll(EXPRmode | BYVALmode) + def typingExprFun = inAll(EXPRmode | FUNmode) + def typingExprNotFun = in(all = EXPRmode, none = FUNmode) + def typingExprNotFunNotLhs = in(all = EXPRmode, none = FUNmode | LHSmode) + def typingExprNotLhs = in(all = EXPRmode, none = LHSmode) + def typingExprNotValue = in(all = EXPRmode, none = BYVALmode) + def typingMonoExprByValue = in(all = EXPRmode | BYVALmode, none = POLYmode) + def typingConstructorPattern = inAll(PATTERNmode | FUNmode) + def typingPatternNotConstructor = in(all = PATTERNmode, none = FUNmode) + def typingPatternOrTypePat = inAny(PATTERNmode | TYPEPATmode) + + override def toString = + if (this == NOmode) "NOmode" + else (modeNameMap filterKeys inAll).values.toList.sorted mkString "-" +} diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index c78ba72dfb..b50f324074 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -3,35 +3,33 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.io.Codec import java.security.MessageDigest import scala.language.implicitConversions -trait LowPriorityNames { - self: Names => - - implicit def nameToNameOps(name: Name): NameOps[Name] = new NameOps[Name](name) -} - -/** The class Names ... - * - * @author Martin Odersky - * @version 1.0, 05/02/2005 - */ -trait Names extends api.Names with LowPriorityNames { - implicit def promoteTermNamesAsNecessary(name: Name): TermName = name.toTermName - -// Operations ------------------------------------------------------------- - +trait Names extends api.Names { private final val HASH_SIZE = 0x8000 private final val HASH_MASK = 0x7FFF private final val NAME_SIZE = 0x20000 final val nameDebug = false + // Ideally we would just synchronize unconditionally and let HotSpot's Biased Locking + // kick in in the compiler universe, where access to the lock is single threaded. But, + // objects created in the first 4seconds of the JVM startup aren't eligible for biased + // locking. + // + // We might also be able to accept the performance hit, but we don't have tools to + // detect performance regressions. + // + // Discussion: https://groups.google.com/forum/#!search/biased$20scala-internals/scala-internals/0cYB7SkJ-nM/47MLhsgw8jwJ + protected def synchronizeNames: Boolean = false + private val nameLock: Object = new Object + /** Memory to store all names sequentially. */ var chrs: Array[Char] = new Array[Char](NAME_SIZE) private var nc = 0 @@ -42,14 +40,17 @@ trait Names extends api.Names with LowPriorityNames { /** Hashtable for finding type names quickly. */ private val typeHashtable = new Array[TypeName](HASH_SIZE) - /** The hashcode of a name. */ + /** + * The hashcode of a name depends on the first, the last and the middle character, + * and the length of the 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; + else 0 /** Is (the ASCII representation of) name at given index equal to * cs[offset..offset+len-1]? @@ -57,7 +58,7 @@ trait Names extends api.Names with LowPriorityNames { 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 += 1 i == len } @@ -78,65 +79,101 @@ trait Names extends api.Names with LowPriorityNames { } /** Create a term name from the characters in cs[offset..offset+len-1]. */ - def newTermName(cs: Array[Char], offset: Int, len: Int): TermName = + final 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) + final def newTermName(cs: Array[Char]): TermName = newTermName(cs, 0, cs.length) + + final 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.) + * + * @param len0 the length of the name. Negative lengths result in empty names. */ - 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) + final def newTermName(cs: Array[Char], offset: Int, len0: Int, cachedString: String): TermName = { + def body = { + require(offset >= 0, "offset must be non-negative, got " + offset) + val len = math.max(len0, 0) + 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. + var startIndex = 0 + if (cs == chrs) { + // Optimize for subName, the new name is already stored in chrs + startIndex = offset + } else { + startIndex = nc + enterChars(cs, offset, len) + } + val next = termHashtable(h) + val termName = + if (cachedString ne null) new TermName_S(startIndex, len, next, cachedString) + else new TermName_R(startIndex, len, next) + // Add the new termName to the hashtable only after it's been fully constructed + termHashtable(h) = termName + termName + } } + if (synchronizeNames) nameLock.synchronized(body) else body } - protected def newTypeName(cs: Array[Char], offset: Int, len: Int, cachedString: String): TypeName = + + final def newTypeName(cs: Array[Char], offset: Int, len: Int, cachedString: String): TypeName = newTermName(cs, offset, len, cachedString).toTypeName /** Create a term name from string. */ + @deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overriden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala def newTermName(s: String): TermName = newTermName(s.toCharArray(), 0, s.length(), null) /** Create a type name from string. */ + @deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overriden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala 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 = { + final 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 = + final def newTermNameCached(s: String): TermName = newTermName(s.toCharArray(), 0, s.length(), cachedString = s) - def newTypeNameCached(s: String): TypeName = + final 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 = + final 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 = + final 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) + /** + * Used by the GenBCode backend to lookup type names that are known to already exist. This method + * might be invoked in a multi-threaded setting. Invoking newTypeName instead might be unsafe. + * + * can-multi-thread: names are added to the hash tables only after they are fully constructed. + */ + final def lookupTypeName(cs: Array[Char]): TypeName = { + val hash = hashValue(cs, 0, cs.length) & HASH_MASK + var typeName = typeHashtable(hash) + + while ((typeName ne null) && (typeName.length != cs.length || !equals(typeName.start, cs, 0, cs.length))) { + typeName = typeName.next + } + assert(typeName != null, s"TypeName ${new String(cs)} not yet created.") + typeName + } // Classes ---------------------------------------------------------------------- @@ -186,28 +223,26 @@ trait Names extends api.Names with LowPriorityNames { scala.compat.Platform.arraycopy(chrs, index, cs, offset, len) /** @return the ascii representation of this name */ - final def toChars: Array[Char] = { + final def toChars: Array[Char] = { // used by ide 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) - scala.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) + /** @return true if the string value of this name is equal + * to the string value of the given name or String. + */ + def string_==(that: Name): Boolean = (that ne null) && (toString == that.toString) + def string_==(that: String): Boolean = (that ne null) && (toString == that) + + /**** + * This has been quite useful to find places where people are comparing + * a TermName and a TypeName, or a Name and a String. + + override def equals(other: Any) = paranoidEquals(other) private def paranoidEquals(other: Any): Boolean = { val cmp = this eq other.asInstanceOf[AnyRef] if (cmp || !nameDebug) @@ -215,7 +250,7 @@ trait Names extends api.Names with LowPriorityNames { other match { case x: String => - Console.println("Compared " + debugString + " and String '" + x + "'") + Console.println(s"Compared $debugString and String '$x'") case x: Name => if (this.isTermName != x.isTermName) { val panic = this.toTermName == x.toTermName @@ -228,6 +263,7 @@ trait Names extends api.Names with LowPriorityNames { } false } + ****/ /** @return the i'th Char of this name */ final def charAt(i: Int): Char = chrs(index + i) @@ -235,14 +271,14 @@ trait Names extends api.Names with LowPriorityNames { /** @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 */ + /** @return the index of first occurrence of s 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 ... + * @param start the index from which to search * @return the index of the first occurrence of c */ final def pos(c: Char, start: Int): Int = { @@ -255,7 +291,7 @@ trait Names extends api.Names with LowPriorityNames { * in this name from start, length if not found. * * @param s the string - * @param start ... + * @param start the index from which to search * @return the index of the first occurrence of s */ final def pos(s: String, start: Int): Int = { @@ -279,13 +315,11 @@ trait Names extends api.Names with LowPriorityNames { */ 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 ... + * @param start the index from which to search * @return the index of the last occurrence of c */ final def lastPos(c: Char, start: Int): Int = { @@ -294,26 +328,6 @@ trait Names extends api.Names with LowPriorityNames { 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) @@ -322,7 +336,14 @@ trait Names extends api.Names with LowPriorityNames { var i = 0 while (i < prefix.length && start + i < len && chrs(index + start + i) == chrs(prefix.start + i)) - i += 1; + i += 1 + i == prefix.length + } + final def startsWith(prefix: String, start: Int): Boolean = { + var i = 0 + while (i < prefix.length && start + i < len && + chrs(index + start + i) == prefix.charAt(i)) + i += 1 i == prefix.length } @@ -334,7 +355,14 @@ trait Names extends api.Names with LowPriorityNames { var i = 1 while (i <= suffix.length && i <= end && chrs(index + end - i) == chrs(suffix.start + suffix.length - i)) - i += 1; + i += 1 + i > suffix.length + } + final def endsWith(suffix: String, end: Int): Boolean = { + var i = 1 + while (i <= suffix.length && i <= end && + chrs(index + end - i) == suffix.charAt(suffix.length - i)) + i += 1 i > suffix.length } @@ -362,20 +390,22 @@ trait Names extends api.Names with LowPriorityNames { final def startChar: Char = this charAt 0 final def endChar: Char = this charAt len - 1 final def startsWith(char: Char): Boolean = len > 0 && startChar == char - final def startsWith(name: String): Boolean = startsWith(newTermName(name)) + final def startsWith(name: String): Boolean = startsWith(name, 0) final def endsWith(char: Char): Boolean = len > 0 && endChar == char - final def endsWith(name: String): Boolean = endsWith(newTermName(name)) + final def endsWith(name: String): Boolean = endsWith(name, len) - 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) + /** Rewrite the confusing failure indication via result == length to + * the normal failure indication via result == -1. + */ + private def fixIndexOf(idx: Int): Int = if (idx == length) -1 else idx + + def indexOf(ch: Char) = fixIndexOf(pos(ch)) + def indexOf(ch: Char, fromIndex: Int) = fixIndexOf(pos(ch, fromIndex)) + def indexOf(s: String) = fixIndexOf(pos(s)) + + /** The lastPos methods already return -1 on failure. */ + def lastIndexOf(ch: Char): Int = lastPos(ch) + def lastIndexOf(s: String): Int = toString lastIndexOf s /** Replace all occurrences of `from` by `to` in * name; result is always a term name. @@ -421,27 +451,44 @@ trait Names extends api.Names with LowPriorityNames { } /** 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 append(ch: Char) = newName(toString + ch) + def append(suffix: String) = newName(toString + suffix) + def append(suffix: Name) = newName(toString + suffix) + def append(separator: Char, suffix: Name) = newName(toString + separator + suffix) 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 isOperatorName: Boolean = decode != toString // used by ide def longString: String = nameKind + " " + decode def debugString = { val s = decode ; if (isTypeName) s + "!" else s } } + implicit def AnyNameOps(name: Name): NameOps[Name] = new NameOps(name) implicit def TermNameOps(name: TermName): NameOps[TermName] = new NameOps(name) implicit def TypeNameOps(name: TypeName): NameOps[TypeName] = new NameOps(name) + /** FIXME: This is a good example of something which is pure "value class" but cannot + * reap the benefits because an (unused) $outer pointer so it is not single-field. + */ final class NameOps[T <: Name](name: T) { - def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name - def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] - def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] - def nonEmpty: Boolean = name.length > 0 + import NameTransformer._ + def stripSuffix(suffix: String): T = if (name endsWith suffix) dropRight(suffix.length) else name // OPT avoid creating a Name with `suffix` + def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name + def take(n: Int): T = name.subName(0, n).asInstanceOf[T] + def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] + def dropLocal: TermName = name.toTermName stripSuffix LOCAL_SUFFIX_STRING + def dropSetter: TermName = name.toTermName stripSuffix SETTER_SUFFIX_STRING + def dropModule: T = this stripSuffix MODULE_SUFFIX_STRING + def localName: TermName = getterName append LOCAL_SUFFIX_STRING + def setterName: TermName = getterName append SETTER_SUFFIX_STRING + def getterName: TermName = dropTraitSetterSeparator.dropSetter.dropLocal + + private def dropTraitSetterSeparator: TermName = + name indexOf TRAIT_SETTER_SEPARATOR_STRING match { + case -1 => name.toTermName + case idx => name.toTermName drop idx drop TRAIT_SETTER_SEPARATOR_STRING.length + } } implicit val NameTag = ClassTag[Name](classOf[Name]) @@ -462,42 +509,49 @@ trait Names extends api.Names with LowPriorityNames { /** 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) + private final class TermName_S(index0: Int, len0: Int, next0: TermName, override val toString: String) extends TermName(index0, len0, next0) { + protected def createCompanionName(next: TypeName): TypeName = new TypeName_S(index, len, next, 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) + private final class TypeName_S(index0: Int, len0: Int, next0: TypeName, override val toString: String) extends TypeName(index0, len0, next0) { 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) + private final class TermName_R(index0: Int, len0: Int, next0: TermName) extends TermName(index0, len0, next0) { + protected def createCompanionName(next: TypeName): TypeName = new TypeName_R(index, len, next) 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) + private final class TypeName_R(index0: Int, len0: Int, next0: TypeName) extends TypeName(index0, len0, next0) { override def toString = new String(chrs, index, len) } - sealed abstract class TermName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { + // SYNCNOTE: caller to constructor must synchronize if `synchronizeNames` is enabled + sealed abstract class TermName(index0: Int, len0: Int, val next: TermName) extends Name(index0, len0) with TermNameApi { 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 body = { + // Re-computing the hash saves a field for storing it in the TermName + 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 { + val next = typeHashtable(h) + val typeName = createCompanionName(next) + // Add the new typeName to the hashtable only after it's been fully constructed + typeHashtable(h) = typeName + typeName + } + } + if (synchronizeNames) nameLock.synchronized(body) else body } def newName(str: String): TermName = newTermName(str) def companionName: TypeName = toTypeName @@ -505,27 +559,35 @@ trait Names extends api.Names with LowPriorityNames { newTermName(chrs, start + from, to - from) def nameKind = "term" - protected def createCompanionName(h: Int): TypeName + /** SYNCNOTE: caller must synchronize if `synchronizeNames` is enabled */ + protected def createCompanionName(next: TypeName): TypeName } implicit val TermNameTag = ClassTag[TermName](classOf[TermName]) - sealed abstract class TypeName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) { + object TermName extends TermNameExtractor { + def apply(s: String) = newTermName(s) + def unapply(name: TermName): Option[String] = Some(name.toString) + } + + sealed abstract class TypeName(index0: Int, len0: Int, val next: TypeName) extends Name(index0, len0) with TypeNameApi { 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 body = { + // Re-computing the hash saves a field for storing it in the TypeName + val h = hashValue(chrs, index, len) & HASH_MASK + var n = termHashtable(h) + while ((n ne null) && n.start != index) + n = n.next + + assert (n ne null, s"TypeName $this is missing its correspondent") + n + } + if (synchronizeNames) nameLock.synchronized(body) else body } def toTypeName: TypeName = this def newName(str: String): TypeName = newTypeName(str) @@ -535,8 +597,12 @@ trait Names extends api.Names with LowPriorityNames { 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]) + + object TypeName extends TypeNameExtractor { + def apply(s: String) = newTypeName(s) + def unapply(name: TypeName): Option[String] = Some(name.toString) + } } diff --git a/src/reflect/scala/reflect/internal/Phase.scala b/src/reflect/scala/reflect/internal/Phase.scala index c0f4232724..1ecc202a07 100644 --- a/src/reflect/scala/reflect/internal/Phase.scala +++ b/src/reflect/scala/reflect/internal/Phase.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal abstract class Phase(val prev: Phase) { @@ -25,11 +26,14 @@ abstract class Phase(val prev: Phase) { ) def flagMask: Long = fmask - private var nx: Phase = this + private var nx: Phase = NoPhase - def next: Phase = nx + // does anyone rely on next == this for terminus? + def next: Phase = if (nx eq NoPhase) this else nx def hasNext = next != this - def iterator = Iterator.iterate(this)(_.next) takeWhile (p => p.next != p) + // this definition excludes the terminal phase + //def iterator = Iterator.iterate(this)(_.nx) takeWhile (p => p.next != p) + def iterator = Iterator.iterate(this)(_.nx) takeWhile (_ ne NoPhase) def name: String def description: String = name diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index faa161d6b1..01fba1efc1 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -1,34 +1,274 @@ -package scala.reflect +package scala +package reflect package internal +import util._ +import scala.collection.mutable.ListBuffer + +/** Handling range positions + * atPos, the main method in this trait, will add positions to a tree, + * and will ensure the following properties: + * + * 1. All nodes between the root of the tree and nodes that already have positions + * will be assigned positions. + * 2. No node which already has a position will be assigned a different range; however + * a RangePosition might become a TransparentPosition. + * 3. The position of each assigned node includes the positions of each of its children. + * 4. The positions of all solid descendants of children of an assigned node + * are mutually non-overlapping. + * + * Here, the solid descendant of a node are: + * + * If the node has a TransparentPosition, the solid descendants of all its children + * Otherwise, the singleton consisting of the node itself. + */ 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]) + def inform(msg: String): Unit + + def useOffsetPositions: Boolean = true + /** 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 that is either focused or not. */ - def wrappingPos(default: Position, trees: List[Tree]) = wrappingPos(default, trees, true) - def wrappingPos(default: Position, trees: List[Tree], focus: Boolean): Position = default + def wrappingPos(default: Position, trees: List[Tree]): Position = wrappingPos(default, trees, focus = true) + def wrappingPos(default: Position, trees: List[Tree], focus: Boolean): Position = { + if (useOffsetPositions) default else { + val ranged = trees filter (_.pos.isRange) + if (ranged.isEmpty) if (focus) default.focus else default + else Position.range(default.source, (ranged map (_.pos.start)).min, default.point, (ranged map (_.pos.end)).max) + } + } /** 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 some of 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 + def wrappingPos(trees: List[Tree]): Position = { + val headpos = trees.head.pos + if (useOffsetPositions || !headpos.isDefined) headpos + else wrappingPos(headpos, trees) + } /** Ensure that given tree has no positions that overlap with * any of the positions of `others`. This is done by * shortening the range, assigning TransparentPositions * to some of the nodes in `tree` or focusing on the position. */ - def ensureNonOverlapping(tree: Tree, others: List[Tree]){ ensureNonOverlapping(tree, others, true) } - def ensureNonOverlapping(tree: Tree, others: List[Tree], focus: Boolean) {} + def ensureNonOverlapping(tree: Tree, others: List[Tree]){ ensureNonOverlapping(tree, others, focus = true) } + def ensureNonOverlapping(tree: Tree, others: List[Tree], focus: Boolean) { + if (useOffsetPositions) return + + def isOverlapping(pos: Position) = + pos.isRange && (others exists (pos overlaps _.pos)) + + if (isOverlapping(tree.pos)) { + val children = tree.children + children foreach (ensureNonOverlapping(_, others, focus)) + if (tree.pos.isOpaqueRange) { + val wpos = wrappingPos(tree.pos, children, focus) + tree setPos (if (isOverlapping(wpos)) tree.pos.makeTransparent else wpos) + } + } + } + + def rangePos(source: SourceFile, start: Int, point: Int, end: Int): Position = + if (useOffsetPositions) Position.offset(source, point) + else Position.range(source, start, point, end) + + def validatePositions(tree: Tree) { + if (useOffsetPositions) return + + def reportTree(prefix : String, tree : Tree) { + val source = if (tree.pos.isDefined) tree.pos.source else "" + inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source) + inform("") + inform(treeStatus(tree)) + inform("") + } + + def positionError(msg: String)(body : => Unit) { + inform("======= Position error\n" + msg) + body + inform("\nWhile validating #" + tree.id) + inform(treeStatus(tree)) + inform("\nChildren:") + tree.children map (t => " " + treeStatus(t, tree)) foreach inform + inform("=======") + throw new ValidateException(msg) + } + + def validate(tree: Tree, encltree: Tree): Unit = { + + if (!tree.isEmpty && tree.canHaveAttrs) { + if (settings.Yposdebug && (settings.verbose || settings.Yrangepos)) + println("[%10s] %s".format("validate", treeStatus(tree, encltree))) + + if (!tree.pos.isDefined) + positionError("Unpositioned tree #"+tree.id) { + inform("%15s %s".format("unpositioned", treeStatus(tree, encltree))) + inform("%15s %s".format("enclosing", treeStatus(encltree))) + encltree.children foreach (t => inform("%15s %s".format("sibling", treeStatus(t, encltree)))) + } + if (tree.pos.isRange) { + if (!encltree.pos.isRange) + positionError("Synthetic tree ["+encltree.id+"] contains nonsynthetic tree ["+tree.id+"]") { + reportTree("Enclosing", encltree) + reportTree("Enclosed", tree) + } + if (!(encltree.pos includes tree.pos)) + positionError("Enclosing tree ["+encltree.id+"] does not include tree ["+tree.id+"]") { + reportTree("Enclosing", encltree) + reportTree("Enclosed", tree) + } + + findOverlapping(tree.children flatMap solidDescendants) match { + case List() => ; + case xs => { + positionError("Overlapping trees "+xs.map { case (x, y) => (x.id, y.id) }.mkString("", ", ", "")) { + reportTree("Ancestor", tree) + for((x, y) <- xs) { + reportTree("First overlapping", x) + reportTree("Second overlapping", y) + } + } + } + } + } + for (ct <- tree.children flatMap solidDescendants) validate(ct, tree) + } + } + + if (!isPastTyper) + validate(tree, tree) + } + + def solidDescendants(tree: Tree): List[Tree] = + if (tree.pos.isTransparent) tree.children flatMap solidDescendants + else List(tree) + + /** A free range from `lo` to `hi` */ + private def free(lo: Int, hi: Int): Range = + Range(Position.range(null, lo, lo, hi), EmptyTree) + + /** The maximal free range */ + private lazy val maxFree: Range = free(0, Int.MaxValue) + + /** A singleton list of a non-empty range from `lo` to `hi`, or else the empty List */ + private def maybeFree(lo: Int, hi: Int) = + if (lo < hi) List(free(lo, hi)) + else List() + + /** Insert `pos` into ranges `rs` if possible; + * otherwise add conflicting trees to `conflicting`. + */ + private def insert(rs: List[Range], t: Tree, conflicting: ListBuffer[Tree]): List[Range] = rs match { + case List() => + assert(conflicting.nonEmpty) + rs + case r :: rs1 => + assert(!t.pos.isTransparent) + if (r.isFree && (r.pos includes t.pos)) { +// println("subdividing "+r+"/"+t.pos) + maybeFree(t.pos.end, r.pos.end) ::: List(Range(t.pos, t)) ::: maybeFree(r.pos.start, t.pos.start) ::: rs1 + } else { + if (!r.isFree && (r.pos overlaps t.pos)) conflicting += r.tree + r :: insert(rs1, t, conflicting) + } + } + + /** Replace elem `t` of `ts` by `replacement` list. */ + private def replace(ts: List[Tree], t: Tree, replacement: List[Tree]): List[Tree] = + if (ts.head == t) replacement ::: ts.tail + else ts.head :: replace(ts.tail, t, replacement) + + /** Does given list of trees have mutually non-overlapping positions? + * pre: None of the trees is transparent + */ + def findOverlapping(cts: List[Tree]): List[(Tree, Tree)] = { + var ranges = List(maxFree) + for (ct <- cts) { + if (ct.pos.isOpaqueRange) { + val conflicting = new ListBuffer[Tree] + ranges = insert(ranges, ct, conflicting) + if (conflicting.nonEmpty) return conflicting.toList map (t => (t, ct)) + } + } + List() + } + + /** Set position of all children of a node + * @param pos A target position. + * Uses the point of the position as the point of all positions it assigns. + * Uses the start of this position as an Offset position for unpositioed trees + * without children. + * @param trees The children to position. All children must be positionable. + */ + private def setChildrenPos(pos: Position, trees: List[Tree]): Unit = try { + for (tree <- trees) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { + val children = tree.children + if (children.isEmpty) { + tree setPos pos.focus + } else { + setChildrenPos(pos, children) + tree setPos wrappingPos(pos, children) + } + } + } + } catch { + case ex: Exception => + println("error while set children pos "+pos+" of "+trees) + throw ex + } + + + class ValidateException(msg : String) extends Exception(msg) + + + /** A locator for trees with given positions. + * Given a position `pos`, locator.apply returns + * the smallest tree that encloses `pos`. + */ + class Locator(pos: Position) extends Traverser { + var last: Tree = _ + def locateIn(root: Tree): Tree = { + this.last = EmptyTree + traverse(root) + this.last + } + protected def isEligible(t: Tree) = !t.pos.isTransparent + override def traverse(t: Tree) { + t match { + case tt : TypeTree if tt.original != null && (tt.pos includes tt.original.pos) => + traverse(tt.original) + case _ => + if (t.pos includes pos) { + if (isEligible(t)) last = t + super.traverse(t) + } else t match { + case mdef: MemberDef => + traverseTrees(mdef.mods.annotations) + case _ => + } + } + } + } + + case class Range(pos: Position, tree: Tree) { + def isFree = tree == EmptyTree + } + + class TypedLocator(pos: Position) extends Locator(pos) { + override protected def isEligible(t: Tree) = super.isEligible(t) && t.tpe != null + } trait PosAssigner extends Traverser { var pos: Position @@ -38,7 +278,7 @@ trait Positions extends api.Positions { self: SymbolTable => protected class DefaultPosAssigner extends PosAssigner { var pos: Position = _ override def traverse(t: Tree) { - if (t eq EmptyTree) () + if (!t.canHaveAttrs) () else if (t.pos == NoPosition) { t.setPos(pos) super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? @@ -57,9 +297,25 @@ trait Positions extends api.Positions { self: SymbolTable => } } + /** Position a tree. + * This means: Set position of a node and position all its unpositioned children. + */ def atPos[T <: Tree](pos: Position)(tree: T): T = { - posAssigner.pos = pos - posAssigner.traverse(tree) - tree + if (useOffsetPositions || !pos.isOpaqueRange) { + posAssigner.pos = pos + posAssigner.traverse(tree) + tree + } + else { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { + tree.setPos(pos) + val children = tree.children + if (children.nonEmpty) { + if (children.tail.isEmpty) atPos(pos)(children.head) + else setChildrenPos(pos, children) + } + } + tree + } } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/Precedence.scala b/src/reflect/scala/reflect/internal/Precedence.scala new file mode 100644 index 0000000000..1430838b9d --- /dev/null +++ b/src/reflect/scala/reflect/internal/Precedence.scala @@ -0,0 +1,38 @@ +package scala +package reflect +package internal + +import scala.annotation.switch +import Chars._ + +final class Precedence private (val level: Int) extends AnyVal with Ordered[Precedence] { + def compare(that: Precedence): Int = level compare that.level + override def toString = s"Precedence($level)" +} + + +object Precedence extends (Int => Precedence) { + private val ErrorName = "<error>" + private def isAssignmentOp(name: String) = name match { + case "!=" | "<=" | ">=" | "" => false + case _ => name.last == '=' && name.head != '=' && isOperatorPart(name.head) + } + private def firstChar(ch: Char): Precedence = apply((ch: @switch) match { + case '|' => 2 + case '^' => 3 + case '&' => 4 + case '=' | '!' => 5 + case '<' | '>' => 6 + case ':' => 7 + case '+' | '-' => 8 + case '*' | '/' | '%' => 9 + case _ => if (isScalaLetter(ch)) 1 else 10 + }) + + def apply(level: Int): Precedence = new Precedence(level) + def apply(name: String): Precedence = name match { + case "" | ErrorName => this(-1) + case _ if isAssignmentOp(name) => this(0) + case _ => firstChar(name charAt 0) + } +} diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 35cb749ede..fcc377ba32 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -5,7 +5,8 @@ // todo. we need to unify this prettyprinter with NodePrinters -package scala.reflect +package scala +package reflect package internal import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } @@ -16,8 +17,6 @@ trait Printers extends api.Printers { 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 @@ -25,34 +24,35 @@ trait Printers extends api.Printers { self: SymbolTable => 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) + def quotedName(name: Name): String = quotedName(name, decode = false) + def quotedName(name: String): String = quotedName(newTermName(name), decode = 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) - } + val sym = tree.symbol + def qname = quotedName(name.dropLocal, decoded) + def qowner = quotedName(sym.owner.name.dropLocal, decoded) + def qsymbol = quotedName(sym.nameString) + + if (sym == null || sym == NoSymbol) + qname + else if (sym.isErroneous) + s"<$qname: error>" + else if (sym.isMixinConstructor) + s"/*$qowner*/$qsymbol" + else + qsymbol } - def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) - def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, decoded = true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, decoded = 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 Select(qual, name) if name.isTermName => s"${backquotedPath(qual)}.${symName(t, name)}" + case Select(qual, name) if name.isTypeName => s"${backquotedPath(qual)}#${symName(t, name)}" case Ident(name) => symName(t, name) case _ => t.toString } @@ -65,16 +65,24 @@ trait Printers extends api.Printers { self: SymbolTable => printTypes = settings.printtypes.value printIds = settings.uniqid.value + printOwners = settings.Yshowsymowners.value printKinds = settings.Yshowsymkinds.value printMirrors = false // typically there's no point to print mirrors inside the compiler, as there is only one mirror there - protected def doPrintPositions = settings.Xprintpos.value + printPositions = settings.Xprintpos.value def indent() = indentMargin += indentStep def undent() = indentMargin -= indentStep - def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) + def printPosition(tree: Tree) = + if (printPositions) comment(print(tree.pos.show)) + + protected def printTypesInfo(tree: Tree) = + if (printTypes && tree.isTerm && tree.canHaveAttrs) + comment{ + print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") + } - def println() { + def println() = { out.println() while (indentMargin > indentString.length()) indentString += indentString @@ -82,116 +90,237 @@ trait Printers extends api.Printers { self: SymbolTable => out.write(indentString, 0, indentMargin) } - def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { + def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit): 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 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) { + def printRow(ts: List[Tree], start: String, sep: String, end: String): Unit = { print(start); printSeq(ts){print(_)}{print(sep)}; print(end) } - def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } + def printRow(ts: List[Tree], sep: String): Unit = printRow(ts, "", sep, "") - def printTypeParams(ts: List[TypeDef]) { - if (!ts.isEmpty) { + def printTypeParams(ts: List[TypeDef]): Unit = + if (ts.nonEmpty) { print("["); printSeq(ts){ t => printAnnotations(t) + if (t.mods.hasFlag(CONTRAVARIANT)) { + print("-") + } else if (t.mods.hasFlag(COVARIANT)) { + print("+") + } printParam(t) }{print(", ")}; print("]") } - } - def printLabelParams(ps: List[Ident]) { + def printLabelParams(ps: List[Ident]) = { print("(") printSeq(ps){printLabelParam}{print(", ")} print(")") } - def printLabelParam(p: Ident) { + 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(")") + protected def parenthesize(condition: Boolean = true, open: String = "(", close: String = ")")(body: => Unit) = { + if (condition) print(open) + body + if (condition) print(close) } - def printParam(tree: Tree) { + protected val commentsRequired = false + + protected def comment(body: => Unit) = + parenthesize(commentsRequired, "/*", "*/")(body) + + protected def printImplicitInParamsList(vds: List[ValDef]) = + if (vds.nonEmpty) printFlags(vds.head.mods.flags & IMPLICIT, "") + + def printValueParams(ts: List[ValDef], inParentheses: Boolean = true): Unit = + parenthesize(inParentheses){ + printImplicitInParamsList(ts) + printSeq(ts){printParam}{print(", ")} + } + + def printParam(tree: Tree) = tree match { - case ValDef(mods, name, tp, rhs) => + case vd @ ValDef(mods, name, tp, rhs) => printPosition(tree) - printAnnotations(tree) + printAnnotations(vd) 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) { + 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) + 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 printOpt(prefix: String, tree: Tree) = if (tree.nonEmpty) { 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 "" + 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 + def printFlags(flags: Long, privateWithin: String) = { + val mask: Long = if (settings.debug) -1L else PrintableFlags val s = flagsToString(flags & mask, privateWithin) if (s != "") print(s + " ") } - def printAnnotations(tree: Tree) { + def printAnnotations(tree: MemberDef) = { // SI-5885: by default this won't print annotations of not yet initialized symbols val annots = tree.symbol.annotations match { - case Nil => tree.asInstanceOf[MemberDef].mods.annotations + case Nil => tree.mods.annotations case anns => anns } - annots foreach (annot => print("@"+annot+" ")) + annots foreach (annot => print(s"@$annot ")) } private var currentOwner: Symbol = NoSymbol private var selectorType: Type = NoType - def printTree(tree: Tree) { + protected def printPackageDef(tree: PackageDef, separator: String) = { + val PackageDef(packaged, stats) = tree + printAnnotations(tree) + print("package ", packaged); printColumn(stats, " {", separator, "}") + } + + protected def printValDef(tree: ValDef, resultName: => String)(printTypeSignature: => Unit)(printRhs: => Unit) = { + val ValDef(mods, name, tp, rhs) = tree + printAnnotations(tree) + printModifiers(tree, mods) + print(if (mods.isMutable) "var " else "val ", resultName) + printTypeSignature + printRhs + } + + protected def printDefDef(tree: DefDef, resultName: => String)(printTypeSignature: => Unit)(printRhs: => Unit) = { + val DefDef(mods, name, tparams, vparamss, tp, rhs) = tree + printAnnotations(tree) + printModifiers(tree, mods) + print("def " + resultName) + printTypeParams(tparams); + vparamss foreach {printValueParams(_)} + printTypeSignature + printRhs + } + + protected def printTypeDef(tree: TypeDef, resultName: => String) = { + val TypeDef(mods, name, tparams, rhs) = tree + if (mods hasFlag (PARAM | DEFERRED)) { + printAnnotations(tree) + printModifiers(tree, mods) + print("type ") + printParam(tree) + } else { + printAnnotations(tree) + printModifiers(tree, mods) + print("type " + resultName) + printTypeParams(tparams) + printOpt(" = ", rhs) + } + } + + protected def printImport(tree: Import, resSelect: => String) = { + val Import(expr, selectors) = tree + // Is this selector renaming a name (i.e, {name1 => name2}) + def isNotRename(s: ImportSelector): Boolean = + s.name == nme.WILDCARD || s.name == s.rename + + def selectorToString(s: ImportSelector): String = { + val from = quotedName(s.name) + if (isNotRename(s)) from + else from + "=>" + quotedName(s.rename) + } + print("import ", resSelect, ".") + selectors match { + case List(s) => + // If there is just one selector and it is not renaming a name, no braces are needed + if (isNotRename(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("{", ", ", "}")) + } + } + + protected def printCaseDef(tree: CaseDef) = { + val CaseDef(pat, guard, body) = tree + print("case ") + def patConstr(pat: Tree): Tree = pat match { + case Apply(fn, args) => patConstr(fn) + case _ => pat + } + + print(pat); printOpt(" if ", guard) + print(" => ", body) + } + + protected def printFunction(tree: Function)(printValueParams: => Unit) = { + val Function(vparams, body) = tree + print("("); + printValueParams + print(" => ", body, ")") + if (printIds && tree.symbol != null) + comment{ + print("#" + tree.symbol.id) + } + + if (printOwners && tree.symbol != null) + comment{ + print("@" + tree.symbol.owner.id) + } + } + + protected def printSuper(tree: Super, resultName: => String, checkSymbol: Boolean = true) = { + val Super(This(qual), mix) = tree + if (qual.nonEmpty || (checkSymbol && tree.symbol != NoSymbol)) print(resultName + ".") + print("super") + if (mix.nonEmpty) print(s"[$mix]") + } + + protected def printThis(tree: This, resultName: => String) = { + val This(qual) = tree + if (qual.nonEmpty) print(resultName + ".") + print("this") + } + + protected def printBlock(stats: List[Tree], expr: Tree) = + printColumn(stats ::: List(expr), "{", ";", "}") + + def printTree(tree: Tree) = { tree match { case EmptyTree => print("<empty>") - case ClassDef(mods, name, tparams, impl) => - printAnnotations(tree) + case cd @ ClassDef(mods, name, tparams, impl) => + printAnnotations(cd) printModifiers(tree, mods) val word = if (mods.isTrait) "trait" @@ -202,85 +331,49 @@ trait Printers extends api.Printers { self: SymbolTable => printTypeParams(tparams) print(if (mods.isDeferred) " <: " else " extends ", impl) - case PackageDef(packaged, stats) => - printAnnotations(tree) - print("package ", packaged); printColumn(stats, " {", ";", "}") + case pd @ PackageDef(packaged, stats) => + printPackageDef(pd, ";") - case ModuleDef(mods, name, impl) => - printAnnotations(tree) - printModifiers(tree, mods); + case md @ ModuleDef(mods, name, impl) => + printAnnotations(md) + 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 vd @ ValDef(mods, name, tp, rhs) => + printValDef(vd, 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 dd @ DefDef(mods, name, tparams, vparamss, tp, rhs) => + printDefDef(dd, symName(tree, name))(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 td @ TypeDef(mods, name, tparams, rhs) => + printTypeDef(td, symName(tree, name)) 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 imp @ Import(expr, _) => + printImport(imp, backquotedPath(expr)) - case Template(parents, self, body) => + 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 (body.nonEmpty) { if (self.name != nme.WILDCARD) { print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") - } else if (!self.tpt.isEmpty) { + } else if (self.tpt.nonEmpty) { print(" { _ : ", self.tpt, " => ") } else { print(" {") } printColumn(body, "", ";", "}") } -// } currentOwner = currentOwner1 case Block(stats, expr) => - printColumn(stats ::: List(expr), "{", ";", "}") + printBlock(stats, expr) case Match(selector, cases) => val selectorType1 = selectorType @@ -288,18 +381,8 @@ trait Printers extends api.Printers { self: SymbolTable => 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 cd @ CaseDef(pat, guard, body) => + printCaseDef(cd) case Alternative(trees) => printRow(trees, "(", "| ", ")") @@ -316,9 +399,8 @@ trait Printers extends api.Printers { self: SymbolTable => case ArrayValue(elemtpt, trees) => print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") - case Function(vparams, body) => - print("("); printValueParams(vparams); print(" => ", body, ")") - if (printIds && tree.symbol != null) print("#"+tree.symbol.id) + case f @ Function(vparams, body) => + printFunction(f)(printValueParams(vparams)) case Assign(lhs, rhs) => print(lhs, " = ", rhs) @@ -327,10 +409,10 @@ trait Printers extends api.Printers { self: SymbolTable => 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 + print("if (", cond, ")"); indent(); println() + print(thenp); undent() + if (elsep.nonEmpty) { + println(); print("else"); indent(); println(); print(elsep); undent() } case Return(expr) => @@ -338,7 +420,7 @@ trait Printers extends api.Printers { self: SymbolTable => case Try(block, catches, finalizer) => print("try "); printBlock(block) - if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") + if (catches.nonEmpty) printColumn(catches, " catch {", "", "}") printOpt(" finally ", finalizer) case Throw(expr) => @@ -360,22 +442,18 @@ trait Printers extends api.Printers { self: SymbolTable => 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 st @ Super(This(qual), mix) => + printSuper(st, symName(tree, qual)) case Super(qual, mix) => print(qual, ".super") - if (!mix.isEmpty) + if (mix.nonEmpty) print("[" + mix + "]") - case This(qual) => - if (!qual.isEmpty) print(symName(tree, qual) + ".") - print("this") + case th @ This(qual) => + printThis(th, symName(tree, qual)) - case Select(qual @ New(tpe), name) if (!settings.debug.value) => + case Select(qual: New, name) if !settings.debug => print(qual) case Select(qualifier, name) => @@ -389,7 +467,7 @@ trait Printers extends api.Printers { self: SymbolTable => print(x.escapedStringValue) case tt: TypeTree => - if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { + if ((tree.tpe eq null) || (printPositions && 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) { @@ -398,10 +476,10 @@ trait Printers extends api.Printers { self: SymbolTable => print(tree.tpe.toString) } - case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => + case an @ Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => def printAnnot() { print("@", tpt) - if (!args.isEmpty) + if (args.nonEmpty) printRow(args, "(", ",", ")") } print(tree, if (tree.isType) " " else ": ") @@ -420,24 +498,29 @@ trait Printers extends api.Printers { self: SymbolTable => print(tp); printRow(args, "[", ", ", "]") case TypeBoundsTree(lo, hi) => - printOpt(" >: ", lo); printOpt(" <: ", hi) + // Avoid printing noisy empty typebounds everywhere + // Untyped empty bounds are not printed by printOpt, + // but after they are typed we have to exclude Nothing/Any. + if ((lo.tpe eq null) || !(lo.tpe =:= definitions.NothingTpe)) + printOpt(" >: ", lo) + + if ((hi.tpe eq null) || !(hi.tpe =:= definitions.AnyTpe)) + printOpt(" <: ", hi) case ExistentialTypeTree(tpt, whereClauses) => - print(tpt); + print(tpt) printColumn(whereClauses, " forSome { ", ";", "}") -// SelectFromArray is no longer visible in reflect.internal. -// eliminated until we figure out what we will do with both Printers and -// SelectFromArray. -// case SelectFromArray(qualifier, name, _) => -// print(qualifier); print(".<arr>"); print(symName(tree, name)) + // SelectFromArray is no longer visible in scala.reflect.internal. + // eliminated until we figure out what we will do with both Printers and + // SelectFromArray. + // case SelectFromArray(qualifier, name, _) => + // print(qualifier); print(".<arr>"); print(symName(tree, name)) case tree => xprintTree(this, tree) } - if (printTypes && tree.isTerm && !tree.isEmpty) { - print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") - } + printTypesInfo(tree) } def print(args: Any*): Unit = args foreach { @@ -451,10 +534,570 @@ trait Printers extends api.Printers { self: SymbolTable => } } + // it's the printer for AST-based code generation + class CodePrinter(out: PrintWriter, printRootPkg: Boolean) extends TreePrinter(out) { + protected val parentsStack = scala.collection.mutable.Stack[Tree]() + + protected def currentTree = if (parentsStack.nonEmpty) Some(parentsStack.top) else None + + protected def currentParent = if (parentsStack.length > 1) Some(parentsStack(1)) else None + + protected def printedName(name: Name, decoded: Boolean = true) = { + import Chars._ + val decName = name.decoded + val bslash = '\\' + val brackets = List('[',']','(',')','{','}') + + def addBackquotes(s: String) = + if (decoded && (decName.exists(ch => brackets.contains(ch) || isWhitespace(ch)) || + (name.isOperatorName && decName.exists(isOperatorPart) && decName.exists(isScalaLetter) && !decName.contains(bslash)))) + s"`$s`" else s + + if (name == nme.CONSTRUCTOR) "this" + else addBackquotes(quotedName(name, decoded)) + } + + protected def isIntLitWithDecodedOp(qual: Tree, name: Name) = { + val qualIsIntLit = qual match { + case Literal(Constant(x: Int)) => true + case _ => false + } + qualIsIntLit && name.isOperatorName + } + + override protected val commentsRequired = true + + protected def needsParentheses(parent: Tree)(insideIf: Boolean = true, insideMatch: Boolean = true, insideTry: Boolean = true, + insideAnnotated: Boolean = true, insideBlock: Boolean = true, insideLabelDef: Boolean = true, insideAssign: Boolean = true) = { + parent match { + case _: If => insideIf + case _: Match => insideMatch + case _: Try => insideTry + case _: Annotated => insideAnnotated + case _: Block => insideBlock + case _: LabelDef => insideLabelDef + case _: Assign => insideAssign + case _ => false + } + } + + protected def checkForBlank(cond: Boolean) = if (cond) " " else "" + protected def blankForOperatorName(name: Name) = checkForBlank(name.isOperatorName) + protected def blankForName(name: Name) = checkForBlank(name.isOperatorName || name.endsWith("_")) + + protected def resolveSelect(t: Tree): String = { + t match { + // case for: 1) (if (a) b else c).meth1.meth2 or 2) 1 + 5 should be represented as (1).+(5) + case Select(qual, name) if (name.isTermName && needsParentheses(qual)(insideLabelDef = false)) || isIntLitWithDecodedOp(qual, name) => s"(${resolveSelect(qual)}).${printedName(name)}" + case Select(qual, name) if name.isTermName => s"${resolveSelect(qual)}.${printedName(name)}" + case Select(qual, name) if name.isTypeName => s"${resolveSelect(qual)}#${blankForOperatorName(name)}%${printedName(name)}" + case Ident(name) => printedName(name) + case _ => render(t, new CodePrinter(_, printRootPkg)) + } + } + + protected def emptyTree(tree: Tree) = tree match { + case EmptyTree | build.SyntacticEmptyTypeTree() => true + case _ => false + } + + protected def originalTypeTrees(trees: List[Tree]) = + trees.filter(!emptyTree(_)) map { + case tt: TypeTree => tt.original + case tree => tree + } + + val defaultClasses = List(tpnme.AnyRef) + val defaultTraitsForCase = List(tpnme.Product, tpnme.Serializable) + protected def removeDefaultTypesFromList(trees: List[Tree])(classesToRemove: List[Name] = defaultClasses)(traitsToRemove: List[Name]) = { + def removeDefaultTraitsFromList(trees: List[Tree], traitsToRemove: List[Name]): List[Tree] = + trees match { + case Nil => trees + case init :+ last => last match { + case Select(Ident(sc), name) if traitsToRemove.contains(name) && sc == nme.scala_ => + removeDefaultTraitsFromList(init, traitsToRemove) + case _ => trees + } + } + + removeDefaultTraitsFromList(removeDefaultClassesFromList(trees, classesToRemove), traitsToRemove) + } + + protected def removeDefaultClassesFromList(trees: List[Tree], classesToRemove: List[Name] = defaultClasses) = + originalTypeTrees(trees) filter { + case Select(Ident(sc), name) => !(classesToRemove.contains(name) && sc == nme.scala_) + case _ => true + } + + protected def syntheticToRemove(tree: Tree) = + tree match { + case _: ValDef | _: TypeDef => false // don't remove ValDef and TypeDef + case md: MemberDef if md.mods.isSynthetic => true + case _ => false + } + + override def printOpt(prefix: String, tree: Tree) = + if (!emptyTree(tree)) super.printOpt(prefix, tree) + + override def printColumn(ts: List[Tree], start: String, sep: String, end: String) = { + super.printColumn(ts.filter(!syntheticToRemove(_)), start, sep, end) + } + + def printFlags(mods: Modifiers, primaryCtorParam: Boolean = false): Unit = { + val base = AccessFlags | OVERRIDE | ABSTRACT | FINAL | SEALED | LAZY + val mask = if (primaryCtorParam) base else base | IMPLICIT + + val s = mods.flagString(mask) + if (s != "") print(s"$s ") + // case flag should be the last + if (mods.isCase) print(mods.flagBitsToString(CASE) + " ") + if (mods.isAbstractOverride) print("abstract override ") + } + + override def printModifiers(tree: Tree, mods: Modifiers): Unit = printModifiers(mods, primaryCtorParam = false) + + def printModifiers(mods: Modifiers, primaryCtorParam: Boolean): Unit = { + def modsAccepted = List(currentTree, currentParent) exists (_ map { + case _: ClassDef | _: ModuleDef | _: Template | _: PackageDef => true + case _ => false + } getOrElse false) + + if (currentParent.isEmpty || modsAccepted) + printFlags(mods, primaryCtorParam) + else + List(IMPLICIT, CASE, LAZY, SEALED).foreach{flag => if (mods.hasFlag(flag)) print(s"${mods.flagBitsToString(flag)} ")} + } + + def printParam(tree: Tree, primaryCtorParam: Boolean): Unit = + tree match { + case vd @ ValDef(mods, name, tp, rhs) => + printPosition(tree) + printAnnotations(vd) + val mutableOrOverride = mods.isOverride || mods.isMutable + val hideCtorMods = mods.isParamAccessor && mods.isPrivateLocal && !mutableOrOverride + val hideCaseCtorMods = mods.isCaseAccessor && mods.isPublic && !mutableOrOverride + + if (primaryCtorParam && !(hideCtorMods || hideCaseCtorMods)) { + printModifiers(mods, primaryCtorParam) + print(if (mods.isMutable) "var " else "val "); + } + print(printedName(name), blankForName(name)); + printOpt(": ", tp); + printOpt(" = ", rhs) + case TypeDef(_, name, tparams, rhs) => + printPosition(tree) + print(printedName(name)) + printTypeParams(tparams); + print(rhs) + case _ => + super.printParam(tree) + } + + override def printParam(tree: Tree): Unit = { + printParam(tree, primaryCtorParam = false) + } + + protected def printArgss(argss: List[List[Tree]]) = + argss foreach {x: List[Tree] => if (!(x.isEmpty && argss.size == 1)) printRow(x, "(", ", ", ")")} + + override def printAnnotations(tree: MemberDef) = { + val annots = tree.mods.annotations + annots foreach {annot => printAnnot(annot); print(" ")} + } + + protected def printAnnot(tree: Tree) = { + tree match { + case treeInfo.Applied(core, _, argss) => + print("@") + core match { + case Select(New(tree), _) => print(tree) + case _ => + } + printArgss(argss) + case _ => super.printTree(tree) + } + } + + override def printTree(tree: Tree): Unit = { + parentsStack.push(tree) + try { + processTreePrinting(tree); + printTypesInfo(tree) + } finally parentsStack.pop() + } + + def processTreePrinting(tree: Tree): Unit = { + tree match { + // don't remove synthetic ValDef/TypeDef + case _ if syntheticToRemove(tree) => + + case cl @ ClassDef(mods, name, tparams, impl) => + if (mods.isJavaDefined) super.printTree(cl) + printAnnotations(cl) + // traits + val clParents: List[Tree] = if (mods.isTrait) { + // avoid abstract modifier for traits + printModifiers(tree, mods &~ ABSTRACT) + print("trait ", printedName(name)) + printTypeParams(tparams) + + val build.SyntacticTraitDef(_, _, _, _, parents, _, _) = tree + parents + // classes + } else { + printModifiers(tree, mods) + print("class ", printedName(name)) + printTypeParams(tparams) + + val build.SyntacticClassDef(_, _, _, ctorMods, vparamss, earlyDefs, parents, selfType, body) = cl + + // constructor's modifier + if (ctorMods.hasFlag(AccessFlags)) { + print(" ") + printModifiers(ctorMods, primaryCtorParam = false) + } + + def printConstrParams(ts: List[ValDef]): Unit = { + parenthesize() { + printImplicitInParamsList(ts) + printSeq(ts)(printParam(_, primaryCtorParam = true))(print(", ")) + } + } + // constructor's params processing (don't print single empty constructor param list) + vparamss match { + case Nil | List(Nil) if (!mods.isCase && !ctorMods.hasFlag(AccessFlags)) => + case _ => vparamss foreach printConstrParams + } + parents + } + + // get trees without default classes and traits (when they are last) + val printedParents = removeDefaultTypesFromList(clParents)()(if (mods.hasFlag(CASE)) defaultTraitsForCase else Nil) + print(if (mods.isDeferred) "<: " else if (printedParents.nonEmpty) " extends " else "", impl) + + case pd @ PackageDef(packaged, stats) => + packaged match { + case Ident(name) if name == nme.EMPTY_PACKAGE_NAME => + printSeq(stats) { + print(_) + } { + println() + println() + }; + case _ => + printPackageDef(pd, scala.util.Properties.lineSeparator) + } + + case md @ ModuleDef(mods, name, impl) => + printAnnotations(md) + printModifiers(tree, mods) + val Template(parents, self, methods) = impl + val parWithoutAnyRef = removeDefaultClassesFromList(parents) + print("object " + printedName(name), if (parWithoutAnyRef.nonEmpty) " extends " else "", impl) + + case vd @ ValDef(mods, name, tp, rhs) => + printValDef(vd, printedName(name)) { + // place space after symbolic def name (val *: Unit does not compile) + printOpt(s"${blankForName(name)}: ", tp) + } { + if (!mods.isDeferred) print(" = ", if (rhs.isEmpty) "_" else rhs) + } + + case dd @ DefDef(mods, name, tparams, vparamss, tp, rhs) => + printDefDef(dd, printedName(name)) { + if (tparams.isEmpty && (vparamss.isEmpty || vparamss(0).isEmpty)) print(blankForName(name)) + printOpt(": ", tp) + } { + printOpt(" = " + (if (mods.isMacro) "macro " else ""), rhs) + } + + case td @ TypeDef(mods, name, tparams, rhs) => + printTypeDef(td, printedName(name)) + + case LabelDef(name, params, rhs) => + if (name.startsWith(nme.WHILE_PREFIX)) { + val If(cond, thenp, elsep) = rhs + print("while (", cond, ") ") + val Block(list, wh) = thenp + printColumn(list, "", ";", "") + } else if (name.startsWith(nme.DO_WHILE_PREFIX)) { + val Block(bodyList, ifCond @ If(cond, thenp, elsep)) = rhs + print("do ") + printColumn(bodyList, "", ";", "") + print(" while (", cond, ") ") + } else { + print(printedName(name)); printLabelParams(params); + printBlock(rhs) + } + + case imp @ Import(expr, _) => + printImport(imp, resolveSelect(expr)) + + case t @ Template(parents, self, tbody) => + val body = treeInfo.untypecheckedTemplBody(t) + val printedParents = + currentParent map { + case _: CompoundTypeTree => parents + case ClassDef(mods, name, _, _) if mods.isCase => removeDefaultTypesFromList(parents)()(List(tpnme.Product, tpnme.Serializable)) + case _ => removeDefaultClassesFromList(parents) + } getOrElse (parents) + + val primaryCtr = treeInfo.firstConstructor(body) + val ap: Option[Apply] = primaryCtr match { + case DefDef(_, _, _, _, _, Block(ctBody, _)) => + val earlyDefs = treeInfo.preSuperFields(ctBody) ::: body.filter { + case td: TypeDef => treeInfo.isEarlyDef(td) + case _ => false + } + if (earlyDefs.nonEmpty) { + print("{") + printColumn(earlyDefs, "", ";", "") + print("} " + (if (printedParents.nonEmpty) "with " else "")) + } + ctBody collectFirst { + case apply: Apply => apply + } + case _ => None + } + + if (printedParents.nonEmpty) { + val (clParent :: traits) = printedParents + print(clParent) + + val constrArgss = ap match { + case Some(treeInfo.Applied(_, _, argss)) => argss + case _ => Nil + } + printArgss(constrArgss) + if (traits.nonEmpty) { + printRow(traits, " with ", " with ", "") + } + } + /* Remove primary constr def and constr val and var defs + * right contains all constructors + */ + val (left, right) = body.filter { + // remove valdefs defined in constructor and presuper vals + case vd: ValDef => !vd.mods.isParamAccessor && !treeInfo.isEarlyValDef(vd) + // remove $this$ from traits + case dd: DefDef => dd.name != nme.MIXIN_CONSTRUCTOR + case td: TypeDef => !treeInfo.isEarlyDef(td) + case EmptyTree => false + case _ => true + } span { + case dd: DefDef => dd.name != nme.CONSTRUCTOR + case _ => true + } + val modBody = (left ::: right.drop(1)) + val showBody = !(modBody.isEmpty && (self == noSelfType || self.isEmpty)) + if (showBody) { + if (self.name != nme.WILDCARD) { + print(" { ", self.name); + printOpt(": ", self.tpt); + print(" =>") + } else if (self.tpt.nonEmpty) { + print(" { _ : ", self.tpt, " =>") + } else { + print(" {") + } + printColumn(modBody, "", ";", "}") + } + + case bl @ Block(stats, expr) => + printBlock(treeInfo.untypecheckedBlockBody(bl), expr) + + case Match(selector, cases) => + /* Insert braces if match is inner + * make this function available for other cases + * passing required type for checking + */ + def insertBraces(body: => Unit): Unit = + if (parentsStack.nonEmpty && parentsStack.tail.exists(_.isInstanceOf[Match])) { + print("(") + body + print(")") + } else body + + val printParentheses = needsParentheses(selector)(insideLabelDef = false) + tree match { + case Match(EmptyTree, cs) => + printColumn(cases, "{", "", "}") + case _ => + insertBraces { + parenthesize(printParentheses)(print(selector)) + printColumn(cases, " match {", "", "}") + } + } + + case cd @ CaseDef(pat, guard, body) => + printCaseDef(cd) + + case Star(elem) => + print(elem, "*") + + case Bind(name, t) => + if (t == EmptyTree) print("(", printedName(name), ")") + else if (t.exists(_.isInstanceOf[Star])) print(printedName(name), " @ ", t) + else print("(", printedName(name), " @ ", t, ")") + + case f @ Function(vparams, body) => + // parentheses are not allowed for val a: Int => Int = implicit x => x + val printParentheses = vparams match { + case head :: _ => !head.mods.isImplicit + case _ => true + } + printFunction(f)(printValueParams(vparams, inParentheses = printParentheses)) + + case Typed(expr, tp) => + def printTp = print("(", tp, ")") + + tp match { + case EmptyTree | build.SyntacticEmptyTypeTree() => printTp + // case for untypechecked trees + case Annotated(annot, arg) if (expr ne null) && (arg ne null) && expr.equalsStructure(arg) => printTp // remove double arg - 5: 5: @unchecked + case tt: TypeTree if tt.original.isInstanceOf[Annotated] => printTp + case Function(List(), EmptyTree) => print("(", expr, " _)") //func _ + // parentheses required when (a match {}) : Type + case _ => print("((", expr, "): ", tp, ")") + } + + // print only fun when targs are TypeTrees with empty original + case TypeApply(fun, targs) => + if (targs.exists(emptyTree(_))) { + print(fun) + } else super.printTree(tree) + + case Apply(fun, vargs) => + tree match { + // processing methods ending on colons (x \: list) + case Apply(Block(l1 @ List(sVD: ValDef), a1 @ Apply(Select(_, methodName), l2 @ List(Ident(iVDName)))), l3) + if sVD.mods.isSynthetic && treeInfo.isLeftAssoc(methodName) && sVD.name == iVDName => + val printBlock = Block(l1, Apply(a1, l3)) + print(printBlock) + case Apply(tree1, _) if (needsParentheses(tree1)(insideAnnotated = false)) => + parenthesize()(print(fun)); printRow(vargs, "(", ", ", ")") + case _ => super.printTree(tree) + } + + case UnApply(fun, args) => + fun match { + case treeInfo.Unapplied(body) => + body match { + case Select(qual, name) if name == nme.unapply => print(qual) + case TypeApply(Select(qual, name), args) if name == nme.unapply || name == nme.unapplySeq => + print(TypeApply(qual, args)) + case _ => print(body) + } + case _ => print(fun) + } + printRow(args, "(", ", ", ")") + + case st @ Super(This(qual), mix) => + printSuper(st, printedName(qual), checkSymbol = false) + + case th @ This(qual) => + if (tree.hasExistingSymbol && tree.symbol.isPackage) print(tree.symbol.fullName) + else printThis(th, printedName(qual)) + + // remove this prefix from constructor invocation in typechecked trees: this.this -> this + case Select(This(_), name @ nme.CONSTRUCTOR) => print(printedName(name)) + + case Select(qual: New, name) => + print(qual) + + case Select(qual, name) => + def checkRootPackage(tr: Tree): Boolean = + (currentParent match { //check that Select is not for package def name + case Some(_: PackageDef) => false + case _ => true + }) && (tr match { // check that Select contains package + case Select(q, _) => checkRootPackage(q) + case _: Ident | _: This => val sym = tr.symbol + tr.hasExistingSymbol && sym.isPackage && sym.name != nme.ROOTPKG + case _ => false + }) + + if (printRootPkg && checkRootPackage(tree)) print(s"${printedName(nme.ROOTPKG)}.") + val printParentheses = needsParentheses(qual)(insideAnnotated = false) || isIntLitWithDecodedOp(qual, name) + if (printParentheses) print("(", resolveSelect(qual), ").", printedName(name)) + else print(resolveSelect(qual), ".", printedName(name)) + + case id @ Ident(name) => + if (name.nonEmpty) { + if (name == nme.dollarScope) { + print(s"scala.xml.${nme.TopScope}") + } else { + val str = printedName(name) + val strIsBackquoted = str.startsWith("`") && str.endsWith("`") + print(if (id.isBackquoted && !strIsBackquoted) "`" + str + "`" else str) + } + } else { + print("") + } + + case l @ Literal(x) => + import Chars.LF + x match { + case Constant(v: String) if { + val strValue = x.stringValue + strValue.contains(LF) && strValue.contains("\"\"\"") && strValue.size > 1 + } => + val splitValue = x.stringValue.split(s"$LF").toList + val multilineStringValue = if (x.stringValue.endsWith(s"$LF")) splitValue :+ "" else splitValue + val trQuotes = "\"\"\"" + print(trQuotes); printSeq(multilineStringValue) { print(_) } { print(LF) }; print(trQuotes) + case _ => + // processing Float constants + val printValue = x.escapedStringValue + (if (x.value.isInstanceOf[Float]) "F" else "") + print(printValue) + } + + case an @ Annotated(ap, tree) => + val printParentheses = needsParentheses(tree)() + parenthesize(printParentheses) { print(tree) }; print(if (tree.isType) " " else ": ") + printAnnot(ap) + + case SelectFromTypeTree(qualifier, selector) => + print("(", qualifier, ")#", blankForOperatorName(selector), printedName(selector)) + + case tt: TypeTree => + if (!emptyTree(tt)) print(tt.original) + + case AppliedTypeTree(tp, args) => + // it's possible to have (=> String) => String type but Function1[=> String, String] is not correct + val containsByNameTypeParam = args exists treeInfo.isByNameParamType + + if (containsByNameTypeParam) { + print("(") + printRow(args.init, "(", ", ", ")") + print(" => ", args.last, ")") + } else { + if (treeInfo.isRepeatedParamType(tree) && args.nonEmpty) { + print(args(0), "*") + } else if (treeInfo.isByNameParamType(tree)) { + print("=> ", if (args.isEmpty) "()" else args(0)) + } else + super.printTree(tree) + } + + case ExistentialTypeTree(tpt, whereClauses) => + print("(", tpt); + printColumn(whereClauses, " forSome { ", ";", "})") + + case EmptyTree => + + case tree => super.printTree(tree) + } + } + } + /** Hook for extensions */ def xprintTree(treePrinter: TreePrinter, tree: Tree) = treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + def newCodePrinter(writer: PrintWriter, tree: Tree, printRootPkg: Boolean): TreePrinter = + new CodePrinter(writer, printRootPkg) + def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) @@ -475,8 +1118,6 @@ trait Printers extends api.Printers { self: SymbolTable => } def newRawTreePrinter(writer: PrintWriter): RawTreePrinter = new RawTreePrinter(writer) - def newRawTreePrinter(stream: OutputStream): RawTreePrinter = newRawTreePrinter(new PrintWriter(stream)) - def newRawTreePrinter(): RawTreePrinter = newRawTreePrinter(new PrintWriter(ConsoleWriter)) // provides footnotes for types and mirrors import scala.collection.mutable.{Map, WeakHashMap, SortedSet} @@ -525,7 +1166,7 @@ trait Printers extends api.Printers { self: SymbolTable => private var depth = 0 private var printTypesInFootnotes = true private var printingFootnotes = false - private var footnotes = footnoteIndex.mkFootnotes() + private val footnotes = footnoteIndex.mkFootnotes() def print(args: Any*): Unit = { // don't print type footnotes if the argument is a mere type @@ -542,14 +1183,17 @@ trait Printers extends api.Printers { self: SymbolTable => print(")") case EmptyTree => print("EmptyTree") - case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => - print("emptyValDef") + case self.noSelfType => + print("noSelfType") + case self.pendingSuperCall => + print("pendingSuperCall") case tree: Tree => - val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol - val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString + def hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol + val isError = hasSymbolField && (tree.symbol.name string_== nme.ERROR) printProduct( tree, preamble = _ => { + if (printPositions) print(tree.pos.show) print(tree.productPrefix) if (printTypes && tree.tpe != null) print(tree.tpe) }, @@ -559,7 +1203,7 @@ trait Printers extends api.Printers { self: SymbolTable => if (isError) print("<") print(name) if (isError) print(": error>") - } else if (hasSymbol) { + } else if (hasSymbolField) { tree match { case refTree: RefTree => if (tree.symbol.name != refTree.name) print("[", tree.symbol, " aka ", refTree.name, "]") @@ -590,6 +1234,7 @@ trait Printers extends api.Printers { self: SymbolTable => else if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) else print(sym.name) if (printIds) print("#", sym.id) + if (printOwners) print("@", sym.owner.id) if (printKinds) print("#", sym.abbreviatedKindString) if (printMirrors) print("%M", footnotes.put[scala.reflect.api.Mirror[_]](mirrorThatLoaded(sym))) case tag: TypeTag[_] => @@ -653,7 +1298,7 @@ trait Printers extends api.Printers { self: SymbolTable => print("(") val it = iterable.iterator while (it.hasNext) { - body(it.next) + body(it.next()) print(if (it.hasNext) ", " else "") } print(")") @@ -662,19 +1307,19 @@ trait Printers extends api.Printers { self: SymbolTable => } def show(name: Name): String = name match { - case tpnme.WILDCARD => "tpnme.WILDCARD" - case tpnme.EMPTY => "tpnme.EMPTY" - case tpnme.ERROR => "tpnme.ERROR" - case tpnme.PACKAGE => "tpnme.PACKAGE" - case tpnme.WILDCARD_STAR => "tpnme.WILDCARD_STAR" - case nme.WILDCARD => "nme.WILDCARD" - case nme.EMPTY => "nme.EMPTY" - case nme.ERROR => "tpnme.ERROR" - case nme.PACKAGE => "nme.PACKAGE" - case nme.CONSTRUCTOR => "nme.CONSTRUCTOR" - case nme.ROOTPKG => "nme.ROOTPKG" + case tpnme.WILDCARD => "typeNames.WILDCARD" + case tpnme.EMPTY => "typeNames.EMPTY" + case tpnme.ERROR => "typeNames.ERROR" + case tpnme.PACKAGE => "typeNames.PACKAGE" + case tpnme.WILDCARD_STAR => "typeNames.WILDCARD_STAR" + case nme.WILDCARD => "termNames.WILDCARD" + case nme.EMPTY => "termNames.EMPTY" + case nme.ERROR => "termNames.ERROR" + case nme.PACKAGE => "termNames.PACKAGE" + case nme.CONSTRUCTOR => "termNames.CONSTRUCTOR" + case nme.ROOTPKG => "termNames.ROOTPKG" case _ => - val prefix = if (name.isTermName) "newTermName(\"" else "newTypeName(\"" + val prefix = if (name.isTermName) "TermName(\"" else "TypeName(\"" prefix + name.toString + "\")" } @@ -688,4 +1333,13 @@ trait Printers extends api.Printers { self: SymbolTable => s_flags mkString " | " } } + + def show(position: Position): String = { + position.show + } + + def showDecl(sym: Symbol): String = { + if (!isCompilerUniverse) definitions.fullyInitializeSymbol(sym) + sym.defString + } } diff --git a/src/reflect/scala/reflect/internal/PrivateWithin.scala b/src/reflect/scala/reflect/internal/PrivateWithin.scala new file mode 100644 index 0000000000..996f9c13bb --- /dev/null +++ b/src/reflect/scala/reflect/internal/PrivateWithin.scala @@ -0,0 +1,27 @@ +package scala +package reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember } + +trait PrivateWithin { + self: SymbolTable => + + def propagatePackageBoundary(c: jClass[_], syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(c), syms: _*) + def propagatePackageBoundary(m: jMember, syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(m), syms: _*) + def propagatePackageBoundary(jflags: JavaAccFlags, syms: Symbol*) { + if (jflags.hasPackageAccessBoundary) + syms foreach setPackageAccessBoundary + } + + // protected in java means package protected. #3946 + // See ticket #1687 for an example of when the enclosing top level class is NoSymbol; + // it apparently occurs when processing v45.3 bytecode. + def setPackageAccessBoundary(sym: Symbol): Symbol = ( + if (sym.enclosingTopLevelClass eq NoSymbol) sym + else sym setPrivateWithin sym.enclosingTopLevelClass.owner + ) +} diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala new file mode 100644 index 0000000000..ad8a2594dd --- /dev/null +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -0,0 +1,1146 @@ +package scala +package reflect +package internal + +import Flags._ +import util._ + +trait ReificationSupport { self: SymbolTable => + import definitions._ + import internal._ + + class ReificationSupportImpl extends ReificationSupportApi { + def selectType(owner: Symbol, name: String): TypeSymbol = + select(owner, newTypeName(name)).asType + + def selectTerm(owner: Symbol, name: String): TermSymbol = { + val result = select(owner, newTermName(name)).asTerm + if (result.isOverloaded) result.suchThat(!_.isMethod).asTerm + else result + } + + protected def select(owner: Symbol, name: Name): Symbol = { + val result = owner.info decl name + if (result ne NoSymbol) result + else + mirrorThatLoaded(owner).missingHook(owner, name) orElse { + throw new ScalaReflectionException("%s %s in %s not found".format(if (name.isTermName) "term" else "type", 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.asMethod + else throw new ScalaReflectionException("overloaded method %s #%d in %s not found".format(name, index, owner.fullName)) + } + + def newFreeTerm(name: String, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = + newFreeTermSymbol(newTermName(name), value, flags, origin).markFlagsCompleted(mask = AllFlags) + + def newFreeType(name: String, flags: Long = 0L, origin: String = null): FreeTypeSymbol = + newFreeTypeSymbol(newTypeName(name), flags, origin).markFlagsCompleted(mask = AllFlags) + + def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: Long, isClass: Boolean): Symbol = + owner.newNestedSymbol(name, pos, flags, isClass).markFlagsCompleted(mask = AllFlags) + + def newScopeWith(elems: Symbol*): Scope = + self.newScopeWith(elems: _*) + + def setAnnotations[S <: Symbol](sym: S, annots: List[AnnotationInfo]): S = + sym.setAnnotations(annots) + + def setInfo[S <: Symbol](sym: S, tpe: Type): S = + sym.setInfo(tpe).markAllCompleted() + + def mkThis(sym: Symbol): Tree = self.This(sym) + + def mkSelect(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym) + + def mkIdent(sym: Symbol): Ident = self.Ident(sym) + + def mkTypeTree(tp: Type): TypeTree = self.TypeTree(tp) + + def ThisType(sym: Symbol): Type = self.ThisType(sym) + + def SingleType(pre: Type, sym: Symbol): Type = self.SingleType(pre, sym) + + def SuperType(thistpe: Type, supertpe: Type): Type = self.SuperType(thistpe, supertpe) + + def ConstantType(value: Constant): ConstantType = self.ConstantType(value) + + def TypeRef(pre: Type, sym: Symbol, args: List[Type]): Type = self.TypeRef(pre, sym, args) + + def RefinedType(parents: List[Type], decls: Scope, typeSymbol: Symbol): RefinedType = self.RefinedType(parents, decls, typeSymbol) + + def ClassInfoType(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType = self.ClassInfoType(parents, decls, typeSymbol) + + def MethodType(params: List[Symbol], resultType: Type): MethodType = self.MethodType(params, resultType) + + def NullaryMethodType(resultType: Type): NullaryMethodType = self.NullaryMethodType(resultType) + + def PolyType(typeParams: List[Symbol], resultType: Type): PolyType = self.PolyType(typeParams, resultType) + + def ExistentialType(quantified: List[Symbol], underlying: Type): ExistentialType = self.ExistentialType(quantified, underlying) + + def AnnotatedType(annotations: List[Annotation], underlying: Type): AnnotatedType = self.AnnotatedType(annotations, underlying) + + def TypeBounds(lo: Type, hi: Type): TypeBounds = self.TypeBounds(lo, hi) + + def BoundedWildcardType(bounds: TypeBounds): BoundedWildcardType = self.BoundedWildcardType(bounds) + + 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 } + + def toStats(tree: Tree): List[Tree] = tree match { + case EmptyTree => Nil + case SyntacticBlock(stats) => stats + case _ => throw new IllegalArgumentException(s"can't flatten $tree") + } + + def mkAnnotation(tree: Tree): Tree = tree match { + case SyntacticNew(Nil, SyntacticApplied(SyntacticAppliedType(_, _), _) :: Nil, noSelfType, Nil) => + tree + case _ => + throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation." + + """Consider reformatting it into a q"new $name[..$targs](...$argss)" shape""") + } + + def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation) + + def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags, excludeFlags: FlagSet = DEFERRED): List[List[ValDef]] = + argss.map { args => args.map { mkParam(_, extraFlags, excludeFlags) } } + + def mkParam(tree: Tree, extraFlags: FlagSet, excludeFlags: FlagSet): ValDef = tree match { + case Typed(Ident(name: TermName), tpt) => + mkParam(ValDef(NoMods, name, tpt, EmptyTree), extraFlags, excludeFlags) + case vd: ValDef => + var newmods = vd.mods & (~excludeFlags) + if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM + copyValDef(vd)(mods = newmods | extraFlags) + case _ => + throw new IllegalArgumentException(s"$tree is not valid represenation of a parameter, " + + """consider reformatting it into q"val $name: $T = $default" shape""") + } + + def mkImplicitParam(args: List[Tree]): List[ValDef] = args.map(mkImplicitParam) + + def mkImplicitParam(tree: Tree): ValDef = mkParam(tree, IMPLICIT | PARAM, NoFlags) + + def mkTparams(tparams: List[Tree]): List[TypeDef] = + tparams.map { + case td: TypeDef => copyTypeDef(td)(mods = (td.mods | PARAM) & (~DEFERRED)) + case other => throw new IllegalArgumentException(s"can't splice $other as type parameter") + } + + def mkRefineStat(stat: Tree): Tree = { + stat match { + case dd: DefDef => require(dd.rhs.isEmpty, "can't use DefDef with non-empty body as refine stat") + case vd: ValDef => require(vd.rhs.isEmpty, "can't use ValDef with non-empty rhs as refine stat") + case td: TypeDef => + case _ => throw new IllegalArgumentException(s"not legal refine stat: $stat") + } + stat + } + + def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat) + + def mkPackageStat(stat: Tree): Tree = { + stat match { + case cd: ClassDef => + case md: ModuleDef => + case pd: PackageDef => + case _ => throw new IllegalArgumentException(s"not legal package stat: $stat") + } + stat + } + + def mkPackageStat(stats: List[Tree]): List[Tree] = stats.map(mkPackageStat) + + object ScalaDot extends ScalaDotExtractor { + def apply(name: Name): Tree = gen.scalaDot(name) + def unapply(tree: Tree): Option[Name] = tree match { + case Select(id @ Ident(nme.scala_), name) if id.symbol == ScalaPackage => Some(name) + case _ => None + } + } + + def mkEarlyDef(defn: Tree): Tree = defn match { + case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => + copyValDef(vdef)(mods = mods | PRESUPER) + case tdef @ TypeDef(mods, _, _, _) => + copyTypeDef(tdef)(mods = mods | PRESUPER) + case _ => + throw new IllegalArgumentException(s"not legal early def: $defn") + } + + def mkEarlyDef(defns: List[Tree]): List[Tree] = defns.map(mkEarlyDef) + + def mkRefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym + + def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX): TermName = self.freshTermName(prefix) + + def freshTypeName(prefix: String): TypeName = self.freshTypeName(prefix) + + protected implicit def fresh: FreshNameCreator = self.currentFreshNameCreator + + object ImplicitParams extends ImplicitParamsExtractor { + def apply(paramss: List[List[Tree]], implparams: List[Tree]): List[List[Tree]] = + if (implparams.nonEmpty) paramss :+ mkImplicitParam(implparams) else paramss + + def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] = vparamss match { + case init :+ (last @ (initlast :: _)) if initlast.mods.isImplicit => Some((init, last)) + case _ => Some((vparamss, Nil)) + } + } + + object FlagsRepr extends FlagsReprExtractor { + def apply(bits: Long): FlagSet = bits + def unapply(flags: Long): Some[Long] = Some(flags) + } + + /** Construct/deconstruct type application term trees. + * Treats other term trees as zero-argument type applications. + */ + object SyntacticTypeApplied extends SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) tree + else if (tree.isTerm) TypeApply(tree, targs) + else throw new IllegalArgumentException(s"can't apply type arguments to $tree") + + def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { + case TypeApply(fun, targs) => Some((fun, targs)) + case _ if tree.isTerm => Some((tree, Nil)) + case _ => None + } + } + + /** Construct/deconstruct applied type trees. + * Treats other types as zero-arity applied types. + */ + object SyntacticAppliedType extends SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) tree + else if (tree.isType) AppliedTypeTree(tree, targs) + else throw new IllegalArgumentException(s"can't create applied type from non-type $tree") + + def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { + case MaybeTypeTreeOriginal(AppliedTypeTree(tpe, targs)) => Some((tpe, targs)) + case _ if tree.isType => Some((tree, Nil)) + case _ => None + } + } + + object SyntacticApplied extends SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree = + argss.foldLeft(tree) { (f, args) => Apply(f, args.map(treeInfo.assignmentToMaybeNamedArg)) } + + def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = tree match { + case UnApply(treeInfo.Unapplied(Select(fun, nme.unapply)), pats) => + Some((fun, pats :: Nil)) + case treeInfo.Applied(fun, targs, argss) => + fun match { + case Select(_: New, nme.CONSTRUCTOR) => + Some((tree, Nil)) + case _ => + val callee = + if (fun.isTerm) SyntacticTypeApplied(fun, targs) + else SyntacticAppliedType(fun, targs) + Some((callee, argss)) + } + } + } + + // recover constructor contents generated by gen.mkTemplate + protected object UnCtor { + def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match { + case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, SyntacticBlock(lvdefs :+ _)) => + Some((mods | Flag.TRAIT, Nil, lvdefs)) + case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, SyntacticBlock(lvdefs :+ _ :+ _)) => + Some((mods, vparamss, lvdefs)) + case _ => None + } + } + + // undo gen.mkTemplate + protected object UnMkTemplate { + def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = { + val Template(parents, selfType, _) = templ + val tbody = treeInfo.untypecheckedTemplBody(templ) + + def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], body: List[Tree]) = + Some((parents, selfType, ctorMods, vparamss, edefs, body)) + def indexOfCtor(trees: List[Tree]) = + trees.indexWhere { case UnCtor(_, _, _) => true ; case _ => false } + + if (tbody forall treeInfo.isInterfaceMember) + result(NoMods | Flag.TRAIT, Nil, Nil, tbody) + else if (indexOfCtor(tbody) == -1) + None + else { + val (rawEdefs, rest) = tbody.span(treeInfo.isEarlyDef) + val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef) + val (fieldDefs, UnCtor(ctorMods, ctorVparamss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest)) + val evdefs = gvdefs.zip(lvdefs).map { + case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) => + copyValDef(gvdef)(tpt = tpt.original, rhs = rhs) + } + val edefs = evdefs ::: etdefs + if (ctorMods.isTrait) + result(ctorMods, Nil, edefs, body) + else { + // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section + val vparamssRestoredImplicits = ctorVparamss match { + case Nil :: (tail @ ((head :: _) :: _)) if head.mods.isImplicit => tail + case other => other + } + // undo flag modifications by merging flag info from constructor args and fieldDefs + val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap + def ctorArgsCorrespondToFields = vparamssRestoredImplicits.flatten.forall { vd => modsMap.contains(vd.name) } + if (!ctorArgsCorrespondToFields) None + else { + val vparamss = mmap(vparamssRestoredImplicits) { vd => + val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM) + atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs)) + } + result(ctorMods, vparamss, edefs, body) + } + } + } + } + } + + protected def mkSelfType(tree: Tree) = tree match { + case vd: ValDef => + require(vd.rhs.isEmpty, "self types must have empty right hand side") + copyValDef(vd)(mods = (vd.mods | PRIVATE) & (~DEFERRED)) + case _ => + throw new IllegalArgumentException(s"$tree is not a valid representation of self type, " + + """consider reformatting into q"val $self: $T" shape""") + } + + object SyntacticClassDef extends SyntacticClassDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], + constrMods: Modifiers, vparamss: List[List[Tree]], + earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = { + val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L) + val vparamss0 = mkParam(vparamss, extraFlags, excludeFlags = DEFERRED | PARAM) + val tparams0 = mkTparams(tparams) + val parents0 = gen.mkParents(mods, + if (mods.isCase) parents.filter { + case ScalaDot(tpnme.Product | tpnme.Serializable | tpnme.AnyRef) => false + case _ => true + } else parents + ) + val body0 = earlyDefs ::: body + val selfType0 = mkSelfType(selfType) + val templ = gen.mkTemplate(parents0, selfType0, constrMods, vparamss0, body0) + gen.mkClassDef(mods, name, tparams0, templ) + } + + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]], + List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfType, ctorMods, vparamss, earlyDefs, body)) + if !ctorMods.isTrait && !ctorMods.hasFlag(JAVA) => + Some((mods, name, tparams, ctorMods, vparamss, earlyDefs, parents, selfType, body)) + case _ => + None + } + } + + object SyntacticTraitDef extends SyntacticTraitDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], earlyDefs: List[Tree], + parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = { + val mods0 = mods | TRAIT | ABSTRACT + val templ = gen.mkTemplate(parents, mkSelfType(selfType), Modifiers(TRAIT), Nil, earlyDefs ::: body) + gen.mkClassDef(mods0, name, mkTparams(tparams), templ) + } + + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], + List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfType, ctorMods, vparamss, earlyDefs, body)) + if mods.isTrait => + Some((mods, name, tparams, earlyDefs, parents, selfType, body)) + case _ => None + } + } + + object SyntacticObjectDef extends SyntacticObjectDefExtractor { + def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfType: Tree, body: List[Tree]): ModuleDef = + ModuleDef(mods, name, gen.mkTemplate(parents, mkSelfType(selfType), NoMods, Nil, earlyDefs ::: body)) + + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ModuleDef(mods, name, UnMkTemplate(parents, selfType, _, _, earlyDefs, body)) => + Some((mods, name, earlyDefs, parents, selfType, body)) + case _ => + None + } + } + + object SyntacticPackageObjectDef extends SyntacticPackageObjectDefExtractor { + def apply(name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfType: Tree, body: List[Tree]): PackageDef = + gen.mkPackageObject(SyntacticObjectDef(NoMods, name, earlyDefs, parents, selfType, body)) + + def unapply(tree: Tree): Option[(TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case PackageDef(Ident(name: TermName), List(SyntacticObjectDef(NoMods, nme.PACKAGEkw, earlyDefs, parents, selfType, body))) => + Some((name, earlyDefs, parents, selfType, body)) + case _ => + None + } + } + + // match references to `scala.$name` + protected class ScalaMemberRef(symbols: Seq[Symbol]) { + def result(name: Name): Option[Symbol] = + symbols.collect { case sym if sym.name == name => sym }.headOption + def unapply(tree: Tree): Option[Symbol] = tree match { + case id @ Ident(name) if symbols.contains(id.symbol) && name == id.symbol.name => + Some(id.symbol) + case Select(scalapkg @ Ident(nme.scala_), name) if scalapkg.symbol == ScalaPackage => + result(name) + case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) => + result(name) + case _ => None + } + } + protected object TupleClassRef extends ScalaMemberRef(TupleClass.seq) + protected object TupleCompanionRef extends ScalaMemberRef(TupleClass.seq.map { _.companionModule }) + protected object UnitClassRef extends ScalaMemberRef(Seq(UnitClass)) + protected object FunctionClassRef extends ScalaMemberRef(FunctionClass.seq) + + object SyntacticTuple extends SyntacticTupleExtractor { + def apply(args: List[Tree]): Tree = { + require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported") + gen.mkTuple(args) + } + + def unapply(tree: Tree): Option[List[Tree]] = tree match { + case Literal(Constant(())) => + Some(Nil) + case Apply(MaybeTypeTreeOriginal(SyntacticTypeApplied(MaybeSelectApply(TupleCompanionRef(sym)), targs)), args) + if sym == TupleClass(args.length).companionModule + && (targs.isEmpty || targs.length == args.length) => + Some(args) + case _ if tree.isTerm => + Some(tree :: Nil) + case _ => + None + } + } + + object SyntacticTupleType extends SyntacticTupleExtractor { + def apply(args: List[Tree]): Tree = { + require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported") + gen.mkTupleType(args) + } + + def unapply(tree: Tree): Option[List[Tree]] = tree match { + case MaybeTypeTreeOriginal(UnitClassRef(_)) => + Some(Nil) + case MaybeTypeTreeOriginal(AppliedTypeTree(TupleClassRef(sym), args)) + if sym == TupleClass(args.length) => + Some(args) + case _ if tree.isType => + Some(tree :: Nil) + case _ => + None + } + } + + object SyntacticFunctionType extends SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree = { + require(FunctionClass(argtpes.length).exists, s"Function types with ${argtpes.length} arity aren't supported") + gen.mkFunctionTypeTree(argtpes, restpe) + } + + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case MaybeTypeTreeOriginal(AppliedTypeTree(FunctionClassRef(sym), args @ (argtpes :+ restpe))) + if sym == FunctionClass(args.length - 1) => + Some((argtpes, restpe)) + case _ => None + } + } + + object SyntheticUnit { + def unapply(tree: Tree): Boolean = tree match { + case Literal(Constant(())) if tree.hasAttachment[SyntheticUnitAttachment.type] => true + case _ => false + } + } + + /** Syntactic combinator that abstracts over Block tree. + * + * Apart from providing a more straightforward api that exposes + * block as a list of elements rather than (stats, expr) pair + * it also: + * + * 1. Strips trailing synthetic units which are inserted by the + * compiler if the block ends with a definition rather + * than an expression or is empty. + * + * 2. Matches non-block term trees and recognizes them as + * single-element blocks for sake of consistency with + * compiler's default to treat single-element blocks with + * expressions as just expressions. The only exception is q"" + * which is not considered to be a block. + */ + object SyntacticBlock extends SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) + + def unapply(tree: Tree): Option[List[Tree]] = tree match { + case bl @ self.Block(stats, SyntheticUnit()) => Some(treeInfo.untypecheckedBlockBody(bl)) + case bl @ self.Block(stats, expr) => Some(treeInfo.untypecheckedBlockBody(bl) :+ expr) + case SyntheticUnit() => Some(Nil) + case _ if tree.isTerm && tree.nonEmpty => Some(tree :: Nil) + case _ => None + } + } + + object SyntacticFunction extends SyntacticFunctionExtractor { + def apply(params: List[Tree], body: Tree): Function = { + val params0 :: Nil = mkParam(params :: Nil, PARAM) + require(params0.forall { _.rhs.isEmpty }, "anonymous functions don't support parameters with default values") + Function(params0, body) + } + + def unapply(tree: Function): Option[(List[ValDef], Tree)] = Function.unapply(tree) + } + + object SyntacticNew extends SyntacticNewExtractor { + def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): Tree = + gen.mkNew(parents, mkSelfType(selfType), earlyDefs ::: body, NoPosition, NoPosition) + + def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case treeInfo.Applied(Select(New(SyntacticAppliedType(ident, targs)), nme.CONSTRUCTOR), Nil, List(Nil)) => + Some((Nil, SyntacticAppliedType(ident, targs) :: Nil, noSelfType, Nil)) + case treeInfo.Applied(Select(New(SyntacticAppliedType(ident, targs)), nme.CONSTRUCTOR), Nil, argss) => + Some((Nil, SyntacticApplied(SyntacticAppliedType(ident, targs), argss) :: Nil, noSelfType, Nil)) + case SyntacticBlock(SyntacticClassDef(_, tpnme.ANON_CLASS_NAME, Nil, _, ListOfNil, earlyDefs, parents, selfType, body) :: + Apply(Select(New(Ident(tpnme.ANON_CLASS_NAME)), nme.CONSTRUCTOR), Nil) :: Nil) => + Some((earlyDefs, parents, selfType, body)) + case _ => + None + } + } + + object SyntacticDefDef extends SyntacticDefDefExtractor { + def apply(mods: Modifiers, name: TermName, tparams: List[Tree], + vparamss: List[List[Tree]], tpt: Tree, rhs: Tree): DefDef = { + val tparams0 = mkTparams(tparams) + val vparamss0 = mkParam(vparamss, PARAM) + val rhs0 = { + if (name != nme.CONSTRUCTOR) rhs + else rhs match { + case Block(_, _) => rhs + case _ => Block(List(rhs), gen.mkSyntheticUnit) + } + } + DefDef(mods, name, tparams0, vparamss0, tpt, rhs0) + } + + def unapply(tree: Tree): Option[(Modifiers, TermName, List[TypeDef], List[List[ValDef]], Tree, Tree)] = tree match { + case DefDef(mods, nme.CONSTRUCTOR, tparams, vparamss, tpt, Block(List(expr), Literal(Constant(())))) => + Some((mods, nme.CONSTRUCTOR, tparams, vparamss, tpt, expr)) + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + Some((mods, name, tparams, vparamss, tpt, rhs)) + case _ => None + } + } + + protected class SyntacticValDefBase(isMutable: Boolean) extends SyntacticValDefExtractor { + def modifiers(mods: Modifiers): Modifiers = if (isMutable) mods | MUTABLE else mods + + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = ValDef(modifiers(mods), name, tpt, rhs) + + def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match { + case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable => + Some((mods, name, tpt, rhs)) + case _ => + None + } + } + object SyntacticValDef extends SyntacticValDefBase(isMutable = false) + object SyntacticVarDef extends SyntacticValDefBase(isMutable = true) + + object SyntacticAssign extends SyntacticAssignExtractor { + def apply(lhs: Tree, rhs: Tree): Tree = gen.mkAssign(lhs, rhs) + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case Assign(lhs, rhs) => Some((lhs, rhs)) + case AssignOrNamedArg(lhs, rhs) => Some((lhs, rhs)) + case Apply(Select(fn, nme.update), args :+ rhs) => Some((atPos(fn.pos)(Apply(fn, args)), rhs)) + case _ => None + } + } + + def UnliftListElementwise[T](unliftable: Unliftable[T]) = new UnliftListElementwise[T] { + def unapply(lst: List[Tree]): Option[List[T]] = { + val unlifted = lst.flatMap { unliftable.unapply(_) } + if (unlifted.length == lst.length) Some(unlifted) else None + } + } + + def UnliftListOfListsElementwise[T](unliftable: Unliftable[T]) = new UnliftListOfListsElementwise[T] { + def unapply(lst: List[List[Tree]]): Option[List[List[T]]] = { + val unlifted = lst.map { l => l.flatMap { unliftable.unapply(_) } } + if (unlifted.flatten.length == lst.flatten.length) Some(unlifted) else None + } + } + + object SyntacticValFrom extends SyntacticValFromExtractor { + def apply(pat: Tree, rhs: Tree): Tree = gen.ValFrom(pat, gen.mkCheckIfRefutable(pat, rhs)) + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case gen.ValFrom(pat, UnCheckIfRefutable(pat1, rhs1)) if pat.equalsStructure(pat1) => + Some((pat, rhs1)) + case gen.ValFrom(pat, rhs) => + Some((pat, rhs)) + case _ => None + } + } + + object SyntacticValEq extends SyntacticValEqExtractor { + def apply(pat: Tree, rhs: Tree): Tree = gen.ValEq(pat, rhs) + def unapply(tree: Tree): Option[(Tree, Tree)] = gen.ValEq.unapply(tree) + } + + object SyntacticFilter extends SyntacticFilterExtractor { + def apply(tree: Tree): Tree = gen.Filter(tree) + def unapply(tree: Tree): Option[Tree] = gen.Filter.unapply(tree) + } + + // If a tree in type position isn't provided by the user (e.g. `tpt` fields of + // `ValDef` and `DefDef`, function params etc), then it's going to be parsed as + // TypeTree with empty original and empty tpe. This extractor matches such trees + // so that one can write q"val x = 2" to match typecheck(q"val x = 2"). Note that + // TypeTree() is the only possible representation for empty trees in type positions. + // We used to sometimes receive EmptyTree in such cases, but not anymore. + object SyntacticEmptyTypeTree extends SyntacticEmptyTypeTreeExtractor { + def apply(): TypeTree = self.TypeTree() + def unapply(tt: TypeTree): Boolean = tt.original == null || tt.original.isEmpty + } + + // match a sequence of desugared `val $pat = $value` + protected object UnPatSeq { + def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = { + val imploded = implodePatDefs(trees) + val patvalues = imploded.flatMap { + case SyntacticPatDef(_, pat, EmptyTree, rhs) => Some((pat, rhs)) + case ValDef(_, name, SyntacticEmptyTypeTree(), rhs) => Some((Bind(name, self.Ident(nme.WILDCARD)), rhs)) + case ValDef(_, name, tpt, rhs) => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), rhs)) + case _ => None + } + if (patvalues.length == imploded.length) Some(patvalues) else None + } + } + + // implode multiple-statement desugaring of pattern definitions + // into single-statement valdefs with nme.QUASIQUOTE_PAT_DEF name + object implodePatDefs extends Transformer { + override def transform(tree: Tree) = tree match { + case templ: Template => deriveTemplate(templ)(transformStats) + case block: Block => + val Block(init, last) = block + Block(transformStats(init), transform(last)).copyAttrs(block) + case ValDef(mods, name1, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) + if name1 == name2 => + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) + case _ => + super.transform(tree) + } + def transformStats(trees: List[Tree]): List[Tree] = trees match { + case Nil => Nil + case ValDef(mods, _, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail + if mods.hasFlag(SYNTHETIC) && mods.hasFlag(ARTIFACT) => + ids match { + case Nil => + ValDef(NoMods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail) + case _ => + val mods = tail.take(1).head.asInstanceOf[ValDef].mods + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail.drop(ids.length)) + } + case other :: tail => + transform(other) :: transformStats(tail) + } + def apply(tree: Tree) = transform(tree) + def apply(trees: List[Tree]) = transformStats(trees) + } + + object SyntacticPatDef extends SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] = tpt match { + case SyntacticEmptyTypeTree() => gen.mkPatDef(mods, pat, rhs) + case _ => gen.mkPatDef(mods, Typed(pat, tpt), rhs) + } + def unapply(tree: Tree): Option[(Modifiers, Tree, Tree, Tree)] = tree match { + case ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), rhs) => Some((mods, pat, tpt, rhs)) + case _ => None + } + } + + // match a sequence of desugared `val $pat = $value` with a tuple in the end + protected object UnPatSeqWithRes { + def unapply(tree: Tree): Option[(List[(Tree, Tree)], List[Tree])] = tree match { + case SyntacticBlock(UnPatSeq(trees) :+ SyntacticTuple(elems)) => Some((trees, elems)) + case _ => None + } + } + + // undo gen.mkSyntheticParam + protected object UnSyntheticParam { + def unapply(tree: Tree): Option[TermName] = tree match { + case ValDef(mods, name, _, EmptyTree) + if mods.hasFlag(SYNTHETIC) && mods.hasFlag(PARAM) => + Some(name) + case _ => None + } + } + + // undo gen.mkVisitor + protected object UnVisitor { + def unapply(tree: Tree): Option[(TermName, List[CaseDef])] = tree match { + case Function(UnSyntheticParam(x1) :: Nil, Match(MaybeUnchecked(Ident(x2)), cases)) + if x1 == x2 => + Some((x1, cases)) + case _ => None + } + } + + // undo gen.mkFor:makeClosure + protected object UnClosure { + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case Function(ValDef(Modifiers(PARAM, _, _), name, tpt, EmptyTree) :: Nil, body) => + tpt match { + case SyntacticEmptyTypeTree() => Some((Bind(name, self.Ident(nme.WILDCARD)), body)) + case _ => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), body)) + } + case UnVisitor(_, CaseDef(pat, EmptyTree, body) :: Nil) => + Some((pat, body)) + case _ => None + } + } + + // match call to either withFilter or filter + protected object FilterCall { + def unapply(tree: Tree): Option[(Tree,Tree)] = tree match { + case Apply(Select(obj, nme.withFilter | nme.filter), arg :: Nil) => + Some(obj, arg) + case _ => None + } + } + + // transform a chain of withFilter calls into a sequence of for filters + protected object UnFilter { + def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match { + case UnCheckIfRefutable(_, _) => + Some((tree, Nil)) + case FilterCall(UnFilter(rhs, rest), UnClosure(_, test)) => + Some((rhs, rest :+ SyntacticFilter(test))) + case _ => + Some((tree, Nil)) + } + } + + // undo gen.mkCheckIfRefutable + protected object UnCheckIfRefutable { + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case FilterCall(rhs, UnVisitor(name, + CaseDef(pat, EmptyTree, Literal(Constant(true))) :: + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) :: Nil)) + if name.toString.contains(nme.CHECK_IF_REFUTABLE_STRING) => + Some((pat, rhs)) + case _ => None + } + } + + // undo gen.mkFor:makeCombination accounting for possible extra implicit argument + protected class UnForCombination(name: TermName) { + def unapply(tree: Tree) = tree match { + case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: Nil) + if name == meth && sel.hasAttachment[ForAttachment.type] => + Some(lhs, f) + case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: _ :: Nil) + if name == meth && sel.hasAttachment[ForAttachment.type] => + Some(lhs, f) + case _ => None + } + } + protected object UnMap extends UnForCombination(nme.map) + protected object UnForeach extends UnForCombination(nme.foreach) + protected object UnFlatMap extends UnForCombination(nme.flatMap) + + // undo desugaring done in gen.mkFor + protected object UnFor { + def unapply(tree: Tree): Option[(List[Tree], Tree)] = { + val interm = tree match { + case UnFlatMap(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) => + Some(((pat, rhs), filters ::: rest, body)) + case UnForeach(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) => + Some(((pat, rhs), filters ::: rest, body)) + case UnMap(UnFilter(rhs, filters), UnClosure(pat, cbody)) => + Some(((pat, rhs), filters, gen.Yield(cbody))) + case UnForeach(UnFilter(rhs, filters), UnClosure(pat, cbody)) => + Some(((pat, rhs), filters, cbody)) + case _ => None + } + interm.flatMap { + case ((Bind(_, SyntacticTuple(_)) | SyntacticTuple(_), + UnFor(SyntacticValFrom(pat, rhs) :: innerRest, gen.Yield(UnPatSeqWithRes(pats, elems2)))), + outerRest, fbody) => + val valeqs = pats.map { case (pat, rhs) => SyntacticValEq(pat, rhs) } + Some((SyntacticValFrom(pat, rhs) :: innerRest ::: valeqs ::: outerRest, fbody)) + case ((pat, rhs), filters, body) => + Some((SyntacticValFrom(pat, rhs) :: filters, body)) + } + } + } + + // check that enumerators are valid + protected def mkEnumerators(enums: List[Tree]): List[Tree] = { + require(enums.nonEmpty, "enumerators can't be empty") + enums.head match { + case SyntacticValFrom(_, _) => + case t => throw new IllegalArgumentException(s"$t is not a valid fist enumerator of for loop") + } + enums.tail.foreach { + case SyntacticValEq(_, _) | SyntacticValFrom(_, _) | SyntacticFilter(_) => + case t => throw new IllegalArgumentException(s"$t is not a valid representation of a for loop enumerator") + } + enums + } + + object SyntacticFor extends SyntacticForExtractor { + def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), body) + def unapply(tree: Tree) = tree match { + case UnFor(enums, gen.Yield(body)) => None + case UnFor(enums, body) => Some((enums, body)) + case _ => None + } + } + + object SyntacticForYield extends SyntacticForExtractor { + def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), gen.Yield(body)) + def unapply(tree: Tree) = tree match { + case UnFor(enums, gen.Yield(body)) => Some((enums, body)) + case _ => None + } + } + + // use typetree's original instead of typetree itself + protected object MaybeTypeTreeOriginal { + def unapply(tree: Tree): Some[Tree] = tree match { + case tt: TypeTree => Some(tt.original) + case _ => Some(tree) + } + } + + // drop potential extra call to .apply + protected object MaybeSelectApply { + def unapply(tree: Tree): Some[Tree] = tree match { + case Select(f, nme.apply) => Some(f) + case other => Some(other) + } + } + + // drop potential @scala.unchecked annotation + protected object MaybeUnchecked { + def unapply(tree: Tree): Some[Tree] = tree match { + case Annotated(SyntacticNew(Nil, ScalaDot(tpnme.unchecked) :: Nil, noSelfType, Nil), annottee) => + Some(annottee) + case Typed(annottee, MaybeTypeTreeOriginal( + Annotated(SyntacticNew(Nil, ScalaDot(tpnme.unchecked) :: Nil, noSelfType, Nil), _))) => + Some(annottee) + case annottee => Some(annottee) + } + } + + protected object MaybeTyped { + def unapply(tree: Tree): Some[(Tree, Tree)] = tree match { + case Typed(v, tpt) => Some((v, tpt)) + case v => Some((v, SyntacticEmptyTypeTree())) + } + } + + protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map { + case c: CaseDef => c + case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case") + } + + object SyntacticPartialFunction extends SyntacticPartialFunctionExtractor { + def apply(cases: List[Tree]): Match = Match(EmptyTree, mkCases(cases)) + def unapply(tree: Tree): Option[List[CaseDef]] = tree match { + case Match(EmptyTree, cases) => Some(cases) + case Typed( + Block( + List(ClassDef(clsMods, tpnme.ANON_FUN_NAME, Nil, Template( + List(abspf: TypeTree, ser: TypeTree), noSelfType, List( + DefDef(_, nme.CONSTRUCTOR, _, _, _, _), + DefDef(_, nme.applyOrElse, _, _, _, + Match(_, cases :+ + CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), _, _))), + DefDef(_, nme.isDefinedAt, _, _, _, _))))), + Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), termNames.CONSTRUCTOR), List())), + pf: TypeTree) + if pf.tpe != null && pf.tpe.typeSymbol.eq(PartialFunctionClass) && + abspf.tpe != null && abspf.tpe.typeSymbol.eq(AbstractPartialFunctionClass) && + ser.tpe != null && ser.tpe.typeSymbol.eq(SerializableClass) && + clsMods.hasFlag(FINAL) && clsMods.hasFlag(SYNTHETIC) => + Some(cases) + case _ => None + } + } + + object SyntacticMatch extends SyntacticMatchExtractor { + def apply(scrutinee: Tree, cases: List[Tree]) = { + require(scrutinee.nonEmpty, "match's scrutinee may not be empty") + Match(scrutinee, mkCases(cases)) + } + + def unapply(tree: Match): Option[(Tree, List[CaseDef])] = tree match { + case Match(scrutinee, cases) if scrutinee.nonEmpty => Some((scrutinee, cases)) + case _ => None + } + } + + object SyntacticTry extends SyntacticTryExtractor { + def apply(block: Tree, catches: List[Tree], finalizer: Tree) = Try(block, mkCases(catches), finalizer) + def unapply(tree: Try): Option[(Tree, List[CaseDef], Tree)] = Try.unapply(tree) + } + + object SyntacticTermIdent extends SyntacticTermIdentExtractor { + def apply(name: TermName, isBackquoted: Boolean): Ident = { + val id = self.Ident(name) + if (isBackquoted) id updateAttachment BackquotedIdentifierAttachment + id + } + def unapply(id: Ident): Option[(TermName, Boolean)] = id.name match { + case name: TermName => Some((name, id.hasAttachment[BackquotedIdentifierAttachment.type])) + case _ => None + } + } + + object SyntacticTypeIdent extends SyntacticTypeIdentExtractor { + def apply(name: TypeName): Ident = self.Ident(name) + def unapply(tree: Tree): Option[TypeName] = tree match { + case MaybeTypeTreeOriginal(Ident(name: TypeName)) => Some(name) + case _ => None + } + } + + /** Facade over Imports and ImportSelectors that lets to structurally + * deconstruct/reconstruct them. + * + * Selectors are represented in the following way: + * 1. q"import foo._" <==> q"import foo.${pq"_"}" + * 2. q"import foo.bar" <==> q"import foo.${pq"bar"}" + * 3. q"import foo.{bar => baz}" <==> q"import foo.${pq"bar -> baz"}" + * 4. q"import foo.{bar => _}" <==> q"import foo.${pq"bar -> _"}" + * + * All names in selectors are TermNames despite the fact ImportSelector + * can theoretically contain TypeNames too (but they never do in practice.) + */ + object SyntacticImport extends SyntacticImportExtractor { + // construct/deconstruct {_} import selector + private object WildcardSelector { + def apply(offset: Int): ImportSelector = ImportSelector(nme.WILDCARD, offset, null, -1) + def unapply(sel: ImportSelector): Option[Int] = sel match { + case ImportSelector(nme.WILDCARD, offset, null, -1) => Some(offset) + case _ => None + } + } + + // construct/deconstruct {foo} import selector + private object NameSelector { + def apply(name: TermName, offset: Int): ImportSelector = ImportSelector(name, offset, name, offset) + def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match { + case ImportSelector(name1, offset1, name2, offset2) if name1 == name2 && offset1 == offset2 => + Some((name1.toTermName, offset1)) + case _ => + None + } + } + + // construct/deconstruct {foo => bar} import selector + private object RenameSelector { + def apply(name1: TermName, offset1: Int, name2: TermName, offset2: Int): ImportSelector = + ImportSelector(name1, offset1, name2, offset2) + def unapply(sel: ImportSelector): Option[(TermName, Int, TermName, Int)] = sel match { + case ImportSelector(_, _, null | nme.WILDCARD, _) => + None + case ImportSelector(name1, offset1, name2, offset2) if name1 != name2 => + Some((name1.toTermName, offset1, name2.toTermName, offset2)) + case _ => + None + } + } + + // construct/deconstruct {foo => _} import selector + private object UnimportSelector { + def apply(name: TermName, offset: Int): ImportSelector = + ImportSelector(name, offset, nme.WILDCARD, -1) + def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match { + case ImportSelector(name, offset, nme.WILDCARD, _) => Some((name.toTermName, offset)) + case _ => None + } + } + + // represent {_} import selector as pq"_" + private object WildcardSelectorRepr { + def apply(pos: Position): Tree = atPos(pos)(self.Ident(nme.WILDCARD)) + def unapply(tree: Tree): Option[Position] = tree match { + case self.Ident(nme.WILDCARD) => Some(tree.pos) + case _ => None + } + } + + // represent {foo} import selector as pq"foo" + private object NameSelectorRepr { + def apply(name: TermName, pos: Position): Tree = atPos(pos)(Bind(name, WildcardSelectorRepr(pos))) + def unapply(tree: Tree): Option[(TermName, Position)] = tree match { + case Bind(name, WildcardSelectorRepr(_)) => Some((name.toTermName, tree.pos)) + case _ => None + } + } + + // pq"left -> right" + private object Arrow { + def apply(left: Tree, right: Tree): Apply = + Apply(self.Ident(nme.MINGT), left :: right :: Nil) + def unapply(tree: Apply): Option[(Tree, Tree)] = tree match { + case Apply(self.Ident(nme.MINGT), left :: right :: Nil) => Some((left, right)) + case _ => None + } + } + + // represent {foo => bar} import selector as pq"foo -> bar" + private object RenameSelectorRepr { + def apply(name1: TermName, pos1: Position, name2: TermName, pos2: Position): Tree = { + val left = NameSelectorRepr(name1, pos1) + val right = NameSelectorRepr(name2, pos2) + atPos(wrappingPos(left :: right :: Nil))(Arrow(left, right)) + } + def unapply(tree: Tree): Option[(TermName, Position, TermName, Position)] = tree match { + case Arrow(NameSelectorRepr(name1, pos1), NameSelectorRepr(name2, pos2)) => + Some((name1.toTermName, pos1, name2.toTermName, pos2)) + case _ => + None + } + } + + // represent {foo => _} import selector as pq"foo -> _" + private object UnimportSelectorRepr { + def apply(name: TermName, pos: Position): Tree = + atPos(pos)(Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(pos))) + def unapply(tree: Tree): Option[(TermName, Position)] = tree match { + case Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(_)) => + Some((name, pos)) + case _ => + None + } + } + + private def derivedPos(t: Tree, offset: Int): Position = + if (t.pos == NoPosition) NoPosition else t.pos.withPoint(offset) + + private def derivedOffset(pos: Position): Int = + if (pos == NoPosition) -1 else pos.point + + def apply(expr: Tree, selectors: List[Tree]): Import = { + val importSelectors = selectors.map { + case WildcardSelectorRepr(pos) => WildcardSelector(derivedOffset(pos)) + case NameSelectorRepr(name, pos) => NameSelector(name, derivedOffset(pos)) + case RenameSelectorRepr(name1, pos1, name2, pos2) => RenameSelector(name1, derivedOffset(pos1), name2, derivedOffset(pos2)) + case UnimportSelectorRepr(name, pos) => UnimportSelector(name, derivedOffset(pos)) + case tree => throw new IllegalArgumentException(s"${showRaw(tree)} doesn't correspond to import selector") + } + Import(expr, importSelectors) + } + + def unapply(imp: Import): Some[(Tree, List[Tree])] = { + val selectors = imp.selectors.map { + case WildcardSelector(offset) => WildcardSelectorRepr(derivedPos(imp, offset)) + case NameSelector(name, offset) => NameSelectorRepr(name, derivedPos(imp, offset)) + case RenameSelector(name1, offset1, name2, offset2) => RenameSelectorRepr(name1, derivedPos(imp, offset1), name2, derivedPos(imp, offset2)) + case UnimportSelector(name, offset) => UnimportSelectorRepr(name, derivedPos(imp, offset)) + } + Some((imp.expr, selectors)) + } + } + + object SyntacticSelectType extends SyntacticSelectTypeExtractor { + def apply(qual: Tree, name: TypeName): Select = Select(qual, name) + def unapply(tree: Tree): Option[(Tree, TypeName)] = tree match { + case MaybeTypeTreeOriginal(Select(qual, name: TypeName)) => Some((qual, name)) + case _ => None + } + } + + object SyntacticSelectTerm extends SyntacticSelectTermExtractor { + def apply(qual: Tree, name: TermName): Select = Select(qual, name) + def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { + case Select(qual, name: TermName) => Some((qual, name)) + case _ => None + } + } + + object SyntacticCompoundType extends SyntacticCompoundTypeExtractor { + def apply(parents: List[Tree], defns: List[Tree]) = + CompoundTypeTree(Template(gen.mkParents(NoMods, parents), noSelfType, defns)) + def unapply(tree: Tree): Option[(List[Tree], List[Tree])] = tree match { + case MaybeTypeTreeOriginal(CompoundTypeTree(Template(parents, _, defns))) => + Some((parents, defns)) + case _ => + None + } + } + + object SyntacticSingletonType extends SyntacitcSingletonTypeExtractor { + def apply(ref: Tree): SingletonTypeTree = SingletonTypeTree(ref) + def unapply(tree: Tree): Option[Tree] = tree match { + case MaybeTypeTreeOriginal(SingletonTypeTree(ref)) => + Some(ref) + case _ => + None + } + } + + object SyntacticTypeProjection extends SyntacticTypeProjectionExtractor { + def apply(qual: Tree, name: TypeName): SelectFromTypeTree = + SelectFromTypeTree(qual, name) + def unapply(tree: Tree): Option[(Tree, TypeName)] = tree match { + case MaybeTypeTreeOriginal(SelectFromTypeTree(qual, name)) => + Some((qual, name)) + case _ => + None + } + } + + object SyntacticAnnotatedType extends SyntacticAnnotatedTypeExtractor { + def apply(tpt: Tree, annot: Tree): Annotated = + Annotated(annot, tpt) + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case MaybeTypeTreeOriginal(Annotated(annot, tpt)) => + Some((tpt, annot)) + case _ => + None + } + } + + object SyntacticExistentialType extends SyntacticExistentialTypeExtractor { + def apply(tpt: Tree, where: List[Tree]): ExistentialTypeTree = + ExistentialTypeTree(tpt, where.map { + case md: MemberDef => md + case tree => throw new IllegalArgumentException("$tree is not legal forSome definition") + }) + def unapply(tree: Tree): Option[(Tree, List[MemberDef])] = tree match { + case MaybeTypeTreeOriginal(ExistentialTypeTree(tpt, where)) => + Some((tpt, where)) + case _ => + None + } + } + } + + val build = new ReificationSupportImpl +} diff --git a/src/reflect/scala/reflect/internal/Required.scala b/src/reflect/scala/reflect/internal/Required.scala index 842491d56d..009bc39d4c 100644 --- a/src/reflect/scala/reflect/internal/Required.scala +++ b/src/reflect/scala/reflect/internal/Required.scala @@ -1,15 +1,16 @@ -package scala.reflect +package scala +package reflect package internal import settings.MutableSettings trait Required { self: SymbolTable => - def picklerPhase: Phase - def settings: MutableSettings + def erasurePhase: Phase - def forInteractive: Boolean + def settings: MutableSettings - def forScaladoc: Boolean + @deprecated("Interactive is implemented with a custom Global; this flag is ignored", "2.11.0") def forInteractive = false + @deprecated("Scaladoc is implemented with a custom Global; this flag is ignored", "2.11.0") def forScaladoc = false } diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index ab3b9b7ed7..103f885ad4 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -3,11 +3,22 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal +import scala.annotation.tailrec + trait Scopes extends api.Scopes { self: SymbolTable => + /** An ADT to represent the results of symbol name lookups. + */ + sealed trait NameLookup { def symbol: Symbol ; def isSuccess = false } + case class LookupSucceeded(qualifier: Tree, symbol: Symbol) extends NameLookup { override def isSuccess = true } + case class LookupAmbiguous(msg: String) extends NameLookup { def symbol = NoSymbol } + case class LookupInaccessible(symbol: Symbol, msg: String) extends NameLookup + case object LookupNotFound extends NameLookup { def symbol = NoSymbol } + class ScopeEntry(val sym: Symbol, val owner: Scope) { /** the next entry in the hash bucket */ @@ -17,15 +28,11 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ var next: ScopeEntry = null + def depth = owner.nestingLevel override def hashCode(): Int = sym.name.start - override def toString(): String = sym.toString() + override def toString() = s"$sym (depth=$depth)" } - /** - * @param sym ... - * @param owner ... - * @return ... - */ private def newScopeEntry(sym: Symbol, owner: Scope): ScopeEntry = { val e = new ScopeEntry(sym, owner) e.next = owner.elems @@ -41,26 +48,26 @@ trait Scopes extends api.Scopes { self: SymbolTable => * This is necessary because when run from reflection every scope needs to have a * SynchronizedScope as mixin. */ - class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends ScopeApi with MemberScopeApi { - - protected[Scopes] def this(base: Scope) = { - this(base.elems) - nestinglevel = base.nestinglevel + 1 - } + class Scope protected[Scopes]() extends ScopeApi with MemberScopeApi { - private[scala] var elems: ScopeEntry = initElems + private[scala] var elems: ScopeEntry = _ /** The number of times this scope is nested in another */ - private var nestinglevel = 0 + private[Scopes] var nestinglevel = 0 /** the hash table */ - private var hashtable: Array[ScopeEntry] = null + private[Scopes] var hashtable: Array[ScopeEntry] = null /** a cache for all elements, to be used by symbol iterator. */ private var elemsCache: List[Symbol] = null + private var cachedSize = -1 + private def flushElemsCache() { + elemsCache = null + cachedSize = -1 + } /** size and mask of hash tables * todo: make hashtables grow? @@ -72,8 +79,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ 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: _*) @@ -82,6 +87,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** the number of entries in this scope */ override def size: Int = { + if (cachedSize < 0) + cachedSize = directSize + + cachedSize + } + private def directSize: Int = { var s = 0 var e = elems while (e ne null) { @@ -92,11 +103,9 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** enter a scope entry - * - * @param e ... */ protected def enterEntry(e: ScopeEntry) { - elemsCache = null + flushElemsCache() if (hashtable ne null) enterInHash(e) else if (size >= MIN_HASH) @@ -110,8 +119,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** enter a symbol - * - * @param sym ... */ def enter[T <: Symbol](sym: T): T = { enterEntry(newScopeEntry(sym, this)) @@ -119,14 +126,18 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** 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) } + def enterIfNew[T <: Symbol](sym: T): T = { + val existing = lookupEntry(sym.name) + if (existing == null) enter(sym) + else existing.sym.asInstanceOf[T] + } + private def createHash() { hashtable = new Array[ScopeEntry](HASHSIZE) enterAllInHash(elems) @@ -175,8 +186,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** remove entry - * - * @param e ... */ def unlink(e: ScopeEntry) { if (elems == e) { @@ -192,30 +201,64 @@ trait Scopes extends api.Scopes { self: SymbolTable => if (e1 == e) { hashtable(index) = e.tail } else { - while (e1.tail != e) e1 = e1.tail; + while (e1.tail != e) e1 = e1.tail e1.tail = e.tail } } - elemsCache = null + flushElemsCache() } /** remove symbol */ def unlink(sym: Symbol) { var e = lookupEntry(sym.name) while (e ne null) { - if (e.sym == sym) unlink(e); + if (e.sym == sym) unlink(e) e = lookupNextEntry(e) } } - /** lookup a symbol - * - * @param name ... - * @return ... + /** Lookup a module or a class, filtering out matching names in scope + * which do not match that requirement. + */ + def lookupModule(name: Name): Symbol = findSymbol(lookupAll(name.toTermName))(_.isModule) + def lookupClass(name: Name): Symbol = findSymbol(lookupAll(name.toTypeName))(_.isClass) + + /** True if the name exists in this scope, false otherwise. */ + def containsName(name: Name) = lookupEntry(name) != null + + /** Lookup a symbol. */ def lookup(name: Name): Symbol = { val e = lookupEntry(name) - if (e eq null) NoSymbol else e.sym + if (e eq null) NoSymbol + else if (lookupNextEntry(e) eq null) e.sym + else { + // We shouldn't get here: until now this method was picking a random + // symbol when there was more than one with the name, so this should + // only be called knowing that there are 0-1 symbols of interest. So, we + // can safely return an overloaded symbol rather than throwing away the + // rest of them. Most likely we still break, but at least we will break + // in an understandable fashion (unexpectedly overloaded symbol) rather + // than a non-deterministic bizarre one (see any bug involving overloads + // in package objects.) + val alts = lookupAll(name).toList + def alts_s = alts map (s => s.defString) mkString " <and> " + devWarning(s"scope lookup of $name found multiple symbols: $alts_s") + // FIXME - how is one supposed to create an overloaded symbol without + // knowing the correct owner? Using the symbol owner is not correct; + // say for instance this is List's scope and the symbols are its three + // mkString members. Those symbols are owned by TraversableLike, which + // is no more meaningful an owner than NoSymbol given that we're in + // List. Maybe it makes no difference who owns the overloaded symbol, in + // which case let's establish that and have a canonical creation method. + // + // FIXME - a similar question for prefix, although there are more + // clues from the symbols on that one, as implemented here. In general + // the distinct list is one type and lub becomes the identity. + // val prefix = lub(alts map (_.info.prefix) distinct) + // Now using NoSymbol and NoPrefix always to avoid forcing info (SI-6664) + NoSymbol.newOverloaded(NoPrefix, alts) + } } /** Returns an iterator yielding every symbol with given name in this scope. @@ -223,7 +266,20 @@ trait Scopes extends api.Scopes { self: SymbolTable => 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 } + def next(): Symbol = try e.sym finally e = lookupNextEntry(e) + } + + def lookupAllEntries(name: Name): Iterator[ScopeEntry] = new Iterator[ScopeEntry] { + var e = lookupEntry(name) + def hasNext: Boolean = e ne null + def next(): ScopeEntry = try e finally e = lookupNextEntry(e) + } + + def lookupUnshadowedEntries(name: Name): Iterator[ScopeEntry] = { + lookupEntry(name) match { + case null => Iterator.empty + case e => lookupAllEntries(name) filter (e1 => (e eq e1) || (e.depth == e1.depth && e.sym != e1.sym)) + } } /** lookup a symbol entry matching given name. @@ -257,20 +313,47 @@ trait Scopes extends api.Scopes { self: SymbolTable => 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); + do { e = e.next } while ((e ne null) && e.sym.name != entry.sym.name) e } + /** TODO - we can test this more efficiently than checking isSubScope + * in both directions. However the size test might be enough to quickly + * rule out most failures. + */ + def isSameScope(other: Scope) = ( + (size == other.size) // optimization - size is cached + && (this isSubScope other) + && (other isSubScope this) + ) + + def isSubScope(other: Scope) = { + def scopeContainsSym(sym: Symbol): Boolean = { + @tailrec def entryContainsSym(e: ScopeEntry): Boolean = e match { + case null => false + case _ => + val comparableInfo = sym.info.substThis(sym.owner, e.sym.owner) + (e.sym.info =:= comparableInfo) || entryContainsSym(lookupNextEntry(e)) + } + entryContainsSym(this lookupEntry sym.name) + } + other.toList forall scopeContainsSym + } + /** 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 symbols: List[Symbol] = Nil + var count = 0 var e = elems while ((e ne null) && e.owner == this) { - elemsCache = e.sym :: elemsCache + count += 1 + symbols ::= e.sym e = e.next } + elemsCache = symbols + cachedSize = count } elemsCache } @@ -287,37 +370,17 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ 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 - - @deprecated("Use `toList.reverse` instead", "2.10.0") + override def filterNot(p: Symbol => Boolean): Scope = ( + if (toList exists p) newScopeWith(toList filterNot p: _*) + else this + ) + override def filter(p: Symbol => Boolean): Scope = ( + if (toList forall p) this + else newScopeWith(toList filter p: _*) + ) + @deprecated("Use `toList.reverse` instead", "2.10.0") // Used in SBT 0.12.4 def reverse: List[Symbol] = toList.reverse override def mkString(start: String, sep: String, end: String) = @@ -365,7 +428,14 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** Create a new scope nested in another one with which it shares its elements */ - def newNestedScope(outer: Scope): Scope = new Scope(outer) + final def newNestedScope(outer: Scope): Scope = { + val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes! + nested.elems = outer.elems + nested.nestinglevel = outer.nestinglevel + 1 + if (outer.hashtable ne null) + nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length) + nested + } /** Create a new scope with given initial elements */ def newScopeWith(elems: Symbol*): Scope = { @@ -396,6 +466,4 @@ trait Scopes extends api.Scopes { self: SymbolTable => 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 index 539d19140c..614e71b597 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal trait StdAttachments { @@ -13,6 +14,7 @@ trait StdAttachments { def setAttachments(attachments: scala.reflect.macros.Attachments { type Pos = Position }): this.type = { rawatt = attachments; this } def updateAttachment[T: ClassTag](attachment: T): this.type = { rawatt = rawatt.update(attachment); this } def removeAttachment[T: ClassTag]: this.type = { rawatt = rawatt.remove[T]; this } + def hasAttachment[T: ClassTag]: Boolean = rawatt.contains[T] // cannot be final due to SynchronizedSymbols def pos: Position = rawatt.pos @@ -20,9 +22,15 @@ trait StdAttachments { def setPos(newpos: Position): this.type = { pos = newpos; this } } - /** When present, indicates that the host `Ident` has been created from a backquoted identifier. - */ - case object BackquotedIdentifierAttachment + /** Attachment that knows how to import itself into another universe. */ + trait ImportableAttachment { + def importAttachment(importer: Importer): this.type + } + + /** Attachment that doesn't contain any reflection artificats and can be imported as-is. */ + trait PlainAttachment extends ImportableAttachment { + def importAttachment(importer: Importer): this.type = this + } /** Stores the trees that give rise to a refined type to be used in reification. * Unfortunately typed `CompoundTypeTree` is lacking essential info, and the reifier cannot use `CompoundTypeTree.tpe`. @@ -30,47 +38,18 @@ trait StdAttachments { */ case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) - /** Is added by the macro engine to the results of macro expansions. - * Stores the original expandee as it entered the `macroExpand` function. + /** When present, indicates that the host `Ident` has been created from a backquoted identifier. */ - case class MacroExpansionAttachment(original: Tree) + case object BackquotedIdentifierAttachment extends PlainAttachment - /** When present, suppresses macro expansion for the host. - * This is occasionally necessary, e.g. to prohibit eta-expansion of macros. - * - * Does not affect expandability of child nodes, there's context.withMacrosDisabled for that - * (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639). + /** Identifies trees are either result or intermidiate value of for loop desugaring. */ - case object SuppressMacroExpansionAttachment + case object ForAttachment extends PlainAttachment - /** Suppresses macro expansion of the tree by putting SuppressMacroExpansionAttachment on it. + /** Identifies unit constants which were inserted by the compiler (e.g. gen.mkBlock) */ - def suppressMacroExpansion(tree: Tree) = tree.updateAttachment(SuppressMacroExpansionAttachment) + case object SyntheticUnitAttachment extends PlainAttachment - /** Unsuppresses macro expansion of the tree by removing SuppressMacroExpansionAttachment from it and its children. - */ - def unsuppressMacroExpansion(tree: Tree): Tree = { - tree.removeAttachment[SuppressMacroExpansionAttachment.type] - tree match { - // see the comment to `isMacroExpansionSuppressed` to learn why we need - // a special traversal strategy here - case Apply(fn, _) => unsuppressMacroExpansion(fn) - case TypeApply(fn, _) => unsuppressMacroExpansion(fn) - case _ => // do nothing - } - tree - } - - /** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children. - */ - def isMacroExpansionSuppressed(tree: Tree): Boolean = - if (tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) true - else tree match { - // we have to account for the fact that during typechecking an expandee might become wrapped, - // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. - // in that case the expandee itself will no longer be suppressed and we need to look at the core - case Apply(fn, _) => isMacroExpansionSuppressed(fn) - case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) - case _ => false - } + /** Untyped list of subpatterns attached to selector dummy. */ + case class SubpatternsAttachment(patterns: List[Tree]) } diff --git a/src/reflect/scala/reflect/internal/StdCreators.scala b/src/reflect/scala/reflect/internal/StdCreators.scala index 5e5e4f9043..a0084dc95c 100644 --- a/src/reflect/scala/reflect/internal/StdCreators.scala +++ b/src/reflect/scala/reflect/internal/StdCreators.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal import scala.reflect.api.{TreeCreator, TypeCreator} diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c3b7f24a9d..6848c357c5 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -3,10 +3,12 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import java.security.MessageDigest +import java.util.UUID.randomUUID import Chars.isOperatorPart import scala.annotation.switch import scala.language.implicitConversions @@ -18,8 +20,6 @@ trait StdNames { 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 @@ -37,11 +37,7 @@ trait StdNames { kws = kws + result result } - def result: Set[TermName] = { - val result = kws - kws = null - result - } + def result: Set[TermName] = try kws finally kws = null } private[reflect] def compactifyName(orig: String): String = compactify(orig) @@ -57,14 +53,17 @@ trait StdNames { * * We obtain the formula: * - * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6 + * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + suffixLength * - * (+6 for ".class"). MaxNameLength can therefore be computed as follows: + * (+suffixLength for ".class" and potential module class suffix that is added *after* this transform). + * + * MaxNameLength can therefore be computed as follows: */ val marker = "$$$$" + val maxSuffixLength = "$.class".length + 1 // potential module class suffix and file extension val MaxNameLength = math.min( - settings.maxClassfileName.value - 6, - 2 * (settings.maxClassfileName.value - 6 - 2*marker.length - 32) + settings.maxClassfileName.value - maxSuffixLength, + 2 * (settings.maxClassfileName.value - maxSuffixLength - 2*marker.length - 32) ) def toMD5(s: String, edge: Int): String = { val prefix = s take edge @@ -93,8 +92,11 @@ trait StdNames { 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 NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING + val LOCAL_SUFFIX_STRING: String = NameTransformer.LOCAL_SUFFIX_STRING + val TRAIT_SETTER_SEPARATOR_STRING: String = NameTransformer.TRAIT_SETTER_SEPARATOR_STRING + val SINGLETON_SUFFIX: String = ".type" val ANON_CLASS_NAME: NameType = "$anon" @@ -105,7 +107,6 @@ trait StdNames { 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" @@ -122,20 +123,17 @@ trait StdNames { 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 WeakTypeTag: NameType = "WeakTypeTag" - final val TypeTag : NameType = "TypeTag" - final val Expr: NameType = "Expr" - final val String: NameType = "String" + 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 WeakTypeTag: NameType = "WeakTypeTag" + final val TypeTag : NameType = "TypeTag" + final val Expr: NameType = "Expr" + final val String: NameType = "String" + final val StringContext: NameType = "StringContext" // fictions we use as both types and terms final val ERROR: NameType = "<error>" @@ -207,10 +205,11 @@ trait StdNames { } abstract class TypeNames extends Keywords with TypeNamesApi { + override 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>" @@ -218,29 +217,45 @@ trait StdNames { final val WILDCARD_STAR: NameType = "_*" final val REIFY_TREECREATOR_PREFIX: NameType = "$treecreator" final val REIFY_TYPECREATOR_PREFIX: NameType = "$typecreator" + final val MACRO_BUNDLE_SUFFIX: NameType = "$Bundle" final val Any: NameType = "Any" final val AnyVal: NameType = "AnyVal" - final val ExprApi: NameType = "ExprApi" + final val FlagSet: NameType = "FlagSet" final val Mirror: NameType = "Mirror" + final val Modifiers: NameType = "Modifiers" 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 unchecked: NameType = "unchecked" + final val api: NameType = "api" final val Annotation: NameType = "Annotation" + final val CaseDef: NameType = "CaseDef" final val ClassfileAnnotation: NameType = "ClassfileAnnotation" final val ClassManifest: NameType = "ClassManifest" final val Enum: NameType = "Enum" final val Group: NameType = "Group" + final val implicitNotFound: NameType = "implicitNotFound" + final val Liftable: NameType = "Liftable" + final val Unliftable: NameType = "Unliftable" + final val Name: NameType = "Name" final val Tree: NameType = "Tree" + final val TermName: NameType = "TermName" final val Type : NameType = "Type" - final val TypeTree: NameType = "TypeTree" + final val TypeName: NameType = "TypeName" + final val TypeDef: NameType = "TypeDef" + final val Quasiquote: NameType = "Quasiquote" + + // quasiquote-specific names + final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$" + final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" + final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" @@ -250,22 +265,20 @@ trait StdNames { // 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" + final val scala_ : NameType = "scala" + 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 @@ -273,27 +286,30 @@ trait StdNames { } abstract class TermNames extends Keywords with TermNamesApi { + override 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$" + val BITMAP_PREFIX = "bitmap$" + val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$" + val DEFAULT_GETTER_STRING = "$default$" + val DEFAULT_GETTER_INIT_STRING = NameTransformer.encode("<init>") + DEFAULT_GETTER_STRING + val DO_WHILE_PREFIX = "doWhile$" + val EVIDENCE_PARAM_PREFIX = "evidence$" + val EXCEPTION_RESULT_PREFIX = "exceptionResult" + val EXPAND_SEPARATOR_STRING = "$$" + val FRESH_TERM_NAME_PREFIX = "x$" + val INTERPRETER_IMPORT_WRAPPER = "$iw" + 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 WHILE_PREFIX = "while$" + val FRESH_PREFIX = "fresh" + val FRESH_SUFFIX = "macro$" // uses a keyword to avoid collisions with mangled names + val QUAL_PREFIX = "qual$" + val NAMEDARG_PREFIX = "x$" // Compiler internal names val ANYname: NameType = "<anyname>" @@ -301,12 +317,9 @@ trait StdNames { val DEFAULT_CASE: NameType = "defaultCase$" 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_BUILD_PREFIX: NameType = "$u.internal.reificationSupport." val UNIVERSE_PREFIX: NameType = "$u." val UNIVERSE_SHORT: NameType = "$u" val MIRROR_PREFIX: NameType = "$m." @@ -316,24 +329,31 @@ trait StdNames { val REIFY_FREE_THIS_SUFFIX: NameType = "$this" val REIFY_FREE_VALUE_SUFFIX: NameType = "$value" val REIFY_SYMDEF_PREFIX: NameType = "symdef$" + val QUASIQUOTE_CASE: NameType = "$quasiquote$case$" + val QUASIQUOTE_EARLY_DEF: NameType = "$quasiquote$early$def$" + val QUASIQUOTE_FILE: String = "<quasiquote>" + val QUASIQUOTE_FOR_ENUM: NameType = "$quasiquote$for$enum$" + val QUASIQUOTE_NAME_PREFIX: String = "nn$" + val QUASIQUOTE_PACKAGE_STAT: NameType = "$quasiquote$package$stat$" + val QUASIQUOTE_PARAM: NameType = "$quasiquote$param$" + val QUASIQUOTE_PAT_DEF: NameType = "$quasiquote$pat$def$" + val QUASIQUOTE_PREFIX: String = "qq$" + val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" + val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" + val QUASIQUOTE_UNLIFT_HELPER: String = "$quasiquote$unlift$helper$" 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_LOCAL: NameType = OUTER.localName 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 SETTER_SUFFIX: NameType = NameTransformer.SETTER_SUFFIX_STRING 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 @@ -341,7 +361,6 @@ trait StdNames { 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) @@ -358,33 +377,32 @@ trait StdNames { ) } - 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 = + private def expandedNameInternal(name: TermName, base: Symbol, separator: String): TermName = newTermNameCached(base.fullName('$') + separator + name) + /** The expanded name of `name` relative to this class `base` + */ + def expandedName(name: TermName, base: Symbol) = expandedNameInternal(name, base, EXPAND_SEPARATOR_STRING) + /** 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) + def expandedSetterName(name: TermName, base: Symbol) = expandedNameInternal(name, base, TRAIT_SETTER_SEPARATOR_STRING) - /** If `name` is an expandedName name, the original name. - * Otherwise `name` itself. - */ - def originalName(name: Name): Name = name.toString lastIndexOf "$$" match { - case -1 | 0 => name - case idx0 => + /** If `name` is an expandedName name, the original (unexpanded) name. + * Otherwise `name` itself. + * Look backward from the end of the string for "$$", and take the + * part of the string after that; but if the string is "$$$" or longer, + * be sure to retain the extra dollars. + */ + def unexpandedName(name: Name): Name = name lastIndexOf "$$" match { + case 0 | -1 => name + case idx0 => // Sketchville - We've found $$ but if it's part of $$$ or $$$$ // or something we need to keep the bonus dollars, so e.g. foo$$$outer // has an original name of $outer. @@ -394,34 +412,38 @@ trait StdNames { name drop idx + 2 } + @deprecated("Use unexpandedName", "2.11.0") def originalName(name: Name): Name = unexpandedName(name) + @deprecated("Use Name#dropModule", "2.11.0") def stripModuleSuffix(name: Name): Name = name.dropModule + @deprecated("Use Name#dropLocal", "2.11.0") def localToGetter(name: TermName): TermName = name.dropLocal + @deprecated("Use Name#dropLocal", "2.11.0") def dropLocalSuffix(name: Name): TermName = name.dropLocal + @deprecated("Use Name#localName", "2.11.0") def getterToLocal(name: TermName): TermName = name.localName + @deprecated("Use Name#setterName", "2.11.0") def getterToSetter(name: TermName): TermName = name.setterName + @deprecated("Use Name#getterName", "2.11.0") def getterName(name: TermName): TermName = name.getterName + @deprecated("Use Name#getterName", "2.11.0") def setterToGetter(name: TermName): TermName = name.getterName + + /** + * Convert `Tuple2$mcII` to `Tuple2`, or `T1$sp` to `T1`. + */ def unspecializedName(name: Name): Name = ( + // DUPLICATED LOGIC WITH `splitSpecializedName` if (name endsWith SPECIALIZED_SUFFIX) - name.subName(0, name.lastIndexOf('m') - 1) + 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") + * splitSpecializedName("foo$mIcD$sp") == ('foo', "D", "I") * }}} * `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. + * + * @return (unspecializedName, class tparam specializations, method tparam specializations) */ def splitSpecializedName(name: Name): (Name, String, String) = + // DUPLICATED LOGIC WITH `unspecializedName` if (name endsWith SPECIALIZED_SUFFIX) { val name1 = name dropRight SPECIALIZED_SUFFIX.length val idxC = name1 lastIndexOf 'c' @@ -433,51 +455,23 @@ trait StdNames { } 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) - } + def defaultGetterName(name: Name, pos: Int): TermName = ( + if (isConstructorName(name)) + DEFAULT_GETTER_INIT_STRING + pos + else + name + 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.charAt(pos - 1).isDigit) - pos -= 1 - - if (pos <= 0 || pos == name.length || name.charAt(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 defaultGetterToMethod(name: Name): TermName = ( + if (name startsWith DEFAULT_GETTER_INIT_STRING) + nme.CONSTRUCTOR + else name indexOf DEFAULT_GETTER_STRING match { + case -1 => name.toTermName + case idx => name.toTermName take idx + } ) + def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">") def superName(name: Name): TermName = newTermName(SUPER_PREFIX_STRING + name) @@ -489,8 +483,6 @@ trait StdNames { 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" @@ -586,115 +578,109 @@ trait StdNames { val Annotation: NameType = "Annotation" 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 CaseDef: NameType = "CaseDef" + val ClassInfoType: NameType = "ClassInfoType" val ConstantType: NameType = "ConstantType" val EmptyPackage: NameType = "EmptyPackage" val EmptyPackageClass: NameType = "EmptyPackageClass" - val ExistentialTypeTree: NameType = "ExistentialTypeTree" + val ExistentialType: NameType = "ExistentialType" val Flag : NameType = "Flag" + val FlagsRepr: NameType = "FlagsRepr" val Ident: NameType = "Ident" + val ImplicitParams: NameType = "ImplicitParams" val Import: NameType = "Import" val Literal: NameType = "Literal" val LiteralAnnotArg: NameType = "LiteralAnnotArg" + val MethodType: NameType = "MethodType" val Modifiers: NameType = "Modifiers" val NestedAnnotArg: NameType = "NestedAnnotArg" + val New: NameType = "New" val NoFlags: NameType = "NoFlags" - val NoPrefix: NameType = "NoPrefix" val NoSymbol: NameType = "NoSymbol" + val NoMods: NameType = "NoMods" val Nothing: NameType = "Nothing" - val NoType: NameType = "NoType" val Null: NameType = "Null" + val NullaryMethodType: NameType = "NullaryMethodType" val Object: NameType = "Object" + val PolyType: NameType = "PolyType" + val RefinedType: NameType = "RefinedType" val RootPackage: NameType = "RootPackage" val RootClass: NameType = "RootClass" val Select: NameType = "Select" val SelectFromTypeTree: NameType = "SelectFromTypeTree" - val StringContext: NameType = "StringContext" + val SingleType: NameType = "SingleType" + val SuperType: NameType = "SuperType" 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 TypeBounds: NameType = "TypeBounds" val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" + val accessor: NameType = "accessor" 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 applyOrElse: NameType = "applyOrElse" val args : NameType = "args" - val argv : NameType = "argv" val arrayClass: NameType = "arrayClass" - val arrayElementClass: NameType = "arrayElementClass" - 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 asTerm: NameType = "asTerm" val asModule: NameType = "asModule" - val asMethod: NameType = "asMethod" val asType: NameType = "asType" - val asClass: NameType = "asClass" val asInstanceOf_ : NameType = "asInstanceOf" val asInstanceOf_Ob : NameType = "$asInstanceOf" - val assert_ : NameType = "assert" - val assume_ : NameType = "assume" val box: NameType = "box" - val build : NameType = "build" val bytes: NameType = "bytes" + val c: NameType = "c" val canEqual_ : NameType = "canEqual" - val checkInitialized: NameType = "checkInitialized" - val ClassManifestFactory: NameType = "ClassManifestFactory" val classOf: NameType = "classOf" - val clone_ : NameType = if (forMSIL) "MemberwiseClone" else "clone" // sn.OClone causes checkinit failure - val conforms: NameType = "conforms" + val clone_ : NameType = "clone" + val collection: NameType = "collection" + val conforms: NameType = "$conforms" // dollar prefix to avoid accidental shadowing val copy: NameType = "copy" + val create: NameType = "create" val currentMirror: NameType = "currentMirror" - val definitions: NameType = "definitions" val delayedInit: NameType = "delayedInit" val delayedInitArg: NameType = "delayedInit$body" + val dollarScope: NameType = "$scope" val drop: NameType = "drop" val elem: NameType = "elem" - val emptyValDef: NameType = "emptyValDef" + val noSelfType: NameType = "noSelfType" 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 equals_ : NameType = "equals" val error: NameType = "error" - val eval: NameType = "eval" val ex: NameType = "ex" val experimental: NameType = "experimental" val f: NameType = "f" val false_ : NameType = "false" val filter: NameType = "filter" - val finalize_ : NameType = if (forMSIL) "Finalize" else "finalize" + val finalize_ : NameType = "finalize" val find_ : NameType = "find" - val flagsFromBits : NameType = "flagsFromBits" val flatMap: NameType = "flatMap" val foreach: NameType = "foreach" - val genericArrayOps: NameType = "genericArrayOps" + val freshTermName: NameType = "freshTermName" + val freshTypeName: NameType = "freshTypeName" val get: NameType = "get" - val getOrElse: NameType = "getOrElse" - val hasNext: NameType = "hasNext" - val hashCode_ : NameType = if (forMSIL) "GetHashCode" else "hashCode" + val hashCode_ : NameType = "hashCode" val hash_ : NameType = "hash" - val head: NameType = "head" - val identity: NameType = "identity" + val head : NameType = "head" + val immutable: NameType = "immutable" val implicitly: NameType = "implicitly" val in: NameType = "in" - val info: NameType = "info" + val internal: NameType = "internal" val inlinedEquals: NameType = "inlinedEquals" val isArray: NameType = "isArray" val isDefinedAt: NameType = "isDefinedAt" @@ -706,102 +692,140 @@ trait StdNames { 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 ManifestFactory: NameType = "ManifestFactory" val manifestToTypeTag: NameType = "manifestToTypeTag" val map: NameType = "map" val materializeClassTag: NameType = "materializeClassTag" val materializeWeakTypeTag: NameType = "materializeWeakTypeTag" val materializeTypeTag: NameType = "materializeTypeTag" - val mirror : NameType = "mirror" val moduleClass : NameType = "moduleClass" - val name: NameType = "name" + val mkAnnotation: NameType = "mkAnnotation" + val mkEarlyDef: NameType = "mkEarlyDef" + val mkIdent: NameType = "mkIdent" + val mkPackageStat: NameType = "mkPackageStat" + val mkRefineStat: NameType = "mkRefineStat" + val mkRefTree: NameType = "mkRefTree" + val mkSelect: NameType = "mkSelect" + val mkThis: NameType = "mkThis" + val mkTypeTree: NameType = "mkTypeTree" val ne: NameType = "ne" val newArray: NameType = "newArray" 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 pendingSuperCall: NameType = "pendingSuperCall" 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 reificationSupport : NameType = "reificationSupport" 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 setInfo: NameType = "setInfo" 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 staticPackage : NameType = "staticPackage" val synchronized_ : NameType = "synchronized" - val tail: NameType = "tail" - val `then` : NameType = "then" + val ScalaDot: NameType = "ScalaDot" + val TermName: NameType = "TermName" 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 toStats: NameType = "toStats" + val TopScope: NameType = "TopScope" + val toString_ : NameType = "toString" val toTypeConstructor: NameType = "toTypeConstructor" val tpe : NameType = "tpe" val tree : NameType = "tree" val true_ : NameType = "true" val typedProductIterator: NameType = "typedProductIterator" + val TypeName: NameType = "TypeName" val typeTagToManifest: NameType = "typeTagToManifest" val unapply: NameType = "unapply" val unapplySeq: NameType = "unapplySeq" val unbox: NameType = "unbox" val universe: NameType = "universe" + val UnliftListElementwise: NameType = "UnliftListElementwise" + val UnliftListOfListsElementwise: NameType = "UnliftListOfListsElementwise" 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" + val zero: NameType = "zero" + + // quasiquote interpolators: + val q: NameType = "q" + val tq: NameType = "tq" + val cq: NameType = "cq" + val pq: NameType = "pq" + val fq: NameType = "fq" + + // quasiquote's syntactic combinators + val SyntacticAnnotatedType: NameType = "SyntacticAnnotatedType" + val SyntacticApplied: NameType = "SyntacticApplied" + val SyntacticAppliedType: NameType = "SyntacticAppliedType" + val SyntacticAssign: NameType = "SyntacticAssign" + val SyntacticBlock: NameType = "SyntacticBlock" + val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticCompoundType: NameType = "SyntacticCompoundType" + val SyntacticDefDef: NameType = "SyntacticDefDef" + val SyntacticEmptyTypeTree: NameType = "SyntacticEmptyTypeTree" + val SyntacticExistentialType: NameType = "SyntacticExistentialType" + val SyntacticFilter: NameType = "SyntacticFilter" + val SyntacticFor: NameType = "SyntacticFor" + val SyntacticForYield: NameType = "SyntacticForYield" + val SyntacticFunction: NameType = "SyntacticFunction" + val SyntacticFunctionType: NameType = "SyntacticFunctionType" + val SyntacticImport: NameType = "SyntacticImport" + val SyntacticMatch: NameType = "SyntacticMatch" + val SyntacticNew: NameType = "SyntacticNew" + val SyntacticObjectDef: NameType = "SyntacticObjectDef" + val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef" + val SyntacticPartialFunction: NameType = "SyntacticPartialFunction" + val SyntacticPatDef: NameType = "SyntacticPatDef" + val SyntacticSelectTerm: NameType = "SyntacticSelectTerm" + val SyntacticSelectType: NameType = "SyntacticSelectType" + val SyntacticSingletonType: NameType = "SyntacticSingletonType" + val SyntacticTermIdent: NameType = "SyntacticTermIdent" + val SyntacticTraitDef: NameType = "SyntacticTraitDef" + val SyntacticTry: NameType = "SyntacticTry" + val SyntacticTuple: NameType = "SyntacticTuple" + val SyntacticTupleType: NameType = "SyntacticTupleType" + val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" + val SyntacticTypeIdent: NameType = "SyntacticTypeIdent" + val SyntacticTypeProjection: NameType = "SyntacticTypeProjection" + val SyntacticValDef: NameType = "SyntacticValDef" + val SyntacticValEq: NameType = "SyntacticValEq" + val SyntacticValFrom: NameType = "SyntacticValFrom" + val SyntacticVarDef: NameType = "SyntacticVarDef" // unencoded operators object raw { - final val AMP : NameType = "&" final val BANG : NameType = "!" final val BAR : NameType = "|" final val DOLLAR: NameType = "$" @@ -810,7 +834,6 @@ trait StdNames { final val MINUS: NameType = "-" final val NE: NameType = "!=" final val PLUS : NameType = "+" - final val SLASH: NameType = "/" final val STAR : NameType = "*" final val TILDE: NameType = "~" @@ -834,29 +857,33 @@ trait StdNames { 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("||") + val ADD = encode("+") + val AND = encode("&") + val ASR = encode(">>") + val CONS = encode("::") + val COLONPLUS = 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 MINGT = 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 PLUSPLUS = encode("++") + val SUB = MINUS // ... as does SUB with PLUS + val XOR = encode("^") + val ZAND = encode("&&") + val ZOR = encode("||") // unary operators val UNARY_~ = encode("unary_~") @@ -866,14 +893,7 @@ trait StdNames { // 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" @@ -998,6 +1018,8 @@ trait StdNames { val BITMAP_CHECKINIT_TRANSIENT: NameType = BITMAP_PREFIX + "inittrans$" // initialization bitmap for transient checkinit values } + lazy val typeNames: tpnme.type = tpnme + object tpnme extends TypeNames { } /** For fully qualified type names. @@ -1005,7 +1027,6 @@ trait StdNames { 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$. @@ -1019,17 +1040,14 @@ trait StdNames { val javanme = nme.javaKeywords - object nme extends TermNames { - - def isModuleVarName(name: Name): Boolean = - stripAnonNumberSuffix(name) endsWith MODULE_VAR_SUFFIX + lazy val termNames: nme.type = nme + object nme extends TermNames { 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 @@ -1037,58 +1055,9 @@ trait StdNames { 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 val stringToTermName = null - protected val stringToTypeName = null - 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 { @@ -1148,7 +1117,11 @@ trait StdNames { final val keywords = kw.result } - private abstract class JavaNames extends SymbolNames { + sealed abstract class SymbolNames { + protected val stringToTermName = null + protected val stringToTypeName = null + protected implicit def createNameType(s: String): TypeName = newTypeNameCached(s) + final val BoxedBoolean: TypeName = "java.lang.Boolean" final val BoxedByte: TypeName = "java.lang.Byte" final val BoxedCharacter: TypeName = "java.lang.Character" @@ -1158,25 +1131,18 @@ trait StdNames { 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, @@ -1190,52 +1156,5 @@ trait StdNames { ) } - 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 + lazy val sn: SymbolNames = new SymbolNames { } } diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala new file mode 100644 index 0000000000..c088e8f57c --- /dev/null +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -0,0 +1,302 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect +package internal + +import scala.collection.mutable +import Flags._ +import util.HashSet +import scala.annotation.tailrec + +/** An abstraction for considering symbol pairs. + * One of the greatest sources of compiler bugs is that symbols can + * trivially lose their prefixes and turn into some completely different + * type with the smallest of errors. It is the exception not the rule + * that type comparisons are done correctly. + * + * This offers a small step toward coherence with two abstractions + * which come up over and over again: + * + * RelativeTo: operations relative to a prefix + * SymbolPair: two symbols being related somehow, plus the class + * in which the relation is being performed + * + * This is only a start, but it is a start. + */ +abstract class SymbolPairs { + val global: SymbolTable + import global._ + + /** Type operations relative to a prefix. All operations work on Symbols, + * and the types are the member types of those symbols in the prefix. + */ + class RelativeTo(val prefix: Type) { + def this(clazz: Symbol) = this(clazz.thisType) + import scala.language.implicitConversions // geez, it even has to hassle me when it's private + private implicit def symbolToType(sym: Symbol): Type = prefix memberType sym + + def erasureOf(sym: Symbol): Type = erasure.erasure(sym)(sym: Type) + def signature(sym: Symbol): String = sym defStringSeenAs (sym: Type) + def erasedSignature(sym: Symbol): String = sym defStringSeenAs erasureOf(sym) + + def isSameType(sym1: Symbol, sym2: Symbol): Boolean = sym1 =:= sym2 + def isSubType(sym1: Symbol, sym2: Symbol): Boolean = sym1 <:< sym2 + def isSuperType(sym1: Symbol, sym2: Symbol): Boolean = sym2 <:< sym1 + def isSameErasure(sym1: Symbol, sym2: Symbol): Boolean = erasureOf(sym1) =:= erasureOf(sym2) + def matches(sym1: Symbol, sym2: Symbol): Boolean = (sym1: Type) matches (sym2: Type) + + override def toString = s"RelativeTo($prefix)" + } + + /** Are types tp1 and tp2 equivalent seen from the perspective + * of `baseClass`? For instance List[Int] and Seq[Int] are =:= + * when viewed from IterableClass. + */ + def sameInBaseClass(baseClass: Symbol)(tp1: Type, tp2: Type) = + (tp1 baseType baseClass) =:= (tp2 baseType baseClass) + + case class SymbolPair(base: Symbol, low: Symbol, high: Symbol) { + def pos = if (low.owner == base) low.pos else if (high.owner == base) high.pos else base.pos + def self: Type = base.thisType + def rootType: Type = base.thisType + + def lowType: Type = self memberType low + def lowErased: Type = erasure.specialErasure(base)(low.tpe) + def lowClassBound: Type = classBoundAsSeen(low.tpe.typeSymbol) + + def highType: Type = self memberType high + def highInfo: Type = self memberInfo high + def highErased: Type = erasure.specialErasure(base)(high.tpe) + def highClassBound: Type = classBoundAsSeen(high.tpe.typeSymbol) + + def isErroneous = low.tpe.isErroneous || high.tpe.isErroneous + def sameKind = sameLength(low.typeParams, high.typeParams) + + private def classBoundAsSeen(tsym: Symbol) = + tsym.classBound.asSeenFrom(rootType, tsym.owner) + + private def memberDefString(sym: Symbol, where: Boolean) = { + val def_s = ( + if (sym.isConstructor) s"$sym: ${self memberType sym}" + else sym defStringSeenAs (self memberType sym) + ) + def_s + whereString(sym) + } + /** A string like ' at line 55' if the symbol is defined in the class + * under consideration, or ' in trait Foo' if defined elsewhere. + */ + private def whereString(sym: Symbol) = + if (sym.owner == base) " at line " + sym.pos.line else sym.locationString + + def lowString = memberDefString(low, where = true) + def highString = memberDefString(high, where = true) + + override def toString = sm""" + |Cursor(in $base) { + | high $highString + | erased $highErased + | infos ${high.infosString} + | low $lowString + | erased $lowErased + | infos ${low.infosString} + |}""".trim + } + + /** The cursor class + * @param base the base class containing the participating symbols + */ + abstract class Cursor(val base: Symbol) { + cursor => + + final val self = base.thisType // The type relative to which symbols are seen. + private val decls = newScope // all the symbols which can take part in a pair. + private val size = bases.length + + /** A symbol for which exclude returns true will not appear as + * either end of a pair. + */ + protected def exclude(sym: Symbol): Boolean + + /** Does `sym1` match `sym2` such that (sym1, sym2) should be + * considered as a (lo, high) pair? Types always match. Term symbols + * match if their member types relative to `self` match. + */ + protected def matches(lo: Symbol, high: Symbol): Boolean + + /** The parents and base classes of `base`. Can be refined in subclasses. + */ + protected def parents: List[Type] = base.info.parents + protected def bases: List[Symbol] = base.info.baseClasses + + /** An implementation of BitSets as arrays (maybe consider collection.BitSet + * for that?) The main purpose of this is to implement + * intersectionContainsElement efficiently. + */ + private type BitSet = Array[Int] + + /** A mapping from all base class indices to a bitset + * which indicates whether parents are subclasses. + * + * i \in subParents(j) iff + * exists p \in parents, b \in baseClasses: + * i = index(p) + * j = index(b) + * p isSubClass b + * p.baseType(b) == self.baseType(b) + */ + private val subParents = new Array[BitSet](size) + + /** A map from baseclasses of <base> to ints, with smaller ints meaning lower in + * linearization order. Symbols that are not baseclasses map to -1. + */ + private val index = new mutable.HashMap[Symbol, Int] { override def default(key: Symbol) = -1 } + + /** The scope entries that have already been visited as highSymbol + * (but may have been excluded via hasCommonParentAsSubclass.) + * These will not appear as lowSymbol. + */ + private val visited = HashSet[ScopeEntry]("visited", 64) + + /** Initialization has to run now so decls is populated before + * the declaration of curEntry. + */ + init() + + // The current low and high symbols; the high may be null. + private[this] var lowSymbol: Symbol = _ + private[this] var highSymbol: Symbol = _ + + // The current entry candidates for low and high symbol. + private[this] var curEntry = decls.elems + private[this] var nextEntry = curEntry + + // These fields are initially populated with a call to next(). + next() + + // populate the above data structures + private def init() { + // Fill `decls` with lower symbols shadowing higher ones + def fillDecls(bcs: List[Symbol], deferred: Boolean) { + if (!bcs.isEmpty) { + fillDecls(bcs.tail, deferred) + var e = bcs.head.info.decls.elems + while (e ne null) { + if (e.sym.initialize.isDeferred == deferred && !exclude(e.sym)) + decls enter e.sym + e = e.next + } + } + } + var i = 0 + for (bc <- bases) { + index(bc) = i + subParents(i) = new BitSet(size) + i += 1 + } + for (p <- parents) { + val pIndex = index(p.typeSymbol) + if (pIndex >= 0) + for (bc <- p.baseClasses ; if sameInBaseClass(bc)(p, self)) { + val bcIndex = index(bc) + if (bcIndex >= 0) + include(subParents(bcIndex), pIndex) + } + } + // first, deferred (this will need to change if we change lookup rules!) + fillDecls(bases, deferred = true) + // then, concrete. + fillDecls(bases, deferred = false) + } + + private def include(bs: BitSet, n: Int) { + val nshifted = n >> 5 + val nmask = 1 << (n & 31) + bs(nshifted) |= nmask + } + + /** Implements `bs1 * bs2 * {0..n} != 0. + * Used in hasCommonParentAsSubclass */ + private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, n: Int): Boolean = { + val nshifted = n >> 5 + val nmask = 1 << (n & 31) + var i = 0 + while (i < nshifted) { + if ((bs1(i) & bs2(i)) != 0) return true + i += 1 + } + (bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0 + } + + /** Do `sym1` and `sym2` have a common subclass in `parents`? + * In that case we do not follow their pairs. + */ + private def hasCommonParentAsSubclass(sym1: Symbol, sym2: Symbol) = { + val index1 = index(sym1.owner) + (index1 >= 0) && { + val index2 = index(sym2.owner) + (index2 >= 0) && { + intersectionContainsElementLeq( + subParents(index1), subParents(index2), index1 min index2) + } + } + } + + @tailrec private def advanceNextEntry() { + if (nextEntry ne null) { + nextEntry = decls lookupNextEntry nextEntry + if (nextEntry ne null) { + val high = nextEntry.sym + val isMatch = matches(lowSymbol, high) && { visited addEntry nextEntry ; true } // side-effect visited on all matches + + // skip nextEntry if a class in `parents` is a subclass of the + // owners of both low and high. + if (isMatch && !hasCommonParentAsSubclass(lowSymbol, high)) + highSymbol = high + else + advanceNextEntry() + } + } + } + @tailrec private def advanceCurEntry() { + if (curEntry ne null) { + curEntry = curEntry.next + if (curEntry ne null) { + if (visited(curEntry) || exclude(curEntry.sym)) + advanceCurEntry() + else + nextEntry = curEntry + } + } + } + + /** The `low` and `high` symbol. In the context of overriding pairs, + * low == overriding and high == overridden. + */ + def low = lowSymbol + def high = highSymbol + + def hasNext = curEntry ne null + def currentPair = new SymbolPair(base, low, high) + def iterator = new Iterator[SymbolPair] { + def hasNext = cursor.hasNext + def next() = try cursor.currentPair finally cursor.next() + } + + // Note that next is called once during object initialization to + // populate the fields tracking the current symbol pair. + def next() { + if (curEntry ne null) { + lowSymbol = curEntry.sym + advanceNextEntry() // sets highSymbol + if (nextEntry eq null) { + advanceCurEntry() + next() + } + } + } + } +} diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 6ca8900d7c..c76dedbff4 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -3,18 +3,22 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.annotation.elidable import scala.collection.{ mutable, immutable } import util._ +import java.util.concurrent.TimeUnit +import scala.reflect.internal.{TreeGen => InternalTreeGen} abstract class SymbolTable extends macros.Universe with Collections with Names with Symbols with Types + with Variances with Kinds with ExistentialsAndSkolems with FlagSets @@ -37,36 +41,53 @@ abstract class SymbolTable extends macros.Universe with CapturedVariables with StdAttachments with StdCreators - with BuildUtils + with ReificationSupport + with PrivateWithin + with pickling.Translations + with FreshNames + with Internals { - val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } - lazy val treeBuild = gen + val gen = new InternalTreeGen { val global: SymbolTable.this.type = SymbolTable.this } def log(msg: => AnyRef): Unit + def deprecationWarning(pos: Position, msg: String): Unit = warning(msg) def warning(msg: String): Unit = Console.err.println(msg) + def inform(msg: String): Unit = Console.err.println(msg) def globalError(msg: String): Unit = abort(msg) def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) + protected def elapsedMessage(msg: String, start: Long) = + msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start) + "ms" + + def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") + def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) + def shouldLogAtThisPhase = false + def isPastTyper = false + protected def isDeveloper: Boolean = settings.debug - @deprecated("Give us a reason", "2.10.0") - def abort(): Nothing = abort("unknown error") + @deprecated("Use devWarning if this is really a warning; otherwise use log", "2.11.0") + def debugwarn(msg: => String): Unit = devWarning(msg) /** 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 debuglog(msg: => String): Unit = if (settings.debug) log(msg) + def devWarning(msg: => String): Unit = if (isDeveloper) Console.err.println(msg) def throwableAsString(t: Throwable): String = "" + t + def throwableAsString(t: Throwable, maxFrames: Int): String = t.getStackTrace take maxFrames mkString "\n at " + + @inline final def devWarningDumpStack(msg: => String, maxFrames: Int): Unit = + devWarning(msg + "\n" + throwableAsString(new Throwable, maxFrames)) /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */ - def debugStack(t: Throwable): Unit = debugwarn(throwableAsString(t)) + def debugStack(t: Throwable): Unit = devWarning(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"))) + (new Throwable).getStackTrace.drop(2).take(50).mkString("\n"))) result } @@ -81,12 +102,33 @@ abstract class SymbolTable extends macros.Universe result } @inline + final private[scala] def debuglogResult[T](msg: => String)(result: T): T = { + debuglog(msg + ": " + result) + result + } + @inline + final private[scala] def devWarningResult[T](msg: => String)(result: T): T = { + devWarning(msg + ": " + result) + result + } + @inline final private[scala] def logResultIf[T](msg: => String, cond: T => Boolean)(result: T): T = { if (cond(result)) log(msg + ": " + result) result } + @inline + final private[scala] def debuglogResultIf[T](msg: => String, cond: T => Boolean)(result: T): T = { + if (cond(result)) + debuglog(msg + ": " + result) + + result + } + + @inline final def findSymbol(xs: TraversableOnce[Symbol])(p: Symbol => Boolean): Symbol = { + xs find p getOrElse NoSymbol + } // For too long have we suffered in order to sort NAMES. // I'm pretty sure there's a reasonable default for that. @@ -108,17 +150,13 @@ abstract class SymbolTable extends macros.Universe val global: SymbolTable.this.type = SymbolTable.this } with util.TraceSymbolActivity + val treeInfo: TreeInfo { val global: SymbolTable.this.type } + /** Check that the executing thread is the compiler thread. No-op here, * overridden in interactive.Global. */ @elidable(elidable.WARNING) def assertCorrectThread() {} - /** 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). @@ -139,7 +177,7 @@ abstract class SymbolTable extends macros.Universe type RunId = Int final val NoRunId = 0 - // sigh, this has to be public or atPhase doesn't inline. + // sigh, this has to be public or enteringPhase doesn't inline. var phStack: List[Phase] = Nil private[this] var ph: Phase = NoPhase private[this] var per = NoPeriod @@ -182,9 +220,6 @@ abstract class SymbolTable extends macros.Universe /** 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) @@ -202,23 +237,37 @@ abstract class SymbolTable extends macros.Universe p != NoPhase && phase.id > p.id /** Perform given operation at given phase. */ - @inline final def atPhase[T](ph: Phase)(op: => T): T = { + @inline final def enteringPhase[T](ph: Phase)(op: => T): T = { val saved = pushPhase(ph) try op finally popPhase(saved) } + final def findPhaseWithName(phaseName: String): Phase = { + var ph = phase + while (ph != NoPhase && ph.name != phaseName) { + ph = ph.prev + } + if (ph eq NoPhase) phase else ph + } + final def enteringPhaseWithName[T](phaseName: String)(body: => T): T = { + val phase = findPhaseWithName(phaseName) + enteringPhase(phase)(body) + } - /** 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) + def slowButSafeEnteringPhase[T](ph: Phase)(op: => T): T = { + if (isCompilerUniverse) enteringPhase(ph)(op) + else op + } + + @inline final def exitingPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph.next)(op) + @inline final def enteringPrevPhase[T](op: => T): T = enteringPhase(phase.prev)(op) - @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = - if (isAtPhaseAfter(target)) atPhase(target)(op) else op + @inline final def enteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isAtPhaseAfter(target)) enteringPhase(target)(op) else op + + def slowButSafeEnteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isCompilerUniverse) enteringPhaseNotLaterThan(target)(op) else op final def isValid(period: Period): Boolean = period != 0 && runId(period) == currentRunId && { @@ -231,7 +280,7 @@ abstract class SymbolTable extends macros.Universe 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) @@ -302,27 +351,45 @@ abstract class SymbolTable extends macros.Universe } object perRunCaches { - 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 = WeakHashSet[Clearable]() + import java.lang.ref.WeakReference + private var caches = List[WeakReference[Clearable]]() def recordCache[T <: Clearable](cache: T): T = { - caches += cache + caches ::= new WeakReference(cache) cache } def clearAll() = { debuglog("Clearing " + caches.size + " caches.") - caches foreach (_.clear) + caches foreach (ref => Option(ref.get).foreach(_.clear)) + caches = caches.filterNot(_.get == null) } 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]()) + + def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]()) + def newGeneric[T](f: => T): () => T = { + val NoCached: T = null.asInstanceOf[T] + var cached: T = NoCached + var cachedRunId = NoRunId + recordCache(new Clearable { + def clear(): Unit = cached = NoCached + }) + () => { + if (currentRunId != cachedRunId || cached == NoCached) { + cached = f + cachedRunId = currentRunId + } + cached + } + } } /** The set of all installed infotransformers. */ @@ -339,26 +406,14 @@ abstract class SymbolTable extends macros.Universe */ def isCompilerUniverse = false + @deprecated("Use enteringPhase", "2.10.0") // Used in SBT 0.12.4 + @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) + + /** * Adds the `sm` String interpolator to a [[scala.StringContext]]. */ implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps - - def importPrivateWithinFromJavaFlags(sym: Symbol, jflags: Int): Symbol = { - import ClassfileConstants._ - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) - // See ticket #1687 for an example of when topLevelClass is NoSymbol: it - // apparently occurs when processing v45.3 bytecode. - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - // protected in java means package protected. #3946 - if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - sym - } } object SymbolTableStats { diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 579f7684fd..2ce54d2259 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3,15 +3,17 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer -import util.Statistics +import util.{ Statistics, shortClassOfInstance } import Flags._ import scala.annotation.tailrec -import scala.reflect.io.AbstractFile +import scala.reflect.io.{ AbstractFile, NoAbstractFile } +import Variance._ trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ @@ -19,8 +21,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => protected var ids = 0 - val emptySymbolArray = new Array[Symbol](0) - protected def nextId() = { ids += 1; ids } /** Used for deciding in the IDE whether we can interrupt the compiler */ @@ -30,13 +30,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => //protected var lockedSyms = scala.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 _recursionTable = immutable.Map.empty[Symbol, Int] + def recursionTable = _recursionTable + def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable = value - private var nextexid = 0 - protected def freshExistentialName(suffix: String) = { - nextexid += 1 - newTypeName("_" + nextexid + suffix) - } + private var existentialIds = 0 + protected def nextExistentialId() = { existentialIds += 1; existentialIds } + protected def freshExistentialName(suffix: String) = newTypeName("_" + nextExistentialId() + suffix) // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { @@ -55,31 +55,50 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newFreeTypeSymbol(name: TypeName, flags: Long = 0L, origin: String): FreeTypeSymbol = new FreeTypeSymbol(name, origin) initFlags flags - /** Determines whether the given information request should trigger the given symbol's completer. - * See comments to `Symbol.needsInitialize` for details. - */ - protected def shouldTriggerCompleter(symbol: Symbol, completer: Type, isFlagRelated: Boolean, mask: Long) = - completer match { - case null => false - case _: FlagAgnosticCompleter => !isFlagRelated - case _ => abort(s"unsupported completer: $completer of class ${if (completer != null) completer.getClass else null} for symbol ${symbol.fullName}") - } - /** 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 { + // 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.) + protected def saveOriginalOwner(sym: Symbol) { + if (originalOwner contains sym) () + else originalOwner(sym) = sym.rawowner + } + protected def originalEnclosingMethod(sym: Symbol): Symbol = { + if (sym.isMethod || sym == NoSymbol) sym + else { + val owner = originalOwner.getOrElse(sym, sym.rawowner) + if (sym.isLocalDummy) owner.enclClass.primaryConstructor + else originalEnclosingMethod(owner) + } + } + + def symbolOf[T: WeakTypeTag]: TypeSymbol = weakTypeOf[T].typeSymbolDirect.asType + + abstract class SymbolContextApiImpl extends SymbolApi { this: Symbol => + def isFreeTerm: Boolean = false + def asFreeTerm: FreeTermSymbol = throw new ScalaReflectionException(s"$this is not a free term") + def isFreeType: Boolean = false + def asFreeType: FreeTypeSymbol = throw new ScalaReflectionException(s"$this is not a free type") + def isExistential: Boolean = this.isExistentiallyBound def isParamWithDefault: Boolean = this.hasDefault + // `isByNameParam` is only true for a call-by-name parameter of a *method*, + // an argument of the primary constructor seen in the class body is excluded by `isValueParameter` def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def isJava: Boolean = isJavaDefined def isVal: Boolean = isTerm && !isModule && !isMethod && !isMutable def isVar: Boolean = isTerm && !isModule && !isMethod && !isLazy && isMutable + def isAbstract: Boolean = isAbstractClass || isDeferred || isAbstractType + def isPrivateThis = (this hasFlag PRIVATE) && (this hasFlag LOCAL) + def isProtectedThis = (this hasFlag PROTECTED) && (this hasFlag LOCAL) def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) @@ -87,27 +106,70 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def knownDirectSubclasses = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize children } + def selfType = { + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize + typeOfThis + } + def baseClasses = info.baseClasses def module = sourceModule def thisPrefix: Type = thisType - def selfType: Type = typeOfThis - def typeSignature: Type = { fullyInitializeSymbol(this); info } - def typeSignatureIn(site: Type): Type = { fullyInitializeSymbol(this); site memberInfo this } + def superPrefix(supertpe: Type): Type = SuperType(thisType, supertpe) + + // These two methods used to call fullyInitializeSymbol on `this`. + // + // The only positive effect of that is, to the best of my knowledge, convenient printing + // (if you print a signature of the symbol that's not fully initialized, + // you might end up with weird <?>'s in value/type params) + // + // Another effect is obviously full initialization of that symbol, + // but that one shouldn't be necessary from the public API standpoint, + // because everything that matters auto-initializes at runtime, + // and auto-initialization at compile-time is anyway dubious + // (I've had spurious cyclic refs caused by calling typeSignature + // that initialized parent, which was in the middle of initialization). + // + // Given that and also given the pressure of being uniform with info and infoIn, + // I've removed calls to fullyInitializeSymbol from typeSignature and typeSignatureIn, + // injected fullyInitializeSymbol in showDecl, and injected fullyInitializeType in runtime Type.toString + // (the latter will make things a bit harder to debug in runtime universe, because + // toString might now very rarely cause cyclic references, but we also have showRaw that doesn't do initialization). + // + // Auto-initialization in runtime Type.toString is one of the examples of why a cake-based design + // isn't a very good idea for reflection API. Sometimes we want to same pretty name for both a compiler-facing + // and a user-facing API that should have different behaviors (other examples here include isPackage, isCaseClass, etc). + // Within a cake it's fundamentally impossible to achieve that. + def typeSignature: Type = info + def typeSignatureIn(site: Type): Type = site memberInfo this def toType: Type = tpe def toTypeIn(site: Type): Type = site.memberType(this) def toTypeConstructor: Type = typeConstructor - def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this } def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } def getter: Symbol = getter(owner) def setter: Symbol = setter(owner) + + def companion: Symbol = { + if (isModule && !isPackage) companionSymbol + else if (isModuleClass && !isPackageClass) sourceModule.companionSymbol + else if (isClass && !isModuleClass && !isPackageClass) companionSymbol + else NoSymbol + } + + def infoIn(site: Type): Type = typeSignatureIn(site) + def overrides: List[Symbol] = allOverriddenSymbols + def paramLists: List[List[Symbol]] = paramss } + private[reflect] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) + /** The class for all symbols */ abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name) extends SymbolContextApiImpl @@ -115,6 +177,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => with Annotatable[Symbol] with Attachable { + // makes sure that all symbols that runtime reflection deals with are synchronized + private def isSynchronized = this.isInstanceOf[scala.reflect.runtime.SynchronizedSymbols#SynchronizedSymbol] + private def isAprioriThreadsafe = isThreadsafe(AllOps) + assert(isCompilerUniverse || isSynchronized || isAprioriThreadsafe, s"unsafe symbol $initName (child of $initOwner) in runtime reflection universe") + type AccessBoundaryType = Symbol type AnnotationType = AnnotationInfo @@ -128,13 +195,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => def name: NameType def name_=(n: Name): Unit = { if (shouldLogAtThisPhase) { - val msg = s"Renaming $fullLocationString to $n" + def msg = s"In $owner, renaming $name -> $n" if (isSpecialized) debuglog(msg) else log(msg) } } 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 + // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api + // The null check is for NoSymbol, which can't pass a reference to itself to the constructor and also + // can't call owner_= due to an assertion it contains. + private[this] var _rawowner = if (initOwner eq null) this else initOwner private[this] var _rawflags: Long = _ def rawowner = _rawowner @@ -171,13 +241,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def debugFlagString: String = flagString(AllFlags) /** String representation of symbol's variance */ - def varianceString: String = - if (variance == 1) "+" - else if (variance == -1) "-" - else "" + def varianceString: String = variance.symbolicString override def flagMask = - if (settings.debug.value && !isAbstractType) AllFlags + if (settings.debug && !isAbstractType) AllFlags else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags @@ -186,10 +253,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (isGADTSkolem) " (this is a GADT skolem)" else "" - def shortSymbolClass = getClass.getName.split('.').last.stripPrefix("Symbols$") + def shortSymbolClass = shortClassOfInstance(this) def symbolCreationString: String = ( "%s%25s | %-40s | %s".format( - if (settings.uniqid.value) "%06d | ".format(id) else "", + if (settings.uniqid) "%06d | ".format(id) else "", shortSymbolClass, name.decode + " in " + owner, rawFlagString @@ -221,7 +288,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Static constructor with info set. */ def newStaticConstructor(pos: Position): MethodSymbol = - newConstructor(pos, STATIC) setInfo UnitClass.tpe + newConstructor(pos, STATIC) setInfo UnitTpe /** Instance constructor with info set. */ def newClassConstructor(pos: Position): MethodSymbol = @@ -249,20 +316,29 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newImport(pos: Position): TermSymbol = newTermSymbol(nme.IMPORT, pos) + def newModuleVarSymbol(accessor: Symbol): TermSymbol = { + val newName = nme.moduleVarName(accessor.name.toTermName) + val newFlags = MODULEVAR | ( if (this.isClass) PrivateLocal | SYNTHETIC else 0 ) + val newInfo = accessor.tpe.finalResultType + val mval = newVariable(newName, accessor.pos.focus, newFlags.toLong) addAnnotation VolatileAttr + + if (this.isClass) + mval setInfoAndEnter newInfo + else + mval setInfo newInfo + } + 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, flags0: FlagSet): (ModuleSymbol, ClassSymbol) = { val flags = flags0 | MODULE - val m = newModuleSymbol(name, pos, flags) + val m = newModuleSymbol(name.toTermName, pos, flags) val c = newModuleClass(name.toTypeName, pos, flags & 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] @@ -325,11 +401,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => () => { cnt += 1; nme.syntheticParamName(cnt) } } - /** Synthetic value parameters when parameter symbols are not available - */ - final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[TermSymbol]] = - argtypess map (xs => newSyntheticValueParams(xs, freshNamer)) - /** Synthetic value parameters when parameter symbols are not available. * Calling this method multiple times will re-use the same parameter names. */ @@ -345,16 +416,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newSyntheticValueParam(argtype: Type, name: TermName = nme.syntheticParamName(1)): TermSymbol = newValueParameter(name, owner.pos.focus, SYNTHETIC) setInfo argtype - 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) + def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = + newExistentialSkolem(basis.name.toTypeName, basis.info, basis.flags, basis.pos, origin) + + /** Create a new existential type skolem with this symbol its owner, and the given other properties. + */ + def newExistentialSkolem(name: TypeName, info: Type, flags: Long, pos: Position, origin: AnyRef): TypeSkolem = { + val skolem = newTypeSkolemSymbol(name.toTypeName, origin, pos, (flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (info cloneInfo skolem) } // don't test directly -- use isGADTSkolem @@ -409,14 +484,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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) @@ -432,23 +499,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => case _ => new StubTermSymbol(this, name.toTermName, missingMessage) } - @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. @@ -532,14 +582,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ 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 def isInvariant = !isCovariant && !isContravariant /** Qualities of Terms, always false for TypeSymbols. @@ -551,6 +599,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isConstructor = false def isEarlyInitialized = false def isGetter = false + def isDefaultGetter = false def isLocalDummy = false def isMixinConstructor = false def isOverloaded = false @@ -559,7 +608,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isValue = false def isValueParameter = false def isVariable = false - override def hasDefault = false def isTermMacro = false /** Qualities of MethodSymbols, always false for TypeSymbols @@ -589,27 +637,75 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def isEffectiveRoot = false + /** Can this symbol only be subclassed by bottom classes? This is assessed + * to be the case if it is final, and any type parameters are invariant. + */ + def hasOnlyBottomSubclasses = { + def loop(tparams: List[Symbol]): Boolean = tparams match { + case Nil => true + case x :: xs => x.variance.isInvariant && loop(xs) + } + isClass && isFinal && loop(typeParams) + } + final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol - final def isOverridableMember = !(isClass || isEffectivelyFinal) && (this ne NoSymbol) && owner.isClass + final def isOverridableMember = !(isClass || isEffectivelyFinal) && safeOwner.isClass /** Does this symbol denote a wrapper created by the repl? */ final def isInterpreterWrapper = ( (this hasFlag MODULE) - && owner.isPackageClass + && isTopLevel && nme.isReplWrapperName(name) ) + + /** In our current architecture, symbols for top-level classes and modules + * are created as dummies. Package symbols just call newClass(name) or newModule(name) and + * consider their job done. + * + * In order for such a dummy to provide meaningful info (e.g. a list of its members), + * it needs to go through unpickling. Unpickling is a process of reading Scala metadata + * from ScalaSignature annotations and assigning it to symbols and types. + * + * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation + * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively + * (i.e. the pickle not only contains info about directly nested classes/modules, but also about + * classes/modules nested into those and so on). + * + * Unpickling is triggered automatically whenever info (info in compiler parlance) is called. + * This happens because package symbols assign completer thunks to the dummies they create. + * Therefore metadata loading happens lazily and transparently. + * + * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members). + * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin. + * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol + * produces incorrect results. + * + * One might think that the solution is simple: automatically call the completer + * whenever one needs flags, annotations and privateWithin - just like it's done for info. + * Unfortunately, this leads to weird crashes in scalac, and currently we can't attempt + * to fix the core of the compiler risk stability a few weeks before the final release. + * upd. Haha, "a few weeks before the final release". This surely sounds familiar :) + * + * However we do need to fix this for runtime reflection, since this idionsynchrazy is not something + * we'd like to expose to reflection users. Therefore a proposed solution is to check whether we're in a + * runtime reflection universe, and if yes and if we've not yet loaded the requested info, then to commence initialization. + */ final def getFlag(mask: Long): Long = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize + if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize flags & mask } /** Does symbol have ANY flag in `mask` set? */ final def hasFlag(mask: Long): Boolean = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize (flags & mask) != 0 } + def hasFlag(mask: Int): Boolean = hasFlag(mask.toLong) + /** Does symbol have ALL the flags in `mask` set? */ final def hasAllFlags(mask: Long): Boolean = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = FlagOps(mask))) initialize (flags & mask) == mask } @@ -642,11 +738,32 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def hasGetter = isTerm && nme.isLocalName(name) + /** A little explanation for this confusing situation. + * Nested modules which have no static owner when ModuleDefs + * are eliminated (refchecks) are given the lateMETHOD flag, + * which makes them appear as methods after refchecks. + * Here's an example where one can see all four of FF FT TF TT + * for (isStatic, isMethod) at various phases. + * + * trait A1 { case class Quux() } + * object A2 extends A1 { object Flax } + * // -- namer object Quux in trait A1 + * // -M flatten object Quux in trait A1 + * // S- flatten object Flax in object A2 + * // -M posterasure object Quux in trait A1 + * // -M jvm object Quux in trait A1 + * // SM jvm object Quux in object A2 + * + * So "isModuleNotMethod" exists not for its achievement in + * brevity, but to encapsulate the relevant condition. + */ + def isModuleNotMethod = isModule && !isMethod + def isStaticModule = isModuleNotMethod && isStatic + 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 + final def isErroneous = isError || isInitialized && tpe_*.isErroneous def isHigherOrderTypeParameter = owner.isTypeParameterOrSkolem @@ -658,7 +775,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass final def isMethodWithExtension = - isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isTermMacro + isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isMacro final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME) final def isDefinedInPackage = effectiveOwner.isPackageClass @@ -715,10 +832,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } 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 isSerializable = info.baseClasses.exists(p => p == SerializableClass || p == JavaSerializableClass) def hasBridgeAnnotation = hasAnnotation(BridgeClass) def isDeprecated = hasAnnotation(DeprecatedAttr) def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0) @@ -728,14 +842,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => = hasAnnotation(DeprecatedInheritanceAttr) def deprecatedInheritanceMessage = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 0) - def deprecatedInheritanceVersion - = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 1) def hasDeprecatedOverridingAnnotation = hasAnnotation(DeprecatedOverridingAttr) def deprecatedOverridingMessage = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 0) - def deprecatedOverridingVersion - = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 1) // !!! when annotation arguments are not literal strings, but any sort of // assembly of strings, there is a fair chance they will turn up here not as @@ -753,19 +863,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => def compileTimeOnlyMessage = getAnnotation(CompileTimeOnlyAttr) flatMap (_ stringArg 0) /** Is this symbol an accessor method for outer? */ - final def isOuterAccessor = { - hasFlag(STABLE | ARTIFACT) && - originalName == nme.OUTER - } + final def isOuterAccessor = hasFlag(STABLE | ARTIFACT) && (unexpandedName == nme.OUTER) /** Is this symbol an accessor method for outer? */ - final def isOuterField = { - hasFlag(ARTIFACT) && - originalName == nme.OUTER_LOCAL - } + final def isOuterField = isArtifact && (unexpandedName == nme.OUTER_LOCAL) - /** Does this symbol denote a stable value? */ - def isStable = false + /** Does this symbol denote a stable value, ignoring volatility? + * + * Stability and volatility are checked separately to allow volatile paths in patterns that amount to equality checks. SI-6815 + */ + final def isStable = isTerm && !isMutable && !(hasFlag(BYNAMEPARAM)) && (!isMethod || hasStableFlag) + final def hasVolatileType = tpe.isVolatile && !hasAnnotation(uncheckedStableClass) /** Does this symbol denote the primary constructor of its enclosing class? */ final def isPrimaryConstructor = @@ -776,9 +884,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => isConstructor && !isPrimaryConstructor /** Is this symbol a synthetic apply or unapply method in a companion object of a case class? */ + // xeno-by: why this obscure use of the CASE flag? why not simply compare name with nme.apply and nme.unapply? final def isCaseApplyOrUnapply = isMethod && isCase && isSynthetic + /** Is this symbol a synthetic copy method in a case class? */ + final def isCaseCopy = + isMethod && owner.isCase && isSynthetic && name == nme.copy + /** Is this symbol a trait which needs an implementation class? */ final def needsImplClass = ( isTrait @@ -815,26 +928,40 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStaticOwner: Boolean = isPackageClass || isModuleClass && isStatic - def isTopLevelModule = hasFlag(MODULE) && owner.isPackageClass + /** A helper function for isEffectivelyFinal. */ + private def isNotOverridden = ( + owner.isClass && ( + owner.isEffectivelyFinal + || owner.isSealed && owner.children.forall(c => c.isEffectivelyFinal && (overridingSymbol(c) == NoSymbol)) + ) + ) /** 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) + || isModuleOrModuleClass && (isTopLevel || !settings.overrideObjects) || isTerm && ( isPrivate - || isLocal - || owner.isClass && owner.isEffectivelyFinal - ) + || isLocalToBlock + ) ) + /** Is this symbol effectively final or a concrete term member of sealed class whose childred do not override it */ + final def isEffectivelyFinalOrNotOverridden: Boolean = isEffectivelyFinal || (isTerm && !isDeferred && isNotOverridden) + + /** Is this symbol owned by a package? */ + final def isTopLevel = owner.isPackageClass - /** Is this symbol locally defined? I.e. not accessed from outside `this` instance */ + /** Is this symbol defined in a block? */ + @deprecated("Use isLocalToBlock instead", "2.11.0") final def isLocal: Boolean = owner.isTerm + /** Is this symbol defined in a block? */ + final def isLocalToBlock: 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)? */ + /** Is this class nested in another class or module (not a package). Includes locally defined classes. */ def isNestedClass = false /** Is this class locally defined? @@ -845,41 +972,36 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ 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 + (isClass || isType || isModule) && info.dealiasWiden/*.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? + def isOnlyRefinementMember = ( + isTerm // Type members are unaffected + && owner.isRefinementClass // owner must be a refinement class + && isPossibleInRefinement // any overridden symbols must also have refinement class owners + && !isConstant // Must not be a constant. Question: Can we exclude @inline methods as well? + && isDeclaredByOwner // Must be explicitly declared in the refinement (not synthesized from glb) + ) + // "(owner.info decl name) == this" is inadequate, because "name" might + // be overloaded in owner - and this might be an overloaded symbol. + // TODO - make this cheaper and see where else we should be doing something similar. + private def isDeclaredByOwner = (owner.info decl name).alternatives exists (alternatives contains _) 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 + final def isPossibleInRefinement = ( + !isConstructor + && allOverriddenSymbols.forall(_.owner.isRefinementClass) // this includes allOverriddenSymbols.isEmpty + ) /** 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 || @@ -888,13 +1010,43 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 } + def exists: Boolean = !isTopLevel || { + val isSourceLoader = rawInfo match { + case sl: SymLoader => sl.fromSource + case _ => false + } + def warnIfSourceLoader() { + if (isSourceLoader) + // Predef is completed early due to its autoimport; we used to get here when type checking its + // parent LowPriorityImplicits. See comment in c5441dc for more elaboration. + // Since the fix for SI-7335 Predef parents must be defined in Predef.scala, and we should not + // get here anymore. + devWarning(s"calling Symbol#exists with sourcefile based symbol loader may give incorrect results."); + } + + rawInfo load this + rawInfo != NoType || { warnIfSourceLoader(); false } + } final def isInitialized: Boolean = validTo != NoPeriod + /** We consider a symbol to be thread-safe, when multiple concurrent threads can call its methods + * (either directly or indirectly via public reflection or internal compiler infrastructure), + * without any locking and everything works as it should work. + * + * In its basic form, `isThreadsafe` always returns false. Runtime reflection augments reflection infrastructure + * with threadsafety-tracking mechanism implemented in `SynchronizedSymbol` that communicates with underlying completers + * and can sometimes return true if the symbol has been completed to the point of thread safety. + * + * The `purpose` parameter signifies whether we want to just check immutability of certain flags for the given mask. + * This is necessary to enable robust auto-initialization of `Symbol.flags` for runtime reflection, and is also quite handy + * in avoiding unnecessary initializations when requesting for flags that have already been set. + */ + def isThreadsafe(purpose: SymbolOps): Boolean = false + def markFlagsCompleted(mask: Long): this.type = this + def markAllCompleted(): this.type = this + /** Can this symbol be loaded by a reflective mirror? * * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. @@ -916,14 +1068,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (isAliasType) return true if (isType && isNonClassType) return false if (isRefinementClass) return false - return true + true } - /** The variance of this symbol as an integer */ - final def variance: Int = - if (isCovariant) 1 - else if (isContravariant) -1 - else 0 + /** The variance of this symbol. */ + def variance: Variance = + if (isCovariant) Covariant + else if (isContravariant) Contravariant + else Invariant /** The sequence number of this parameter symbol among all type * and value parameters of symbol's owner. -1 if symbol does not @@ -947,23 +1099,27 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ owner attribute -------------------------------------------------------------- + /** In general when seeking the owner of a symbol, one should call `owner`. + * The other possibilities include: + * - call `safeOwner` if it is expected that the target may be NoSymbol + * - call `assertOwner` if it is an unrecoverable error if the target is NoSymbol + * + * `owner` behaves like `safeOwner`, but logs NoSymbol.owner calls under -Xdev. + * `assertOwner` aborts compilation immediately if called on NoSymbol. + */ def owner: Symbol = { if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) rawowner } + final def safeOwner: Symbol = if (this eq NoSymbol) NoSymbol else owner + final def assertOwner: Symbol = if (this eq NoSymbol) abort("no-symbol does not have an owner") else owner // 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 - } + saveOriginalOwner(this) assert(isCompilerUniverse, "owner_= is not thread-safe; cannot be run in reflexive code") if (traceSymbolActivity) traceSymbols.recordNewSymbolOwner(this, owner) @@ -993,10 +1149,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ name attribute -------------------------------------------------------------- - /** If this symbol has an expanded name, its original name, otherwise its name itself. - * @see expandName + @deprecated("Use unexpandedName", "2.11.0") def originalName: Name = unexpandedName + + /** If this symbol has an expanded name, its original (unexpanded) name, + * otherwise the name itself. */ - def originalName: Name = nme.originalName(nme.dropLocalSuffix(name)) + def unexpandedName: Name = nme.unexpandedName(name) /** The name of the symbol before decoding, e.g. `\$eq\$eq` instead of `==`. */ @@ -1004,7 +1162,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The decoded name of the symbol, e.g. `==` instead of `\$eq\$eq`. */ - def decodedName: String = nme.dropLocalSuffix(name).decode + def decodedName: String = name.decode private def addModuleSuffix(n: Name): Name = if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n @@ -1023,7 +1181,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) /** These should be moved somewhere like JavaPlatform. */ - def javaSimpleName: Name = addModuleSuffix(nme.dropLocalSuffix(simpleName)) + def javaSimpleName: Name = addModuleSuffix(simpleName.dropLocal) def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString @@ -1041,10 +1199,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def fullNameInternal(separator: Char): Name = ( if (isRoot || isRootPackage || this == NoSymbol) name else if (owner.isEffectiveRoot) name - else ((effectiveOwner.enclClass.fullNameAsName(separator) append separator): Name) append name + else effectiveOwner.enclClass.fullNameAsName(separator) append (separator, name) ) - def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) + def fullNameAsName(separator: Char): Name = fullNameInternal(separator).dropLocal /** The encoded full path name of this symbol, where outer names and inner names * are separated by periods. @@ -1082,9 +1240,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 @@ -1101,12 +1256,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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) + // Package before Module, Module before Method, or we might grab the wrong guy. + if ((newFlags & PACKAGE) != 0) createPackageSymbol(name, pos, newFlags | PackageFlags) else if ((newFlags & MODULE) != 0) createModuleSymbol(name, pos, newFlags) + else if ((newFlags & METHOD) != 0) + createMethodSymbol(name, pos, newFlags) else if ((newFlags & PARAM) != 0) createValueParameterSymbol(name, pos, newFlags) else @@ -1144,7 +1300,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * as public. */ def accessBoundary(base: Symbol): Symbol = { - if (hasFlag(PRIVATE) || isLocal) owner + if (hasFlag(PRIVATE) || isLocalToBlock) owner else if (hasAllFlags(PROTECTED | STATIC | JAVA)) enclosingRootClass else if (hasAccessBoundary && !phase.erasedTypes) privateWithin else if (hasFlag(PROTECTED)) base @@ -1166,7 +1322,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ private[this] var _privateWithin: Symbol = _ def privateWithin = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize _privateWithin } def privateWithin_=(sym: Symbol) { _privateWithin = sym } @@ -1187,20 +1344,61 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } - /** 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. + /** The "type" of this symbol. The type of a term symbol is its usual + * type. A TypeSymbol is more complicated; see that class for elaboration. + * Since tpe forwards to tpe_*, if you call it on a type symbol with unapplied + * type parameters, the type returned will contain dummies types. These will + * hide legitimate errors or create spurious ones if used as normal types. * * For type symbols, `tpe` is different than `info`. `tpe` returns a typeRef * to the type symbol, `info` returns the type information of the type symbol, * e.g. a ClassInfoType for classes or a TypeBounds for abstract types. */ - def tpe: Type = info - def tpeHK: Type = tpe + final def tpe: Type = tpe_* + + /** typeConstructor throws an exception when called on term + * symbols; this is a more forgiving alternative. Calls + * typeConstructor on TypeSymbols, returns info otherwise. + */ + def tpeHK: Type = info + + /** Only applicable to TypeSymbols, it is the type corresponding + * to the symbol itself. For instance, the type of a List might + * be List[Int] - the same symbol's typeConstructor is simply List. + * One might be tempted to write that as List[_], and in some + * contexts this is possible, but it is discouraged because it is + * syntactically indistinguishable from and easily confused with the + * type List[T] forSome { type T; }, which can also be written List[_]. + */ + def typeConstructor: Type = ( + // Avoiding a third override in NoSymbol to preserve bimorphism + if (this eq NoSymbol) + abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") + else + abort("typeConstructor inapplicable for " + this) + ) + + /** The type of this symbol, guaranteed to be of kind *. + * If there are unapplied type parameters, they will be + * substituted with dummy type arguments derived from the + * type parameters. Such types are not valid in a general + * sense and will cause difficult-to-find bugs if allowed + * to roam free. + * + * If you call tpe_* explicitly to obtain these types, + * you are responsible for them as if it they were your own + * minor children. + */ + def tpe_* : Type = info + + // Alternate implementation of def tpe for warning about misuse, + // disabled to keep the method maximally hotspot-friendly: + // def tpe: Type = { + // val result = tpe_* + // if (settings.debug.value && result.typeArgs.nonEmpty) + // printCaller(s"""Call to ${this.tpe} created $result: call tpe_* or tpeHK""")("") + // result + // } /** Get type info associated with symbol at current phase, after * ensuring that symbol is initialized (i.e. type is completed). @@ -1236,13 +1434,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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) + if (cnt == 3) abort(s"no progress in completing $this: $tp") } rawInfo } catch { case ex: CyclicReference => - debugwarn("... hit cycle trying to complete " + this.fullLocationString) + devWarning("... hit cycle trying to complete " + this.fullLocationString) throw ex } @@ -1254,9 +1452,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** Set initial info. */ - def setInfo(info: Type): this.type = { info_=(info); this } + 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)) + 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 @@ -1342,6 +1540,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(isCompilerUniverse) if (infos == null || runId(infos.validFrom) == currentRunId) { infos + } else if (isPackageClass) { + // SI-7801 early phase package scopes are mutated in new runs (Namers#enterPackage), so we have to + // discard transformed infos, rather than just marking them as from this run. + val oldest = infos.oldest + oldest.validFrom = validTo + this.infos = oldest + oldest } else { val prev1 = adaptInfos(infos.prev) if (prev1 ne infos.prev) prev1 @@ -1351,10 +1556,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => _validTo = period(currentRunId, pid) phase = phaseWithId(pid) - val info1 = ( - if (isPackageClass) infos.info - else adaptToNewRunMap(infos.info) - ) + val info1 = adaptToNewRunMap(infos.info) if (info1 eq infos.info) { infos.validFrom = validTo infos @@ -1374,53 +1576,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isInitialized) info this } - - /** Called when the programmer requests information that might require initialization of the underlying symbol. - * - * `isFlagRelated` and `mask` describe the nature of this information. - * isFlagRelated = true means that the programmer needs particular bits in flags. - * isFlagRelated = false means that the request is unrelated to flags (annotations or privateWithin). - * - * In our current architecture, symbols for top-level classes and modules - * are created as dummies. Package symbols just call newClass(name) or newModule(name) and - * consider their job done. - * - * In order for such a dummy to provide meaningful info (e.g. a list of its members), - * it needs to go through unpickling. Unpickling is a process of reading Scala metadata - * from ScalaSignature annotations and assigning it to symbols and types. - * - * A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation - * and then reads metadata for the unpicklee, its companion (if any) and all their members recursively - * (i.e. the pickle not only contains info about directly nested classes/modules, but also about - * classes/modules nested into those and so on). - * - * Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called. - * This happens because package symbols assign completer thunks to the dummies they create. - * Therefore metadata loading happens lazily and transparently. - * - * Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members). - * It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin. - * This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol - * produces incorrect results. - * - * One might think that the solution is simple: automatically call the completer whenever one needs - * flags, annotations and privateWithin - just like it's done for typeSignature. Unfortunately, this - * leads to weird crashes in scalac, and currently we can't attempt to fix the core of the compiler - * risk stability a few weeks before the final release. - * - * However we do need to fix this for runtime reflection, since it's not something we'd like to - * expose to reflection users. Therefore a proposed solution is to check whether we're in a - * runtime reflection universe and if yes then to commence initialization. - */ - protected def needsInitialize(isFlagRelated: Boolean, mask: Long) = - !isInitialized && (flags & LOCKED) == 0 && shouldTriggerCompleter(this, if (infos ne null) infos.info else null, isFlagRelated, mask) - - /** 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 + def maybeInitialize = { + try { initialize ; true } + catch { case _: CyclicReference => debuglog("Hit cycle in maybeInitialize of $this") ; false } } /** Was symbol's type updated during given phase? */ @@ -1436,21 +1594,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => * 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() - } + def cookJavaRawInfo(): this.type = { + // only try once... + if (phase.erasedTypes || (this hasFlag TRIEDCOOKING)) + return this - protected def doCookJavaRawInfo(): Unit + this setFlag TRIEDCOOKING + info // force the current info + if (isJavaDefined || isType && owner.isJavaDefined) + this modifyInfo rawToExistential + else if (isOverloaded) + alternatives withFilter (_.isJavaDefined) foreach (_ modifyInfo rawToExistential) - /** 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) + this + } /** The logic approximately boils down to finding the most recent phase * which immediately follows any of parser, namer, typer, or erasure. @@ -1472,23 +1629,27 @@ trait Symbols extends api.Symbols { self: SymbolTable => * assumption: if a type starts out as monomorphic, it will not acquire * type parameters later. */ + // NOTE: overridden in SynchronizedSymbols with the code copy/pasted + // don't forget to modify the code over there if you modify this method def unsafeTypeParams: List[Symbol] = if (isMonomorphicType) Nil - else atPhase(unsafeTypeParamPhase)(rawInfo.typeParams) + else enteringPhase(unsafeTypeParamPhase)(rawInfo.typeParams) /** The type parameters of this symbol. * assumption: if a type starts out as monomorphic, it will not acquire * type parameters later. */ + // NOTE: overridden in SynchronizedSymbols with the code copy/pasted + // don't forget to modify the code over there if you modify this method 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) + enteringPhase(phaseOf(infos.validFrom))(rawInfo load this) if (validTo == NoPeriod) - atPhase(phaseOf(infos.validFrom))(rawInfo load this) + enteringPhase(phaseOf(infos.validFrom))(rawInfo load this) rawInfo.typeParams } @@ -1555,7 +1716,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def makeSerializable() { info match { case ci @ ClassInfoType(_, _, _) => - setInfo(ci.copy(parents = ci.parents :+ SerializableClass.tpe)) + setInfo(ci.copy(parents = ci.parents :+ SerializableTpe)) case i => abort("Only ClassInfoTypes can be made serializable: "+ i) } @@ -1578,7 +1739,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => * the annotations attached to member a definition (class, method, type, field). */ def annotations: List[AnnotationInfo] = { - if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. + if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize _annotations } @@ -1645,6 +1807,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => * and is this class symbol also different from Null or Nothing? */ def isNonBottomSubClass(that: Symbol): Boolean = false + /** Is this class symbol Null or Nothing, + * and (if Null) is `that` inhabited by null? + * If this is Nothing, of course, it is a + * subclass of `that` by definition. + * + * TODO - what is implied by the fact that AnyVal now has + * infinitely many non-bottom subclasses, not only 9? + */ + def isBottomSubClass(that: Symbol) = ( + (this eq NothingClass) + || (this eq NullClass) && that.isClass && (that ne NothingClass) && !(that isNonBottomSubClass AnyValClass) + ) + /** Overridden in NullClass and NothingClass for custom behavior. */ def isSubClass(that: Symbol) = isNonBottomSubClass(that) @@ -1663,12 +1838,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => def filter(cond: Symbol => Boolean): Symbol = if (isOverloaded) { - val alts = alternatives - val alts1 = alts filter cond - if (alts1 eq alts) this + var changed = false + var alts0: List[Symbol] = alternatives + var alts1: List[Symbol] = Nil + + while (alts0.nonEmpty) { + if (cond(alts0.head)) + alts1 ::= alts0.head + else + changed = true + + alts0 = alts0.tail + } + + if (!changed) this else if (alts1.isEmpty) NoSymbol else if (alts1.tail.isEmpty) alts1.head - else owner.newOverloaded(info.prefix, alts1) + else owner.newOverloaded(info.prefix, alts1.reverse) } else if (cond(this)) this else NoSymbol @@ -1679,10 +1865,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => result } - @inline final def map(f: Symbol => Symbol): Symbol = if (this eq NoSymbol) this else f(this) - - final def toOption: Option[Symbol] = if (exists) Some(this) else None - // ------ cloneing ------------------------------------------------------------------- /** A clone of this symbol. */ @@ -1748,11 +1930,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def thisSym: Symbol = this + def hasSelfType = thisSym.tpeHK != this.tpeHK + /** The type of `this` in a class, or else the type of the symbol itself. */ - def typeOfThis = thisSym.tpe + def typeOfThis = thisSym.tpe_* - /** If symbol is a class, the type <code>this.type</code> in this class, - * otherwise <code>NoPrefix</code>. + /** If symbol is a class, the type `this.type` in this class, + * otherwise `NoPrefix`. * We always have: thisType <:< typeOfThis */ def thisType: Type = NoPrefix @@ -1773,7 +1957,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // // The slightly more principled approach of using the paramss of the // primary constructor leads to cycles in, for example, pos/t5084.scala. - val primaryNames = constrParamAccessors.map(acc => nme.dropLocalSuffix(acc.name)) + val primaryNames = constrParamAccessors map (_.name.dropLocal) caseFieldAccessorsUnsorted.sortBy { acc => primaryNames indexWhere { orig => (acc.name == orig) || (acc.name startsWith (orig append "$")) @@ -1792,7 +1976,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** 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) + ownerTp decl localName } /** The module corresponding to this module class (note that this @@ -1886,15 +2070,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * 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 - } - } + def originalEnclosingMethod: Symbol = Symbols.this.originalEnclosingMethod(this) /** The method or class which logically encloses the current symbol. * If the symbol is defined in the initialization part of a template @@ -1920,18 +2096,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => * (or, for traits: `$init`) of `C`. * */ - def logicallyEnclosingMember: Symbol = + final def logicallyEnclosingMember: Symbol = if (isLocalDummy) enclClass.primaryConstructor - else if (isMethod || isClass) this + else if (isMethod || isClass || this == NoSymbol) this + else if (this == NoSymbol) { devWarningDumpStack("NoSymbol.logicallyEnclosingMember", 15); 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 (isTopLevel) { if (isClass) this else moduleClass } else owner.enclosingTopLevelClass @@ -1940,11 +2113,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => !rawInfoIsNoType && (this.effectiveOwner == that.effectiveOwner) && ( !this.effectiveOwner.isPackageClass - || (this.sourceFile eq null) - || (that.sourceFile eq null) - || (this.sourceFile.path == that.sourceFile.path) // Cheap possibly wrong check, then expensive normalization - || (this.sourceFile.canonicalPath == that.sourceFile.canonicalPath) - ) + || (this.associatedFile eq NoAbstractFile) + || (that.associatedFile eq NoAbstractFile) + || (this.associatedFile.path == that.associatedFile.path) // Cheap possibly wrong check, then expensive normalization + || (this.associatedFile.canonicalPath == that.associatedFile.canonicalPath) + ) ) /** The internal representation of classes and objects: @@ -2040,70 +2213,113 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @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 = { - //OPT cut down on #closures by special casing non-overloaded case - // was: ofclazz.info.nonPrivateDecl(name) filter (sym => - // !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) - val result = ofclazz.info.nonPrivateDecl(name) - def qualifies(sym: Symbol) = !sym.isTerm || (site.memberType(this) matches site.memberType(sym)) - if ((result eq NoSymbol) || !result.isOverloaded && qualifies(result)) result - else result filter qualifies - } + final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = + matchingSymbolInternal(site, ofclazz.info nonPrivateDecl name) /** 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))) + matchingSymbolInternal(site, site.nonPrivateMemberAdmitting(name, admit)) - /** The symbol, in class `ofclazz`, that is overridden by this symbol. + private def matchingSymbolInternal(site: Type, candidate: Symbol): Symbol = { + def qualifies(sym: Symbol) = !sym.isTerm || ((site memberType this) matches (site memberType sym)) + //OPT cut down on #closures by special casing non-overloaded case + if (candidate.isOverloaded) candidate filter qualifies + else if (qualifies(candidate)) candidate + else NoSymbol + } + + /** The symbol, in class `baseClass`, that is overridden by this symbol. * - * @param ofclazz is a base class of this symbol's owner. + * @param baseClass is a base class of this symbol's owner. */ - final def overriddenSymbol(ofclazz: Symbol): Symbol = - if (isClassConstructor) NoSymbol else matchingSymbol(ofclazz, owner.thisType) + final def overriddenSymbol(baseClass: Symbol): Symbol = ( + // concrete always overrides abstract, so don't let an abstract definition + // claim to be overriding an inherited concrete one. + matchingInheritedSymbolIn(baseClass) filter (res => res.isDeferred || !this.isDeferred) + ) + + private def matchingInheritedSymbolIn(baseClass: Symbol): Symbol = + if (canMatchInheritedSymbols) matchingSymbol(baseClass, owner.thisType) else NoSymbol /** 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) + final def overridingSymbol(ofclazz: Symbol): Symbol = ( + if (canMatchInheritedSymbols) + matchingSymbol(ofclazz, ofclazz.thisType) + else + NoSymbol + ) + + /** If false, this symbol cannot possibly participate in an override, + * either as overrider or overridee. For internal use; you should consult + * with isOverridingSymbol. This is used by isOverridingSymbol to escape + * the recursive knot. + */ + private def canMatchInheritedSymbols = ( + owner.isClass + && !this.isClass + && !this.isConstructor + ) - /** Returns all symbols overriden by this symbol. */ - final def allOverriddenSymbols: List[Symbol] = - if (!owner.isClass) Nil - else owner.ancestors map overriddenSymbol filter (_ != NoSymbol) + // All the symbols overridden by this symbol and this symbol at the head, + // or Nil if this is NoSymbol. + def overrideChain = ( + if (this eq NoSymbol) Nil + else if (isOverridingSymbol) this :: allOverriddenSymbols + else this :: Nil + ) + + /** Returns all symbols overridden by this symbol. */ + final def allOverriddenSymbols: List[Symbol] = { + def loop(xs: List[Symbol]): List[Symbol] = xs match { + case Nil => Nil + case x :: xs => + overriddenSymbol(x) match { + case NoSymbol => loop(xs) + case sym => sym :: loop(xs) + } + } + if (isOverridingSymbol) loop(owner.ancestors) else Nil + } /** 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) + lazy val isOverridingSymbol = ( + canMatchInheritedSymbols + && owner.ancestors.exists(base => overriddenSymbol(base) != 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 + @tailrec def loop(bases: List[Symbol]): Symbol = bases match { + case Nil => NoSymbol + case base :: rest => + val sym = overriddenSymbol(base) + if (sym == NoSymbol) loop(rest) else sym } - NoSymbol + if (isOverridingSymbol) loop(owner.ancestors) else 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) + final def extendedOverriddenSymbols: List[Symbol] = ( + if (canMatchInheritedSymbols) + owner.thisSym.ancestors map overriddenSymbol filter (_ != NoSymbol) + else + Nil + ) /** 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 + @deprecated("Use `superSymbolIn` instead", "2.11.0") + final def superSymbol(base: Symbol): Symbol = superSymbolIn(base) + + final def superSymbolIn(base: Symbol): Symbol = { + var bcs = base.info.baseClasses dropWhile (owner != _) drop 1 var sym: Symbol = NoSymbol while (!bcs.isEmpty && sym == NoSymbol) { if (!bcs.head.isImplClass) @@ -2116,22 +2332,30 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** 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) + @deprecated("Use `getterIn` instead", "2.11.0") + final def getter(base: Symbol): Symbol = getterIn(base) - def getterName: TermName = ( - if (isSetter) nme.setterToGetter(name.toTermName) - else if (nme.isLocalName(name)) nme.localToGetter(name.toTermName) - else name.toTermName - ) + final def getterIn(base: Symbol): Symbol = + base.info decl getterName filter (_.hasAccessorFlag) + + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName /** The setter of this value or getter definition, or NoSymbol if none exists */ - final def setter(base: Symbol): Symbol = setter(base, false) + @deprecated("Use `setterIn` instead", "2.11.0") + final def setter(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = + setterIn(base, hasExpandedName) - 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) - } + final def setterIn(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = + base.info decl setterNameInBase(base, hasExpandedName) filter (_.hasAccessorFlag) + + def needsExpandedSetterName = ( + if (isMethod) hasStableFlag && !isLazy + else hasNoFlags(LAZY | MUTABLE) + ) + def setterNameInBase(base: Symbol, expanded: Boolean): TermName = + if (expanded) nme.expandedSetterName(setterName, base) else setterName /** If this is a derived value class, return its unbox method * or NoSymbol if it does not exist. @@ -2188,35 +2412,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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. + final def sourceFile: AbstractFile = + if ((associatedFile eq NoAbstractFile) || (associatedFile.path endsWith ".class")) null else associatedFile - /** 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. + /** Overridden in ModuleSymbols to delegate to the module class. + * Never null; if there is no associated file, returns NoAbstractFile. */ - private def sourceFileOnly(file: AbstractFile): AbstractFile = - if ((file eq null) || (file.path endsWith ".class")) null else file - - private def binaryFileOnly(file: AbstractFile): AbstractFile = - if ((file eq null) || !(file.path endsWith ".class")) null else file - - final def binaryFile: AbstractFile = binaryFileOnly(associatedFile) - final def sourceFile: AbstractFile = sourceFileOnly(associatedFile) - - /** Overridden in ModuleSymbols to delegate to the module class. */ def associatedFile: AbstractFile = enclosingTopLevelClass.associatedFile def associatedFile_=(f: AbstractFile) { abort("associatedFile_= inapplicable for " + this) } - @deprecated("Use associatedFile_= instead", "2.10.0") - def sourceFile_=(f: AbstractFile): Unit = associatedFile_=(f) - /** If this is a sealed class, its known direct subclasses. * Otherwise, the empty set. */ @@ -2228,11 +2437,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => @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 } + @inline final def fold[T](none: => T)(f: Symbol => T): T = if (this ne NoSymbol) f(this) else none + @inline final def map(f: Symbol => Symbol): Symbol = if (this eq NoSymbol) this else f(this) -// ------ toString ------------------------------------------------------------------- + final def toOption: Option[Symbol] = if (exists) Some(this) else None - /** A tag which (in the ideal case) uniquely identifies class symbols */ - final def tag: Int = fullName.## + +// ------ toString ------------------------------------------------------------------- /** The simple name of this Symbol */ final def simpleName: Name = name @@ -2251,20 +2462,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isClass) "class" else if (isType && !isParameter) "type" else if (isVariable) "var" - else if (isPackage) "package" + else if (hasPackageFlag) "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") + if (isTermMacro) ("term macro", "macro method", "MACM") 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 (isPackage) ("package", "package", "PK") else if (isPackageObject) ("package object", "package", "PKO") else if (isPackageObjectClass) ("package object class", "package", "PKOC") else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") @@ -2279,7 +2489,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 && (owner.hasCompleteInfo && isPrimaryConstructor)) ("primary constructor", "constructor", "PCTOR") else if (isClassConstructor) ("constructor", "constructor", "CTOR") else if (isSourceMethod) ("method", "method", "METH") else if (isTerm) ("value", "value", "VAL") @@ -2321,14 +2531,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If !settings.debug translates expansions of operators back to operator symbol. * E.g. $eq => =. * If settings.uniqid, adds id. + * If settings.Yshowsymowners, adds owner's id * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ - def nameString: String = ( - if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + originalName.decode - else if (settings.uniqid.value && !settings.Yshowsymkinds.value) originalName.decode + "#" + id - else if (!settings.uniqid.value && settings.Yshowsymkinds.value) originalName.decode + "#" + abbreviatedKindString - else originalName.decode + "#" + id + "#" + abbreviatedKindString - ) + def nameString: String = { + val name_s = if (settings.debug.value) "" + unexpandedName else unexpandedName.dropLocal.decode + val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" + + name_s + idString + kind_s + } def fullNameString: String = { def recur(sym: Symbol): String = { @@ -2341,15 +2552,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** If settings.uniqid is set, the symbol's id, else "" */ - final def idString = if (settings.uniqid.value) "#"+id else "" + final def idString = { + val id_s = if (settings.uniqid.value) "#"+id else "" + val owner_s = if (settings.Yshowsymowners.value) "@"+owner.id else "" + id_s + owner_s + } /** 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 - ) + override def toString: String = { + if (isPackageObjectOrClass && !settings.debug) + s"package object ${owner.decodedName}" + else compose( + kindString, + if (hasMeaninglessName) owner.decodedName + idString else nameString + ) + } /** String representation of location. */ @@ -2374,6 +2593,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (settings.debug.value) parentsString(tp.parents) else briefParentsString(tp.parents) ) + def isStructuralThisType = ( + // prevents disasters like SI-8158 + owner.isInitialized && owner.isStructuralRefinement && tp == owner.tpe + ) if (isType) typeParamsString(tp) + ( if (isClass) " extends " + parents else if (isAliasType) " = " + tp.resultType @@ -2384,15 +2607,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) 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 + case PolyType(tparams, res) => typeParamsString(tp) + infoString(res) + case NullaryMethodType(res) => infoString(res) + case MethodType(params, res) => valueParamsString(tp) + infoString(res) + case _ if isStructuralThisType => ": " + owner.name + case _ => ": " + tp } } def infosString = infos.toString - def debugLocationString = fullLocationString + " (flags: " + debugFlagString + ")" + def debugLocationString = { + val pre = flagString match { + case "" => "" + case s if s contains ' ' => "(" + s + ") " + case s => s + " " + } + pre + fullLocationString + } private def defStringCompose(infoString: String) = compose( flagString, @@ -2458,31 +2689,32 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 + /*** !!! TODO: shouldn't we do something like the following: + override def isOverloaded = ( + if (this.isInitialized) + this hasFlag OVERLOADED + else + (infos ne null) && infos.info.isInstanceOf[OverloadedType] + ) + ***/ 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 isDefaultGetter = name containsName nme.DEFAULT_GETTER_STRING 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)) - ) + override def isPackageObject = isModule && (name == nme.PACKAGE) // The name in comments is what it is being disambiguated from. // TODO - rescue CAPTURED from BYNAMEPARAM so we can see all the names. @@ -2562,36 +2794,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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]) @@ -2672,17 +2874,36 @@ trait Symbols extends api.Symbols { self: SymbolTable => } loop(info) } + + override def exceptions = annotations flatMap ThrownException.unapply } 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 + override def variance = if (isLocalToThis) Bivariant else info.typeSymbol.variance + override def isContravariant = variance.isContravariant + override def isCovariant = variance.isCovariant final override def isAliasType = true override def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeSymbol = owner.newNonClassSymbol(name, pos, newFlags) } + /** Let's say you have a type definition + * + * {{{ + * type T <: Number + * }}} + * + * and tsym is the symbol corresponding to T. Then + * + * {{{ + * tsym is an instance of AbstractTypeSymbol + * tsym.info == TypeBounds(Nothing, Number) + * tsym.tpe == TypeRef(NoPrefix, T, List()) + * }}} + */ class AbstractTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { type TypeOfClonedSymbol = TypeSymbol @@ -2711,7 +2932,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 @@ -2729,7 +2949,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 @@ -2751,63 +2970,63 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 - * }}} + /** A polymorphic type symbol has two distinct "types": * - * and tsym is the symbol corresponding to T. Then + * tpe_* a TypeRef with: dummy type args, no unapplied type parameters, and kind * + * tpeHK a TypeRef with: no type args, unapplied type parameters, and + * kind (*,*,...,*) => * depending on the number of tparams. * - * {{{ - * tsym.info = TypeBounds(Nothing, Number) - * tsym.tpe = TypeRef(NoPrefix, T, List()) - * }}} - */ - override def tpe: Type = { - if (tpeCache eq NoType) throw CyclicReference(this, typeConstructor) + * The dummy type args in tpe_* are created by wrapping a TypeRef + * around the type parameter symbols. Types containing dummies will + * hide errors or introduce spurious ones if they are passed around + * as if normal types. They should only be used in local operations + * where they will either be discarded immediately after, or will + * undergo substitution in which the dummies are replaced by actual + * type arguments. + */ + override def tpe_* : Type = { + maybeUpdateTypeCache() + tpeCache + } + override def typeConstructor: Type = { + if (tyconCacheNeedsUpdate) + setTyconCache(newTypeRef(Nil)) + tyconCache + } + override def tpeHK: Type = typeConstructor + + private def tyconCacheNeedsUpdate = (tyconCache eq null) || tyconRunId != currentRunId + private def setTyconCache(tycon: Type) { + tyconCache = tycon + tyconRunId = currentRunId + assert(tyconCache ne null, this) + } + + private def maybeUpdateTypeCache() { if (tpePeriod != currentPeriod) { - if (isValid(tpePeriod)) { + 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) - } + else + updateTypeCache() // perform the actual update } - assert(tpeCache ne null/*, "" + this + " " + phase*/)//debug - tpeCache } + private def updateTypeCache() { + if (tpeCache eq NoType) + throw CyclicReference(this, typeConstructor) - /** @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 + if (isInitialized) + tpePeriod = currentPeriod - override def typeConstructor: Type = { - if ((tyconCache eq null) || tyconRunId != currentRunId) { - tyconCache = newTypeRef(Nil) - tyconRunId = currentRunId - } - assert(tyconCache ne null) - tyconCache + tpeCache = NoType // cycle marker + val noTypeParams = phase.erasedTypes && this != ArrayClass || unsafeTypeParams.isEmpty + tpeCache = newTypeRef( + if (noTypeParams) Nil + else unsafeTypeParams map (_.typeConstructor) + ) + // Avoid carrying around different types in tyconCache and tpeCache + // for monomorphic types. + if (noTypeParams && tyconCacheNeedsUpdate) + setTyconCache(tpeCache) } override def info_=(tp: Type) { @@ -2833,15 +3052,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => * 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) - } - } - } if (Statistics.hotEnabled) Statistics.incCounter(typeSymbolCount) } @@ -2871,11 +3081,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => // 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 hasAllFlags GADT_SKOLEM_FLAGS + override def isGADTSkolem = this hasAllFlags GADT_SKOLEM_FLAGS.toLong 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 */ @@ -2934,7 +3143,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 isNestedClass = !isTopLevel override def isNumericValueClass = definitions.isNumericValueClass(this) override def isNumeric = isNumericValueClass override def isPackageObjectClass = isModuleClass && (name == tpnme.PACKAGE) @@ -2959,24 +3168,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ override def isLocalClass = ( isAnonOrRefinementClass - || isLocal - || !owner.isPackageClass && owner.isLocalClass + || isLocalToBlock + || !isTopLevel && 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 @@ -2985,8 +3179,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * returned, otherwise, `NoSymbol` is returned. */ protected final def companionModule0: Symbol = - flatOwnerInfo.decl(name.toTermName).suchThat( - sym => sym.isModule && (sym isCoDefinedWith this) && !sym.isMethod) + flatOwnerInfo.decl(name.toTermName).suchThat(sym => sym.isModuleNotMethod && (sym isCoDefinedWith this)) override def companionModule = companionModule0 override def companionSymbol = companionModule0 @@ -3003,7 +3196,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (c.isOverloaded) c.alternatives.head else c } - override def associatedFile = if (owner.isPackageClass) _associatedFile else super.associatedFile + override def associatedFile = ( + if (!isTopLevel) super.associatedFile + else { + if (_associatedFile eq null) NoAbstractFile // guarantee not null, but save cost of initializing the var + else _associatedFile + } + ) override def associatedFile_=(f: AbstractFile) { _associatedFile = f } override def reset(completer: Type): this.type = { @@ -3016,8 +3215,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def thisType: Type = { val period = thisTypePeriod if (period != currentPeriod) { - thisTypePeriod = currentPeriod if (!isValid(period)) thisTypeCache = ThisType(this) + thisTypePeriod = currentPeriod } thisTypeCache } @@ -3052,9 +3251,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => clone.typeOfThis = typeOfThis clone.thisSym setName thisSym.name } - if (_associatedFile ne null) - clone.associatedFile = _associatedFile - + clone.associatedFile = _associatedFile clone } @@ -3067,6 +3264,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def children = childSet override def addChild(sym: Symbol) { childSet = childSet + sym } + def anonOrRefinementString = { + if (hasCompleteInfo) { + val label = if (isAnonymousClass) "$anon:" else "refinement of" + val parents = parentsString(info.parents map functionNBaseType filterNot (_.typeSymbol == SerializableClass)) + s"<$label $parents>" + } + else if (isAnonymousClass) "$anon" + else nameString + } + override def toString = ( + if (isAnonOrRefinementClass) anonOrRefinementString + else super.toString + ) + if (Statistics.hotEnabled) Statistics.incCounter(classSymbolCount) } implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol]) @@ -3093,9 +3304,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def typeOfThis = { val period = typeOfThisPeriod if (period != currentPeriod) { - typeOfThisPeriod = currentPeriod if (!isValid(period)) typeOfThisCache = singleType(owner.thisType, sourceModule) + typeOfThisPeriod = currentPeriod } typeOfThisCache } @@ -3106,9 +3317,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => // 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) { + implicitMembersCacheValue = tp.implicitMembers implicitMembersCacheKey1 = tp implicitMembersCacheKey2 = tp.decls.elems - implicitMembersCacheValue = tp.implicitMembers } } implicitMembersCacheValue @@ -3162,6 +3373,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) } trait StubSymbol extends Symbol { + devWarning("creating stub symbol to defer error: " + missingMessage) + protected def missingMessage: String /** Fail the stub by throwing a [[scala.reflect.internal.MissingRequirementError]]. */ @@ -3189,8 +3402,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def info = fail(NoType) override def rawInfo = fail(NoType) override def companionSymbol = fail(NoSymbol) - - debugwarn("creating stub symbol to defer error: " + missingMessage) } class StubClassSymbol(owner0: Symbol, name0: TypeName, protected val missingMessage: String) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol class StubTermSymbol(owner0: Symbol, name0: TermName, protected val missingMessage: String) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol @@ -3199,11 +3410,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => def origin: String } class FreeTermSymbol(name0: TermName, value0: => Any, val origin: String) extends TermSymbol(NoSymbol, NoPosition, name0) with FreeSymbol with FreeTermSymbolApi { + final override def isFreeTerm = true + final override def asFreeTerm = this def value = value0 } implicit val FreeTermSymbolTag = ClassTag[FreeTermSymbol](classOf[FreeTermSymbol]) - class FreeTypeSymbol(name0: TypeName, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) with FreeSymbol with FreeTypeSymbolApi + class FreeTypeSymbol(name0: TypeName, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) with FreeSymbol with FreeTypeSymbolApi { + final override def isFreeType = true + final override def asFreeType = this + } implicit val FreeTypeSymbolTag = ClassTag[FreeTypeSymbol](classOf[FreeTypeSymbol]) /** An object representing a missing symbol */ @@ -3216,10 +3432,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def name = nme.NO_NAME override def name_=(n: Name) = abort("Cannot set NoSymbol's name to " + n) - synchronized { - setInfo(NoType) - privateWithin = this - } + // Syncnote: no need to synchronize this, because NoSymbol's initialization is triggered by JavaUniverse.init + // which is called in universe's constructor - something that's inherently single-threaded + setInfo(NoType) + privateWithin = this + override def info_=(info: Type) = { infos = TypeHistory(1, NoType, null) unlock() @@ -3240,23 +3457,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 associatedFile = NoAbstractFile + override def owner: Symbol = { + devWarningDumpStack("NoSymbol.owner", 15) + this + } + override def ownerChain: List[Symbol] = Nil 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 @@ -3272,8 +3487,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @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)) + val syms1 = mapList(syms)(symFn) + mapList(syms1)(_ substInfo (syms, syms1)) + } + + /** Derives a new list of symbols from the given list by mapping the given + * list of `syms` and `as` 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 as arguments to be passed to symFn together with symbols from syms (must be same length) + * @param symFn the function to create new symbols + * @return the new list of info-adjusted symbols + */ + def deriveSymbols2[A](syms: List[Symbol], as: List[A], symFn: (Symbol, A) => Symbol): List[Symbol] = { + val syms1 = map2(syms, as)(symFn) + mapList(syms1)(_ substInfo (syms, syms1)) } /** Derives a new Type by first deriving new symbols as in deriveSymbols, @@ -3289,6 +3519,22 @@ trait Symbols extends api.Symbols { self: SymbolTable => val syms1 = deriveSymbols(syms, symFn) tpe.substSym(syms, syms1) } + + /** Derives a new Type by first deriving new symbols as in deriveSymbols2, + * then performing the same oldSyms => newSyms substitution on `tpe` as is + * performed on the symbol infos in deriveSymbols. + * + * @param syms the prototypical symbols + * @param as arguments to be passed to symFn together with symbols from syms (must be same length) + * @param symFn the function to create new symbols based on `as` + * @param tpe the prototypical type + * @return the new symbol-subsituted type + */ + def deriveType2[A](syms: List[Symbol], as: List[A], symFn: (Symbol, A) => Symbol)(tpe: Type): Type = { + val syms1 = deriveSymbols2(syms, as, symFn) + tpe.substSym(syms, syms1) + } + /** Derives a new Type by instantiating the given list of symbols as * WildcardTypes. * @@ -3313,9 +3559,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @return the newly created, info-adjusted symbols */ def cloneSymbolsAndModify(syms: List[Symbol], infoFn: Type => Type): List[Symbol] = - cloneSymbols(syms) map (_ modifyInfo infoFn) + mapList(cloneSymbols(syms))(_ modifyInfo infoFn) def cloneSymbolsAtOwnerAndModify(syms: List[Symbol], owner: Symbol, infoFn: Type => Type): List[Symbol] = - cloneSymbolsAtOwner(syms, owner) map (_ modifyInfo infoFn) + mapList(cloneSymbolsAtOwner(syms, owner))(_ 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. @@ -3333,6 +3579,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def mapParamss[T](sym: Symbol)(f: Symbol => T): List[List[T]] = mmap(sym.info.paramss)(f) + def existingSymbols(syms: List[Symbol]): List[Symbol] = + syms filter (s => (s ne null) && (s ne NoSymbol)) + /** Return closest enclosing method, unless shadowed by an enclosing class. */ // TODO Move back to ExplicitOuter when the other call site is removed. // no use of closures here in the interest of speed. @@ -3348,25 +3597,26 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** A class for type histories */ - private sealed case class TypeHistory(var validFrom: Period, info: Type, prev: TypeHistory) { + private 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 + ")" + private def phaseString = "%s: %s".format(phaseOf(validFrom), info) + override def toString = toList reverseMap (_.phaseString) mkString ", " def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) + + def oldest: TypeHistory = if (prev == null) this else prev.oldest } // ----- Hoisted closures and convenience methods, for compile time reductions ------- private[scala] final val symbolIsPossibleInRefinement = (sym: Symbol) => sym.isPossibleInRefinement - private[scala] final val symbolIsNonVariant = (sym: Symbol) => sym.variance == 0 @tailrec private[scala] final def allSymbolsHaveOwner(syms: List[Symbol], owner: Symbol): Boolean = syms match { case sym :: rest => sym.owner == owner && allSymbolsHaveOwner(rest, owner) - case _ => true + case _ => true } @@ -3374,6 +3624,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => Statistics.newView("#symbols")(ids) + +// -------------- Completion -------------------------------------------------------- + + // is used to differentiate levels of thread-safety in `Symbol.isThreadsafe` + case class SymbolOps(isFlagRelated: Boolean, mask: Long) + val AllOps = SymbolOps(isFlagRelated = false, mask = 0L) + def FlagOps(mask: Long) = SymbolOps(isFlagRelated = true, mask = mask) + + private def relevantSymbols(syms: Seq[Symbol]) = syms.flatMap(sym => List(sym, sym.moduleClass, sym.sourceModule)) + def markFlagsCompleted(syms: Symbol*)(mask: Long): Unit = relevantSymbols(syms).foreach(_.markFlagsCompleted(mask)) + def markAllCompleted(syms: Symbol*): Unit = relevantSymbols(syms).foreach(_.markAllCompleted) } object SymbolsStats { diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index c1753fc5a1..9066c73393 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -1,7 +1,12 @@ -package scala.reflect +package scala +package reflect package internal -abstract class TreeGen extends macros.TreeBuilder { +import Flags._ +import util._ +import scala.collection.mutable.ListBuffer + +abstract class TreeGen { val global: SymbolTable import global._ @@ -11,10 +16,7 @@ abstract class TreeGen extends macros.TreeBuilder { 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 scalaAnyRefConstr = scalaDot(tpnme.AnyRef) // used in ide def scalaFunctionConstr(argtpes: List[Tree], restpe: Tree, abstractFun: Boolean = false): Tree = { val cls = if (abstractFun) @@ -47,10 +49,10 @@ abstract class TreeGen extends macros.TreeBuilder { mkMethodCall(Select(receiver, method), targs, args) def mkMethodCall(target: Tree, targs: List[Type], args: List[Tree]): Tree = - Apply(mkTypeApply(target, targs map TypeTree), args) + Apply(mkTypeApply(target, mapList(targs)(TypeTree)), args) def mkNullaryCall(method: Symbol, targs: List[Type]): Tree = - mkTypeApply(mkAttributedRef(method), targs map TypeTree) + mkTypeApply(mkAttributedRef(method), mapList(targs)(TypeTree)) /** Builds a reference to value whose type is given stable prefix. * The type must be suitable for this. For example, it @@ -93,7 +95,7 @@ abstract class TreeGen extends macros.TreeBuilder { case ConstantType(value) => Literal(value) setType tpe - case AnnotatedType(_, atp, _) => + case AnnotatedType(_, atp) => mkAttributedQualifier(atp) case RefinedType(parents, _) => @@ -116,7 +118,7 @@ abstract class TreeGen extends macros.TreeBuilder { } /** Builds a reference to given symbol with given stable prefix. */ - def mkAttributedRef(pre: Type, sym: Symbol): Tree = { + def mkAttributedRef(pre: Type, sym: Symbol): RefTree = { val qual = mkAttributedQualifier(pre) qual match { case EmptyTree => mkAttributedIdent(sym) @@ -126,33 +128,33 @@ abstract class TreeGen extends macros.TreeBuilder { } /** Builds a reference to given symbol. */ - def mkAttributedRef(sym: Symbol): Tree = + def mkAttributedRef(sym: Symbol): RefTree = 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) + def mkUnattributedRef(sym: Symbol): RefTree = mkUnattributedRef(sym.fullNameAsName('.')) + + def mkUnattributedRef(fullName: Name): RefTree = { + val hd :: tl = nme.segments(fullName.toString, assumeTerm = fullName.isTermName) + tl.foldLeft(Ident(hd): RefTree)(Select(_,_)) + } /** Replaces tree type with a stable type if possible */ - def stabilize(tree: Tree): Tree = { - for(tp <- stableTypeFor(tree)) tree.tpe = tp - tree + def stabilize(tree: Tree): Tree = stableTypeFor(tree) match { + case NoType => tree + case tp => tree setType tp } /** Computes stable type for a tree if possible */ - def stableTypeFor(tree: Tree): Option[Type] = tree match { - case This(_) if tree.symbol != null && !tree.symbol.isError => - Some(ThisType(tree.symbol)) - 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 - } + def stableTypeFor(tree: Tree): Type = ( + if (!treeInfo.admitsTypeSelection(tree)) NoType + else tree match { + case This(_) => ThisType(tree.symbol) + case Ident(_) => singleType(tree.symbol.owner.thisType, tree.symbol) + case Select(qual, _) => singleType(qual.tpe, tree.symbol) + case _ => NoType + } + ) /** Builds a reference with stable type to given symbol */ def mkAttributedStableRef(pre: Type, sym: Symbol): Tree = @@ -161,13 +163,13 @@ abstract class TreeGen extends macros.TreeBuilder { def mkAttributedStableRef(sym: Symbol): Tree = stabilize(mkAttributedRef(sym)) - def mkAttributedThis(sym: Symbol): Tree = + def mkAttributedThis(sym: Symbol): This = This(sym.name.toTypeName) setSymbol sym setType sym.thisType - def mkAttributedIdent(sym: Symbol): Tree = - Ident(sym.name) setSymbol sym setType sym.tpe + def mkAttributedIdent(sym: Symbol): RefTree = + Ident(sym.name) setSymbol sym setType sym.tpeHK - def mkAttributedSelect(qual: Tree, sym: Symbol): Tree = { + def mkAttributedSelect(qual: Tree, sym: Symbol): RefTree = { // Tests involving the repl fail without the .isEmptyPackage condition. if (qual.symbol != null && (qual.symbol.isEffectiveRoot || qual.symbol.isEmptyPackage)) mkAttributedIdent(sym) @@ -184,16 +186,12 @@ abstract class TreeGen extends macros.TreeBuilder { ) val needsPackageQualifier = ( (sym ne null) - && qualsym.isPackage - && !sym.isDefinedInPackage + && qualsym.hasPackageFlag + && !(sym.isDefinedInPackage || sym.moduleClass.isDefinedInPackage) // SI-7817 work around strangeness in post-flatten `Symbol#owner` ) val pkgQualifier = if (needsPackageQualifier) { - // The owner of a symbol which requires package qualification may be the - // package object iself, but it also could be any superclass of the package - // object. In the latter case, we must go through the qualifier's info - // to obtain the right symbol. - val packageObject = if (sym.owner.isModuleClass) sym.owner.sourceModule else qual.tpe member nme.PACKAGE + val packageObject = rootMirror.getPackageObjectWithMember(qual.tpe, sym) Select(qual, nme.PACKAGE) setSymbol packageObject setType singleType(qual.tpe, packageObject) } else qual @@ -207,13 +205,13 @@ abstract class TreeGen extends macros.TreeBuilder { /** 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 mkAppliedTypeTree(fun: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) fun else AppliedTypeTree(fun, targs) 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, tpe.normalize :: Nil) + val tapp = mkAttributedTypeApply(value, what, tpe.dealias :: Nil) if (wrapInApply) Apply(tapp, Nil) else tapp } private def typeTestSymbol(any: Boolean) = if (any) Any_isInstanceOf else Object_isInstanceOf @@ -229,7 +227,7 @@ abstract class TreeGen extends macros.TreeBuilder { /** 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 + if ((pt == UnitTpe) || (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)) @@ -248,10 +246,6 @@ abstract class TreeGen extends macros.TreeBuilder { 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 @@ -259,7 +253,7 @@ abstract class TreeGen extends macros.TreeBuilder { * which is appropriate to the given Type. */ def mkZero(tp: Type): Tree = tp.typeSymbol match { - case NothingClass => mkMethodCall(Predef_???, Nil) setType NothingClass.tpe + case NothingClass => mkMethodCall(Predef_???, Nil) setType NothingTpe case _ => Literal(mkConstantZero(tp)) setType tp } @@ -276,12 +270,28 @@ abstract class TreeGen extends macros.TreeBuilder { case _ => Constant(null) } + /** Wrap an expression in a named argument. */ + def mkNamedArg(name: Name, tree: Tree): Tree = mkNamedArg(Ident(name), tree) + def mkNamedArg(lhs: Tree, rhs: Tree): Tree = atPos(rhs.pos)(AssignOrNamedArg(lhs, rhs)) + /** 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) + def mkTuple(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match { + case Nil => + Literal(Constant(())) + case tree :: Nil if flattenUnary => + tree + case _ => + Apply(scalaDot(TupleClass(elems.length).name.toTermName), elems) + } + + def mkTupleType(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match { + case Nil => + scalaDot(tpnme.Unit) + case List(tree) if flattenUnary => + tree + case _ => + AppliedTypeTree(scalaDot(TupleClass(elems.length).name), elems) + } // tree1 AND tree2 def mkAnd(tree1: Tree, tree2: Tree): Tree = @@ -295,4 +305,595 @@ abstract class TreeGen extends macros.TreeBuilder { assert(ReflectRuntimeUniverse != NoSymbol) mkAttributedRef(ReflectRuntimeUniverse) setType singleType(ReflectRuntimeUniverse.owner.thisPrefix, ReflectRuntimeUniverse) } + + def mkSeqApply(arg: Tree): Apply = { + val factory = Select(mkAttributedRef(SeqModule), nme.apply) + Apply(factory, List(arg)) + } + + def mkSuperInitCall: Select = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) + + /** Generates a template with constructor corresponding to + * + * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } + * extends superclass(args_1) ... (args_n) with mixins { self => body } + * + * This gets translated to + * + * extends superclass with mixins { self => + * presupers' // presupers without rhs + * vparamss // abstract fields corresponding to value parameters + * def <init>(vparamss) { + * presupers + * super.<init>(args) + * } + * body + * } + */ + def mkTemplate(parents: List[Tree], self: ValDef, constrMods: Modifiers, + vparamss: List[List[ValDef]], body: List[Tree], superPos: Position = NoPosition): Template = { + /* Add constructor to template */ + + // create parameters for <init> as synthetic trees. + var vparamss1 = mmap(vparamss) { vd => + val param = atPos(vd.pos.makeTransparent) { + val mods = Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR) + ValDef(mods withAnnotations vd.mods.annotations, vd.name, vd.tpt.duplicate, duplicateAndKeepPositions(vd.rhs)) + } + param + } + + val (edefs, rest) = body span treeInfo.isEarlyDef + val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef + val gvdefs = evdefs map { + case vdef @ ValDef(_, _, tpt, _) => + copyValDef(vdef)( + // atPos for the new tpt is necessary, since the original tpt might have no position + // (when missing type annotation for ValDef for example), so even though setOriginal modifies the + // position of TypeTree, it would still be NoPosition. That's what the author meant. + tpt = atPos(vdef.pos.focus)(TypeTree() setOriginal tpt setPos tpt.pos.focus), + rhs = EmptyTree + ) + } + val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) } + + val constr = { + if (constrMods.isTrait) { + if (body forall treeInfo.isInterfaceMember) None + else Some( + atPos(wrappingPos(superPos, lvdefs)) ( + DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(lvdefs, Literal(Constant()))))) + } + else { + // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section + if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) + vparamss1 = List() :: vparamss1 + val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass + // this requires knowing which of the parents is a type macro and which is not + // and that's something that cannot be found out before typer + // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) + // this means that we don't know what will be the arguments of the super call + // therefore here we emit a dummy which gets populated when the template is named and typechecked + Some( + atPos(wrappingPos(superPos, lvdefs ::: vparamss1.flatten).makeTransparent) ( + DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) + } + } + constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus = false)) + // Field definitions for the class - remove defaults. + + val fieldDefs = vparamss.flatten map (vd => { + val field = copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree) + // Prevent overlapping of `field` end's position with default argument's start position. + // This is needed for `Positions.Locator(pos).traverse` to return the correct tree when + // the `pos` is a point position with all its values equal to `vd.rhs.pos.start`. + if(field.pos.isRange && vd.rhs.pos.isRange) field.pos = field.pos.withEnd(vd.rhs.pos.start - 1) + field + }) + + global.Template(parents, self, gvdefs ::: fieldDefs ::: constr ++: etdefs ::: rest) + } + + def mkParents(ownerMods: Modifiers, parents: List[Tree], parentPos: Position = NoPosition) = + if (ownerMods.isCase) parents ::: List(scalaDot(tpnme.Product), scalaDot(tpnme.Serializable)) + else if (parents.isEmpty) atPos(parentPos)(scalaAnyRefConstr) :: Nil + else parents + + def mkClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], templ: Template): ClassDef = { + val isInterface = mods.isTrait && (templ.body forall treeInfo.isInterfaceMember) + val mods1 = if (isInterface) (mods | Flags.INTERFACE) else mods + ClassDef(mods1, name, tparams, templ) + } + + /** Create positioned tree representing an object creation <new parents { stats } + * @param npos the position of the new + * @param cpos the position of the anonymous class starting with parents + */ + def mkNew(parents: List[Tree], self: ValDef, stats: List[Tree], + npos: Position, cpos: Position): Tree = + if (parents.isEmpty) + mkNew(List(scalaAnyRefConstr), self, stats, npos, cpos) + else if (parents.tail.isEmpty && stats.isEmpty) { + // `Parsers.template` no longer differentiates tpts and their argss + // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil) + // instead of parents = Ident(C), argss = Nil as before + // this change works great for things that are actually templates + // but in this degenerate case we need to perform postprocessing + val app = treeInfo.dissectApplied(parents.head) + atPos(npos union cpos) { New(app.callee, app.argss) } + } else { + val x = tpnme.ANON_CLASS_NAME + atPos(npos union cpos) { + Block( + List( + atPos(cpos) { + ClassDef( + Modifiers(FINAL), x, Nil, + mkTemplate(parents, self, NoMods, ListOfNil, stats, cpos.focus)) + }), + atPos(npos) { + New( + Ident(x) setPos npos.focus, + Nil) + } + ) + } + } + + /** Create a tree representing the function type (argtpes) => restpe */ + def mkFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + + /** Create a literal unit tree that is inserted by the compiler but not + * written by end user. It's important to distinguish the two so that + * quasiquotes can strip synthetic ones away. + */ + def mkSyntheticUnit() = Literal(Constant(())).updateAttachment(SyntheticUnitAttachment) + + /** Create block of statements `stats` */ + def mkBlock(stats: List[Tree]): Tree = + if (stats.isEmpty) mkSyntheticUnit() + else if (!stats.last.isTerm) Block(stats, mkSyntheticUnit()) + else if (stats.length == 1) stats.head + else Block(stats.init, stats.last) + + /** Create a block that wraps multiple statements but don't + * do any wrapping if there is just one statement. Used by + * quasiquotes, macro c.parse api and toolbox. + */ + def mkTreeOrBlock(stats: List[Tree]) = stats match { + case Nil => EmptyTree + case head :: Nil => head + case _ => mkBlock(stats) + } + + /** Create a tree representing an assignment <lhs = rhs> */ + def mkAssign(lhs: Tree, rhs: Tree): Tree = lhs match { + case Apply(fn, args) => Apply(atPos(fn.pos)(Select(fn, nme.update)), args :+ rhs) + case _ => Assign(lhs, rhs) + } + + def mkPackageObject(defn: ModuleDef, pidPos: Position = NoPosition, pkgPos: Position = NoPosition) = { + val module = copyModuleDef(defn)(name = nme.PACKAGEkw) + val pid = atPos(pidPos)(Ident(defn.name)) + atPos(pkgPos)(PackageDef(pid, module :: Nil)) + } + + // Following objects represent encoding of for loop enumerators + // into the regular trees. Such representations are used for: + // + // - as intermediate value of enumerators inside of the parser + // right before the mkFor desugaring is being called + // + // - as intermediate value of enumerators obtained after + // re-sugaring of for loops through build.SyntacticFor + // and build.SyntacticForYield (which are used by quasiquotes) + // + // The encoding uses regular trees with ForAttachment that helps + // to reliably differentiate them from normal trees that can have + // similar shape. fq"$pat <- $rhs" for example is represented in + // the same way as "`<-`($pat, $rhs)"" but with added attachment to + // the `<-` identifier. + // + // The primary rationale behind such representation in favor of + // simple case classes is a wish to re-use the same representation + // between quasiquotes and parser without exposing compiler internals. + // Opaque tree encoding can be changed/adapted at any time without + // breaking end users code. + + /** Encode/decode fq"$pat <- $rhs" enumerator as q"`<-`($pat, $rhs)" */ + object ValFrom { + def apply(pat: Tree, rhs: Tree): Tree = + Apply(Ident(nme.LARROWkw).updateAttachment(ForAttachment), + List(pat, rhs)) + + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case Apply(id @ Ident(nme.LARROWkw), List(pat, rhs)) + if id.hasAttachment[ForAttachment.type] => + Some((pat, rhs)) + case _ => None + } + } + + /** Encode/decode fq"$pat = $rhs" enumerator as q"$pat = $rhs" */ + object ValEq { + def apply(pat: Tree, rhs: Tree): Tree = + Assign(pat, rhs).updateAttachment(ForAttachment) + + def unapply(tree: Tree): Option[(Tree, Tree)] = tree match { + case Assign(pat, rhs) + if tree.hasAttachment[ForAttachment.type] => + Some((pat, rhs)) + case _ => None + } + } + + /** Encode/decode fq"if $cond" enumerator as q"`if`($cond)" */ + object Filter { + def apply(tree: Tree) = + Apply(Ident(nme.IFkw).updateAttachment(ForAttachment), List(tree)) + + def unapply(tree: Tree): Option[Tree] = tree match { + case Apply(id @ Ident(nme.IFkw), List(cond)) + if id.hasAttachment[ForAttachment.type] => + Some((cond)) + case _ => None + } + } + + /** Encode/decode body of for yield loop as q"`yield`($tree)" */ + object Yield { + def apply(tree: Tree): Tree = + Apply(Ident(nme.YIELDkw).updateAttachment(ForAttachment), List(tree)) + + def unapply(tree: Tree): Option[Tree] = tree match { + case Apply(id @ Ident(nme.YIELDkw), List(tree)) + if id.hasAttachment[ForAttachment.type] => + Some(tree) + case _ => None + } + } + + /** Create tree for for-comprehension <for (enums) do body> or + * <for (enums) yield body> where mapName and flatMapName are chosen + * corresponding to whether this is a for-do or a for-yield. + * The creation performs the following rewrite rules: + * + * 1. + * + * for (P <- G) E ==> G.foreach (P => E) + * + * Here and in the following (P => E) is interpreted as the function (P => E) + * if P is a variable pattern and as the partial function { case P => E } otherwise. + * + * 2. + * + * for (P <- G) yield E ==> G.map (P => E) + * + * 3. + * + * for (P_1 <- G_1; P_2 <- G_2; ...) ... + * ==> + * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...) + * + * 4. + * + * for (P <- G; E; ...) ... + * => + * for (P <- G.filter (P => E); ...) ... + * + * 5. For N < MaxTupleArity: + * + * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...) + * ==> + * for (TupleN(P_1, P_2, ... P_N) <- + * for (x_1 @ P_1 <- G) yield { + * val x_2 @ P_2 = E_2 + * ... + * val x_N & P_N = E_N + * TupleN(x_1, ..., x_N) + * } ...) + * + * If any of the P_i are variable patterns, the corresponding `x_i @ P_i' is not generated + * and the variable constituting P_i is used instead of x_i + * + * @param mapName The name to be used for maps (either map or foreach) + * @param flatMapName The name to be used for flatMaps (either flatMap or foreach) + * @param enums The enumerators in the for expression + * @param body The body of the for expression + */ + def mkFor(enums: List[Tree], sugarBody: Tree)(implicit fresh: FreshNameCreator): Tree = { + val (mapName, flatMapName, body) = sugarBody match { + case Yield(tree) => (nme.map, nme.flatMap, tree) + case _ => (nme.foreach, nme.foreach, sugarBody) + } + + /* make a closure pat => body. + * The closure is assigned a transparent position with the point at pos.point and + * the limits given by pat and body. + */ + def makeClosure(pos: Position, pat: Tree, body: Tree): Tree = { + def wrapped = wrappingPos(List(pat, body)) + def splitpos = (if (pos != NoPosition) wrapped.withPoint(pos.point) else pos).makeTransparent + matchVarPattern(pat) match { + case Some((name, tpt)) => + Function( + List(atPos(pat.pos) { ValDef(Modifiers(PARAM), name.toTermName, tpt, EmptyTree) }), + body) setPos splitpos + case None => + atPos(splitpos) { + mkVisitor(List(CaseDef(pat, EmptyTree, body)), checkExhaustive = false) + } + } + } + + /* Make an application qual.meth(pat => body) positioned at `pos`. + */ + def makeCombination(pos: Position, meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree = + // ForAttachment on the method selection is used to differentiate + // result of for desugaring from a regular method call + Apply(Select(qual, meth) setPos qual.pos updateAttachment ForAttachment, + List(makeClosure(pos, pat, body))) setPos pos + + /* If `pat` is not yet a `Bind` wrap it in one with a fresh name */ + def makeBind(pat: Tree): Tree = pat match { + case Bind(_, _) => pat + case _ => Bind(freshTermName(), pat) setPos pat.pos + } + + /* A reference to the name bound in Bind `pat`. */ + def makeValue(pat: Tree): Tree = pat match { + case Bind(name, _) => Ident(name) setPos pat.pos.focus + } + + /* The position of the closure that starts with generator at position `genpos`. */ + def closurePos(genpos: Position) = + if (genpos == NoPosition) NoPosition + else { + val end = body.pos match { + case NoPosition => genpos.point + case bodypos => bodypos.end + } + rangePos(genpos.source, genpos.start, genpos.point, end) + } + + enums match { + case (t @ ValFrom(pat, rhs)) :: Nil => + makeCombination(closurePos(t.pos), mapName, rhs, pat, body) + case (t @ ValFrom(pat, rhs)) :: (rest @ (ValFrom(_, _) :: _)) => + makeCombination(closurePos(t.pos), flatMapName, rhs, pat, + mkFor(rest, sugarBody)) + case (t @ ValFrom(pat, rhs)) :: Filter(test) :: rest => + mkFor(ValFrom(pat, makeCombination(rhs.pos union test.pos, nme.withFilter, rhs, pat.duplicate, test)).setPos(t.pos) :: rest, sugarBody) + case (t @ ValFrom(pat, rhs)) :: rest => + val valeqs = rest.take(definitions.MaxTupleArity - 1).takeWhile { ValEq.unapply(_).nonEmpty } + assert(!valeqs.isEmpty) + val rest1 = rest.drop(valeqs.length) + val pats = valeqs map { case ValEq(pat, _) => pat } + val rhss = valeqs map { case ValEq(_, rhs) => rhs } + val defpat1 = makeBind(pat) + val defpats = pats map makeBind + val pdefs = (defpats, rhss).zipped flatMap mkPatDef + val ids = (defpat1 :: defpats) map makeValue + val rhs1 = mkFor( + List(ValFrom(defpat1, rhs).setPos(t.pos)), + Yield(Block(pdefs, atPos(wrappingPos(ids)) { mkTuple(ids) }) setPos wrappingPos(pdefs))) + val allpats = (pat :: pats) map (_.duplicate) + val pos1 = + if (t.pos == NoPosition) NoPosition + else rangePos(t.pos.source, t.pos.start, t.pos.point, rhs1.pos.end) + val vfrom1 = ValFrom(atPos(wrappingPos(allpats)) { mkTuple(allpats) }, rhs1).setPos(pos1) + mkFor(vfrom1 :: rest1, sugarBody) + case _ => + EmptyTree //may happen for erroneous input + + } + } + + /** Create tree for pattern definition <val pat0 = rhs> */ + def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = + mkPatDef(Modifiers(0), pat, rhs) + + /** Create tree for pattern definition <mods val pat0 = rhs> */ + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { + case Some((name, tpt)) => + List(atPos(pat.pos union rhs.pos) { + ValDef(mods, name.toTermName, tpt, rhs) + }) + + case None => + // in case there is exactly one variable x_1 in pattern + // val/var p = e ==> val/var x_1 = e.match (case p => (x_1)) + // + // in case there are zero or more than one variables in pattern + // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + // val/var x_1 = t$._1 + // ... + // val/var x_N = t$._N + + val rhsUnchecked = mkUnchecked(rhs) + + // TODO: clean this up -- there is too much information packked into mkPatDef's `pat` argument + // when it's a simple identifier (case Some((name, tpt)) -- above), + // pat should have the type ascription that was specified by the user + // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`) + // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => } + // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs + val (pat1, rhs1) = patvarTransformer.transform(pat) match { + // move the Typed ascription to the rhs + case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => + val rhsTypedUnchecked = + if (tpt.isEmpty) rhsUnchecked + else Typed(rhsUnchecked, tpt) setPos (rhs.pos union tpt.pos) + (expr, rhsTypedUnchecked) + case ok => + (ok, rhsUnchecked) + } + val vars = getVariables(pat1) + val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) { + Match( + rhs1, + List( + atPos(pat1.pos) { + CaseDef(pat1, EmptyTree, mkTuple(vars map (_._1) map Ident.apply)) + } + )) + } + vars match { + case List((vname, tpt, pos)) => + List(atPos(pat.pos union pos union rhs.pos) { + ValDef(mods, vname.toTermName, tpt, matchExpr) + }) + case _ => + val tmp = freshTermName() + val firstDef = + atPos(matchExpr.pos) { + ValDef(Modifiers(PrivateLocal | SYNTHETIC | ARTIFACT | (mods.flags & LAZY)), + tmp, TypeTree(), matchExpr) + } + var cnt = 0 + val restDefs = for ((vname, tpt, pos) <- vars) yield atPos(pos) { + cnt += 1 + ValDef(mods, vname.toTermName, tpt, Select(Ident(tmp), newTermName("_" + cnt))) + } + firstDef :: restDefs + } + } + + /** Create tree for for-comprehension generator <val pat0 <- rhs0> */ + def mkGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree)(implicit fresh: FreshNameCreator): Tree = { + val pat1 = patvarTransformer.transform(pat) + if (valeq) ValEq(pat1, rhs).setPos(pos) + else ValFrom(pat1, mkCheckIfRefutable(pat1, rhs)).setPos(pos) + } + + def mkCheckIfRefutable(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator) = + if (treeInfo.isVarPatternDeep(pat)) rhs + else { + val cases = List( + CaseDef(pat.duplicate, EmptyTree, Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + ) + val visitor = mkVisitor(cases, checkExhaustive = false, nme.CHECK_IF_REFUTABLE_STRING) + atPos(rhs.pos)(Apply(Select(rhs, nme.withFilter), visitor :: Nil)) + } + + /** If tree is a variable pattern, return Some("its name and type"). + * Otherwise return none */ + private def matchVarPattern(tree: Tree): Option[(Name, Tree)] = { + def wildType(t: Tree): Option[Tree] = t match { + case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree()) + case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt) + case _ => None + } + tree match { + case Ident(name) => Some((name, TypeTree())) + case Bind(name, body) => wildType(body) map (x => (name, x)) + case Typed(Ident(name), tpt) => Some((name, tpt)) + case _ => None + } + } + + /** Create visitor <x => x match cases> */ + def mkVisitor(cases: List[CaseDef], checkExhaustive: Boolean, prefix: String = "x$")(implicit fresh: FreshNameCreator): Tree = { + val x = freshTermName(prefix) + val id = Ident(x) + val sel = if (checkExhaustive) id else mkUnchecked(id) + Function(List(mkSyntheticParam(x)), Match(sel, cases)) + } + + /** Traverse pattern and collect all variable names with their types in buffer + * The variables keep their positions; whereas the pattern is converted to be + * synthetic for all nodes that contain a variable position. + */ + class GetVarTraverser extends Traverser { + val buf = new ListBuffer[(Name, Tree, Position)] + + def namePos(tree: Tree, name: Name): Position = + if (!tree.pos.isRange || name.containsName(nme.raw.DOLLAR)) tree.pos.focus + else { + val start = tree.pos.start + val end = start + name.decode.length + rangePos(tree.pos.source, start, start, end) + } + + override def traverse(tree: Tree): Unit = { + def seenName(name: Name) = buf exists (_._1 == name) + def add(name: Name, t: Tree) = if (!seenName(name)) buf += ((name, t, namePos(tree, name))) + val bl = buf.length + + tree match { + case Bind(nme.WILDCARD, _) => + super.traverse(tree) + + case Bind(name, Typed(tree1, tpt)) => + val newTree = if (treeInfo.mayBeTypePat(tpt)) TypeTree() else tpt.duplicate + add(name, newTree) + traverse(tree1) + + case Bind(name, tree1) => + // can assume only name range as position, as otherwise might overlap + // with binds embedded in pattern tree1 + add(name, TypeTree()) + traverse(tree1) + + case _ => + super.traverse(tree) + } + if (buf.length > bl) + tree setPos tree.pos.makeTransparent + } + def apply(tree: Tree) = { + traverse(tree) + buf.toList + } + } + + /** Returns list of all pattern variables, possibly with their types, + * without duplicates + */ + private def getVariables(tree: Tree): List[(Name, Tree, Position)] = + new GetVarTraverser apply tree + + /** Convert all occurrences of (lower-case) variables in a pattern as follows: + * x becomes x @ _ + * x: T becomes x @ (_: T) + */ + object patvarTransformer extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) => + atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD)))) + case Typed(id @ Ident(name), tpt) if (treeInfo.isVarPattern(id) && name != nme.WILDCARD) => + atPos(tree.pos.withPoint(id.pos.point)) { + Bind(name, atPos(tree.pos.withStart(tree.pos.point)) { + Typed(Ident(nme.WILDCARD), tpt) + }) + } + case Apply(fn @ Apply(_, _), args) => + treeCopy.Apply(tree, transform(fn), transformTrees(args)) + case Apply(fn, args) => + treeCopy.Apply(tree, fn, transformTrees(args)) + case Typed(expr, tpt) => + treeCopy.Typed(tree, transform(expr), tpt) + case Bind(name, body) => + treeCopy.Bind(tree, name, transform(body)) + case Alternative(_) | Star(_) => + super.transform(tree) + case _ => + tree + } + } + + // annotate the expression with @unchecked + def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { + // This can't be "Annotated(New(UncheckedClass), expr)" because annotations + // are very picky about things and it crashes the compiler with "unexpected new". + Annotated(New(scalaDot(tpnme.unchecked), Nil), expr) + } + + def mkSyntheticParam(pname: TermName) = + ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree) + + def mkCast(tree: Tree, pt: Type): Tree = + atPos(tree.pos)(mkAsInstanceOf(tree, pt, any = true, wrapInApply = false)) } diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index a6556fc22d..b7d7d4df88 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import Flags._ @@ -17,7 +18,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType } + import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, uncheckedStableClass, isBlackboxMacroBundleType, isWhiteboxContextType } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -70,6 +71,90 @@ abstract class TreeInfo { false } + /** Is `tree` a path, defined as follows? (Spec: 3.1 Paths) + * + * - The empty path ε (which cannot be written explicitly in user programs). + * - C.this, where C references a class. + * - p.x where p is a path and x is a stable member of p. + * - C.super.x or C.super[M].x where C references a class + * and x references a stable member of the super class or designated parent class M of C. + * + * NOTE: Trees with errors are (mostly) excluded. + * + * Path ::= StableId | [id ‘.’] this + * + */ + def isPath(tree: Tree, allowVolatile: Boolean): Boolean = + tree match { + // Super is not technically a path. + // However, syntactically, it can only occur nested in a Select. + // This gives a nicer definition of isStableIdentifier that's equivalent to the spec's. + // must consider Literal(_) a path for typedSingletonTypeTree + case EmptyTree | Literal(_) => true + case This(_) | Super(_, _) => symOk(tree.symbol) + case _ => isStableIdentifier(tree, allowVolatile) + } + + /** Is `tree` a stable identifier, a path which ends in an identifier? + * + * StableId ::= id + * | Path ‘.’ id + * | [id ’.’] ‘super’ [‘[’ id ‘]’] ‘.’ id + */ + def isStableIdentifier(tree: Tree, allowVolatile: Boolean): Boolean = + tree match { + case i @ Ident(_) => isStableIdent(i, allowVolatile) + case Select(qual, _) => isStableMemberOf(tree.symbol, qual, allowVolatile) && isPath(qual, allowVolatile) + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` + free.symbol.hasStableFlag && isPath(free, allowVolatile) + case _ => false + } + + private def symOk(sym: Symbol) = sym != null && !sym.isError && sym != NoSymbol + private def typeOk(tp: Type) = tp != null && ! tp.isError + + /** Assuming `sym` is a member of `tree`, is it a "stable member"? + * + * Stable members are packages or members introduced + * by object definitions or by value definitions of non-volatile types (§3.6). + */ + def isStableMemberOf(sym: Symbol, tree: Tree, allowVolatile: Boolean): Boolean = ( + symOk(sym) && (!sym.isTerm || (sym.isStable && (allowVolatile || !sym.hasVolatileType))) && + typeOk(tree.tpe) && (allowVolatile || !hasVolatileType(tree)) && !definitions.isByNameParamType(tree.tpe) + ) + + private def isStableIdent(tree: Ident, allowVolatile: Boolean): Boolean = ( + symOk(tree.symbol) + && tree.symbol.isStable + && !definitions.isByNameParamType(tree.tpe) + && (allowVolatile || !tree.symbol.hasVolatileType) // TODO SPEC: not required by spec + ) + + /** Is `tree`'s type volatile? (Ignored if its symbol has the @uncheckedStable annotation.) + */ + def hasVolatileType(tree: Tree): Boolean = + symOk(tree.symbol) && tree.tpe.isVolatile && !tree.symbol.hasAnnotation(uncheckedStableClass) + + /** Is `tree` either a non-volatile type, + * or a path that does not include any of: + * - a reference to a mutable variable/field + * - a reference to a by-name parameter + * - a member selection on a volatile type (Spec: 3.6 Volatile Types)? + * + * Such a tree is a suitable target for type selection. + */ + def admitsTypeSelection(tree: Tree): Boolean = isPath(tree, allowVolatile = false) + + /** Is `tree` admissible as a stable identifier pattern (8.1.5 Stable Identifier Patterns)? + * + * We disregard volatility, as it's irrelevant in patterns (SI-6815) + */ + def isStableIdentifierPattern(tree: Tree): Boolean = isStableIdentifier(tree, allowVolatile = true) + + // TODO SI-5304 tighten this up so we don't elide side effect in module loads + def isQualifierSafeToElide(tree: Tree): Boolean = isExprSafeToInline(tree) + /** Is tree an expression which can be inlined without affecting program semantics? * * Note that this is not called "isExprPure" since purity (lack of side-effects) @@ -113,80 +198,70 @@ abstract class TreeInfo { false } - @deprecated("Use isExprSafeToInline instead", "2.10.0") - def isPureExpr(tree: Tree) = isExprSafeToInline(tree) + /** As if the name of the method didn't give it away, + * this logic is designed around issuing helpful + * warnings and minimizing spurious ones. That means + * don't reuse it for important matters like inlining + * decisions. + */ + def isPureExprForWarningPurposes(tree: Tree): Boolean = tree match { + case Typed(expr, _) => isPureExprForWarningPurposes(expr) + case EmptyTree | Literal(Constant(())) => false + case _ => + def isWarnableRefTree = tree match { + case t: RefTree => isExprSafeToInline(t.qualifier) && t.symbol != null && t.symbol.isAccessor + case _ => false + } + def isWarnableSymbol = { + val sym = tree.symbol + (sym == null) || !(sym.isModule || sym.isLazy || definitions.isByNameParamType(sym.tpe_*)) || { + debuglog("'Pure' but side-effecting expression in statement position: " + tree) + false + } + } - def zipMethodParamsAndArgs(params: List[Symbol], args: List[Tree]): List[(Symbol, Tree)] = - mapMethodParamsAndArgs(params, args)((param, arg) => ((param, arg))) + ( !tree.isErrorTyped + && (isExprSafeToInline(tree) || isWarnableRefTree) + && isWarnableSymbol + ) + } 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 + 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" - ) + global.devWarning( + s"""|Mismatch trying to zip method parameters and argument list: + | params = $params + | args = $args""".stripMargin) false } if (plen == alen) foreach2(params, args)(f) - else if (params.isEmpty) return fail + 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 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 + 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 = dissectApplied(fn).applyDepth - // 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 { @@ -201,7 +276,7 @@ abstract class TreeInfo { 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 + def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(sym.setterName) != NoSymbol tree match { case Ident(_) => isVar @@ -211,20 +286,24 @@ abstract class TreeInfo { } } + def isDefaultGetter(tree: Tree) = { + tree.symbol != null && tree.symbol.isDefaultGetter + } + /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ - def isSelfConstrCall(tree: Tree): Boolean = tree match { - case Applied(Ident(nme.CONSTRUCTOR), _, _) => true - case Applied(Select(This(_), nme.CONSTRUCTOR), _, _) => true - case _ => false + def isSelfConstrCall(tree: Tree): Boolean = dissectApplied(tree).core match { + case Ident(nme.CONSTRUCTOR) => true + case Select(This(_), nme.CONSTRUCTOR) => true + case _ => false } /** Is tree a super constructor call? */ - def isSuperConstrCall(tree: Tree): Boolean = tree match { - case Applied(Select(Super(_, _), nme.CONSTRUCTOR), _, _) => true - case _ => false + def isSuperConstrCall(tree: Tree): Boolean = dissectApplied(tree).core match { + case Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false } /** @@ -330,9 +409,66 @@ abstract class TreeInfo { case x: Ident => !x.isBackquoted && nme.isVariableName(x.name) case _ => false } - def isDeprecatedIdentifier(tree: Tree): Boolean = tree match { - case x: Ident => !x.isBackquoted && nme.isDeprecatedIdentifierName(x.name) - case _ => false + + /** Does the tree have a structure similar to typechecked trees? */ + private[internal] def detectTypecheckedTree(tree: Tree) = + tree.hasExistingSymbol || tree.exists { + case dd: DefDef => dd.mods.hasAccessorFlag || dd.mods.isSynthetic // for untypechecked trees + case md: MemberDef => md.hasExistingSymbol + case _ => false + } + + /** Recover template body to parsed state */ + private[internal] def untypecheckedTemplBody(templ: Template) = + untypecheckedTreeBody(templ, templ.body) + + /** Recover block body to parsed state */ + private[internal] def untypecheckedBlockBody(block: Block) = + untypecheckedTreeBody(block, block.stats) + + /** Recover tree body to parsed state */ + private[internal] def untypecheckedTreeBody(tree: Tree, tbody: List[Tree]) = { + def filterBody(body: List[Tree]) = body filter { + case _: ValDef | _: TypeDef => true + // keep valdef or getter for val/var + case dd: DefDef if dd.mods.hasAccessorFlag => !nme.isSetterName(dd.name) && !tbody.exists { + case vd: ValDef => dd.name == vd.name.dropLocal + case _ => false + } + case md: MemberDef => !md.mods.isSynthetic + case tree => true + } + + def lazyValDefRhs(body: Tree) = + body match { + case Block(List(Assign(_, rhs)), _) => rhs + case _ => body + } + + def recoverBody(body: List[Tree]) = body map { + case vd @ ValDef(vmods, vname, _, vrhs) if nme.isLocalName(vname) => + tbody find { + case dd: DefDef => dd.name == vname.dropLocal + case _ => false + } map { dd => + val DefDef(dmods, dname, _, _, _, drhs) = dd + // get access flags from DefDef + val vdMods = (vmods &~ Flags.AccessFlags) | (dmods & Flags.AccessFlags).flags + // for most cases lazy body should be taken from accessor DefDef + val vdRhs = if (vmods.isLazy) lazyValDefRhs(drhs) else vrhs + copyValDef(vd)(mods = vdMods, name = dname, rhs = vdRhs) + } getOrElse (vd) + // for abstract and some lazy val/vars + case dd @ DefDef(mods, name, _, _, tpt, rhs) if mods.hasAccessorFlag => + // transform getter mods to field + val vdMods = (if (!mods.hasStableFlag) mods | Flags.MUTABLE else mods &~ Flags.STABLE) &~ Flags.ACCESSOR + ValDef(vdMods, name, tpt, rhs) + case tr => tr + } + + if (detectTypecheckedTree(tree)) { + recoverBody(filterBody(tbody)) + } else tbody } /** The first constructor definitions in `stats` */ @@ -351,6 +487,9 @@ abstract class TreeInfo { def preSuperFields(stats: List[Tree]): List[ValDef] = stats collect { case vd: ValDef if isEarlyValDef(vd) => vd } + def hasUntypedPreSuperFields(stats: List[Tree]): Boolean = + preSuperFields(stats) exists (_.tpt.isEmpty) + def isEarlyDef(tree: Tree) = tree match { case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER case ValDef(mods, _, _, _) => mods hasFlag PRESUPER @@ -362,11 +501,6 @@ abstract class TreeInfo { 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) @@ -389,15 +523,17 @@ abstract class TreeInfo { case _ => false } + /** Translates an Assign(_, _) node to AssignOrNamedArg(_, _) if + * the lhs is a simple ident. Otherwise returns unchanged. + */ + def assignmentToMaybeNamedArg(tree: Tree) = tree match { + case t @ Assign(id: Ident, rhs) => atPos(t.pos)(AssignOrNamedArg(id, rhs)) + case t => t + } + /** Is name a left-associative operator? */ def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.endChar != ':') - /** 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 - } - /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) */ def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation definitions.SwitchClass @@ -413,8 +549,15 @@ abstract class TreeInfo { /** Is this argument node of the form <expr> : _* ? */ def isWildcardStarArg(tree: Tree): Boolean = tree match { - case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true - case _ => false + case WildcardStarArg(_) => true + case _ => false + } + + object WildcardStarArg { + def unapply(tree: Tree): Option[Tree] = tree match { + case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => Some(expr) + case _ => None + } } /** If this tree has type parameters, those. Otherwise Nil. @@ -450,6 +593,8 @@ abstract class TreeInfo { case _ => false } + private def hasNoSymbol(t: Tree) = t.symbol == null || t.symbol == NoSymbol + /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know * whether the user provided cases are exhaustive. */ def isSyntheticDefaultCase(cdef: CaseDef) = cdef match { @@ -458,14 +603,19 @@ abstract class TreeInfo { } /** Does this CaseDef catch Throwable? */ - def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe) + def catchesThrowable(cdef: CaseDef) = ( + cdef.guard.isEmpty && (unbind(cdef.pat) match { + case Ident(nme.WILDCARD) => true + case i@Ident(name) => hasNoSymbol(i) + case _ => false + }) + ) - /** 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 CaseDef synthetically generated, e.g. by `MatchTranslation.translateTry`? */ + def isSyntheticCase(cdef: CaseDef) = cdef.pat.exists { + case dt: DefTree => dt.symbol.isSynthetic + case _ => false + } /** Is this pattern node a catch-all or type-test pattern? */ def isCatchCase(cdef: CaseDef) = cdef match { @@ -490,7 +640,7 @@ abstract class TreeInfo { tp match { case TypeRef(pre, sym, args) => - args.isEmpty && (sym.owner.isPackageClass || isSimple(pre)) + args.isEmpty && (sym.isTopLevel || isSimple(pre)) case NoPrefix => true case _ => @@ -529,11 +679,12 @@ abstract class TreeInfo { * case Extractor(a @ (b, c)) => 2 * }}} */ - def effectivePatternArity(args: List[Tree]): Int = (args.map(unbind) match { - case Apply(fun, xs) :: Nil if isTupleSymbol(fun.symbol) => xs - case xs => xs - }).length + def effectivePatternArity(args: List[Tree]): Int = flattenedPatternArgs(args).length + def flattenedPatternArgs(args: List[Tree]): List[Tree] = args map unbind match { + case build.SyntacticTuple(xs) :: Nil => xs + case xs => xs + } // used in the symbols for labeldefs and valdefs emitted by the pattern matcher // tailcalls, cps,... use this flag combination to detect translated matches @@ -543,6 +694,10 @@ abstract class TreeInfo { def isSynthCaseSymbol(sym: Symbol) = sym hasAllFlags SYNTH_CASE_FLAGS def hasSynthCaseSymbol(t: Tree) = t.symbol != null && isSynthCaseSymbol(t.symbol) + def isTraitRef(tree: Tree): Boolean = { + val sym = if (tree.tpe != null) tree.tpe.typeSymbol else null + ((sym ne null) && sym.initialize.isTrait) + } /** Applications in Scala can have one of the following shapes: * @@ -625,19 +780,6 @@ abstract class TreeInfo { } loop(tree) } - - /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _) - * has depth 3. Continues through type applications (without counting them.) - */ - def applyDepth: Int = { - def loop(tree: Tree): Int = tree match { - case Apply(fn, _) => 1 + loop(fn) - case TypeApply(fn, _) => loop(fn) - case AppliedTypeTree(fn, _) => loop(fn) - case _ => 0 - } - loop(tree) - } } /** Returns a wrapper that knows how to destructure and analyze applications. @@ -654,6 +796,8 @@ abstract class TreeInfo { * For advanced use, call `dissectApplied` explicitly and use its methods instead of pattern matching. */ object Applied { + def apply(tree: Tree): Applied = new Applied(tree) + def unapply(applied: Applied): Option[(Tree, List[Tree], List[List[Tree]])] = Some((applied.core, applied.targs, applied.argss)) @@ -665,13 +809,26 @@ abstract class TreeInfo { * 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 - } + case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name) + case Annotated(_, tree1) :: _ => firstDefinesClassOrObject(List(tree1), name) + case ModuleDef(_, `name`, _) :: _ => true + case ClassDef(_, `name`, _, _) :: _ => true + case _ => false + } + /** Locates the synthetic Apply node corresponding to an extractor's call to + * unapply (unwrapping nested Applies) and returns the fun part of that Apply. + */ + object Unapplied { + // Duplicated with `spliceApply` + def unapply(tree: Tree): Option[Tree] = tree match { + // SI-7868 Admit Select() to account for numeric widening, e.g. <unappplySelector>.toInt + case Apply(fun, (Ident(nme.SELECTOR_DUMMY)| Select(Ident(nme.SELECTOR_DUMMY), _)) :: Nil) + => Some(fun) + case Apply(fun, _) => unapply(fun) + case _ => None + } + } /** Is this file the body of a compilation unit which should not * have Predef imported? @@ -726,12 +883,6 @@ abstract class TreeInfo { 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) @@ -755,8 +906,24 @@ abstract class TreeInfo { } def unapply(tree: Tree) = refPart(tree) match { - case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs)) - case _ => None + case ref: RefTree => { + val qual = ref.qualifier + val isBundle = definitions.isMacroBundleType(qual.tpe) + val isBlackbox = + if (isBundle) isBlackboxMacroBundleType(qual.tpe) + else ref.symbol.paramss match { + case (c :: Nil) :: _ if isWhiteboxContextType(c.info) => false + case _ => true + } + val owner = + if (isBundle) qual.tpe.typeSymbol + else { + val qualSym = if (qual.hasSymbolField) qual.symbol else NoSymbol + if (qualSym.isModule) qualSym.moduleClass else qualSym + } + Some((isBundle, isBlackbox, owner, ref.symbol, dissectApplied(tree).targs)) + } + case _ => None } } @@ -766,4 +933,14 @@ abstract class TreeInfo { case tree: RefTree => true case _ => false }) + + def isMacroApplication(tree: Tree): Boolean = !tree.isDef && { + val sym = tree.symbol + sym != null && sym.isTermMacro && !sym.isErroneous + } + + def isMacroApplicationOrBlock(tree: Tree): Boolean = tree match { + case Block(_, expr) => isMacroApplicationOrBlock(expr) + case tree => isMacroApplication(tree) + } } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 0e5985face..9dc4baee32 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -3,17 +3,37 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import Flags._ -import scala.collection.mutable.{ListBuffer, LinkedHashSet} +import pickling.PickleFormat._ +import scala.collection.{ mutable, immutable } import util.Statistics -trait Trees extends api.Trees { self: SymbolTable => +trait Trees extends api.Trees { + self: SymbolTable => private[scala] var nodeCount = 0 + protected def treeLine(t: Tree): String = + if (t.pos.isDefined && t.pos.isRange) t.pos.lineContent.drop(t.pos.column - 1).take(t.pos.end - t.pos.start + 1) + else t.summaryString + + protected def treeStatus(t: Tree, enclosingTree: Tree = null) = { + val parent = if (enclosingTree eq null) " " else " P#%5s".format(enclosingTree.id) + + "[L%4s%8s] #%-6s %-15s %-10s // %s".format(t.pos.line, parent, t.id, t.pos.show, t.shortClass, treeLine(t)) + } + protected def treeSymStatus(t: Tree) = { + val line = if (t.pos.isDefined) "line %-4s".format(t.pos.line) else " " + "#%-5s %s %-10s // %s".format(t.id, line, t.shortClass, + if (t.symbol ne NoSymbol) "(" + t.symbol.fullLocationString + ")" + else treeLine(t) + ) + } + abstract class Tree extends TreeContextApiImpl with Attachable with Product { val id = nodeCount // TODO: add to attachment? nodeCount += 1 @@ -24,18 +44,24 @@ trait Trees extends api.Trees { self: SymbolTable => private[this] var rawtpe: Type = _ final def tpe = rawtpe - def tpe_=(t: Type) = rawtpe = t + @deprecated("Use setType", "2.11.0") def tpe_=(t: Type): Unit = setType(t) + + def clearType(): this.type = this setType null def setType(tp: Type): this.type = { rawtpe = tp; this } def defineType(tp: Type): this.type = setType(tp) def symbol: Symbol = null //!!!OPT!!! symbol is about 3% of hot compile times -- megamorphic dispatch? def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) } def setSymbol(sym: Symbol): this.type = { symbol = sym; this } - def hasSymbol = false + def hasSymbolField = false + @deprecated("Use hasSymbolField", "2.11.0") def hasSymbol = hasSymbolField def isDef = false def isEmpty = false + def nonEmpty = !isEmpty + + def canHaveAttrs = true /** The canonical way to test if a Tree represents a term. */ @@ -62,7 +88,7 @@ trait Trees extends api.Trees { self: SymbolTable => private[scala] def copyAttrs(tree: Tree): this.type = { rawatt = tree.rawatt tpe = tree.tpe - if (hasSymbol) symbol = tree.symbol + if (hasSymbolField) symbol = tree.symbol this } @@ -73,7 +99,7 @@ trait Trees extends api.Trees { self: SymbolTable => (duplicator transform this).asInstanceOf[this.type] } - abstract class TreeContextApiImpl extends TreeContextApi { this: Tree => + abstract class TreeContextApiImpl extends TreeApi { this: Tree => override def orElse(alt: => Tree) = if (!isEmpty) this else alt @@ -132,11 +158,11 @@ trait Trees extends api.Trees { self: SymbolTable => productIterator.toList flatMap subtrees } - override def freeTerms: List[FreeTermSymbol] = freeSyms[FreeTermSymbol](_.isFreeTerm, _.termSymbol) - override def freeTypes: List[FreeTypeSymbol] = freeSyms[FreeTypeSymbol](_.isFreeType, _.typeSymbol) + def freeTerms: List[FreeTermSymbol] = freeSyms[FreeTermSymbol](_.isFreeTerm, _.termSymbol) + def freeTypes: List[FreeTypeSymbol] = freeSyms[FreeTypeSymbol](_.isFreeType, _.typeSymbol) private def freeSyms[S <: Symbol](isFree: Symbol => Boolean, symOfType: Type => Symbol): List[S] = { - val s = scala.collection.mutable.LinkedHashSet[S]() + val s = mutable.LinkedHashSet[S]() def addIfFree(sym: Symbol): Unit = if (sym != null && isFree(sym)) s += sym.asInstanceOf[S] for (t <- this) { addIfFree(t.symbol) @@ -149,17 +175,17 @@ trait Trees extends api.Trees { self: SymbolTable => s.toList } - override def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree = + def substituteSymbols(from: List[Symbol], to: List[Symbol]): Tree = new TreeSymSubstituter(from, to)(this) - override def substituteTypes(from: List[Symbol], to: List[Type]): Tree = + def substituteTypes(from: List[Symbol], to: List[Type]): Tree = new TreeTypeSubstituter(from, to)(this) - override def substituteThis(clazz: Symbol, to: Tree): Tree = + def substituteThis(clazz: Symbol, to: Tree): Tree = new ThisSubstituter(clazz, to) transform this - def hasSymbolWhich(f: Symbol => Boolean) = - (symbol ne null) && (symbol ne NoSymbol) && f(symbol) + def hasExistingSymbol = (symbol ne null) && (symbol ne NoSymbol) + def hasSymbolWhich(f: Symbol => Boolean) = hasExistingSymbol && f(symbol) def isErroneous = (tpe ne null) && tpe.isErroneous def isTyped = (tpe ne null) && !tpe.isErroneous @@ -209,13 +235,16 @@ trait Trees extends api.Trees { self: SymbolTable => trait TypTree extends Tree with TypTreeApi - abstract class SymTree extends Tree with SymTreeContextApi { - override def hasSymbol = true + abstract class SymTree extends Tree with SymTreeApi { + override def hasSymbolField = true override var symbol: Symbol = NoSymbol } trait NameTree extends Tree with NameTreeApi { def name: Name + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName } trait RefTree extends SymTree with NameTree with RefTreeApi { @@ -223,19 +252,24 @@ trait Trees extends api.Trees { self: SymbolTable => def name: Name } + object RefTree extends RefTreeExtractor { + def apply(qualifier: Tree, name: Name): RefTree = qualifier match { + case EmptyTree => + Ident(name) + case qual if qual.isTerm => + Select(qual, name) + case qual if qual.isType => + assert(name.isTypeName, s"qual = $qual, name = $name") + SelectFromTypeTree(qual, name.toTypeName) + } + def unapply(refTree: RefTree): Option[(Tree, Name)] = Some((refTree.qualifier, refTree.name)) + } + abstract class DefTree extends SymTree with NameTree with DefTreeApi { def name: Name override def isDef = true } - case object EmptyTree extends TermTree { - val asList = List(this) - 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 { @@ -262,37 +296,91 @@ trait Trees extends api.Trees { self: SymbolTable => case class ClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template) extends ImplDef with ClassDefApi - object ClassDef extends ClassDefExtractor + object ClassDef extends ClassDefExtractor { + /** @param sym the class symbol + * @param impl the implementation template + * @return the class definition + */ + def apply(sym: Symbol, impl: Template): ClassDef = + atPos(sym.pos) { + ClassDef(Modifiers(sym.flags), + sym.name.toTypeName, + sym.typeParams map TypeDef.apply, + impl) setSymbol sym + } + + /** @param sym the class symbol + * @param body trees that constitute the body of the class + * @return the class definition + */ + def apply(sym: Symbol, body: List[Tree]): ClassDef = + ClassDef(sym, Template(sym, body)) + } case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) extends ImplDef with ModuleDefApi - object ModuleDef extends ModuleDefExtractor + object ModuleDef extends ModuleDefExtractor { + /** + * @param sym the class symbol + * @param impl the implementation template + */ + def apply(sym: Symbol, impl: Template): ModuleDef = + atPos(sym.pos) { + ModuleDef(Modifiers(sym.flags), sym.name.toTermName, impl) setSymbol sym + } + } abstract class ValOrDefDef extends MemberDef with ValOrDefDefApi { - def name: Name + def name: TermName def tpt: Tree def rhs: Tree } + object ValOrDefDef { + def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match { + case ValDef(mods, name, tpt, rhs) => Some((mods, name, tpt, rhs)) + case DefDef(mods, name, _, _, tpt, rhs) => Some((mods, name, tpt, rhs)) + case _ => None + } + } + case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef with ValDefApi - object ValDef extends ValDefExtractor + object ValDef extends ValDefExtractor { + def apply(sym: Symbol): ValDef = newValDef(sym, EmptyTree)() + def apply(sym: Symbol, rhs: Tree): ValDef = newValDef(sym, rhs)() + } - case class DefDef(mods: Modifiers, name: Name, tparams: List[TypeDef], + case class DefDef(mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef with DefDefApi - object DefDef extends DefDefExtractor + object DefDef extends DefDefExtractor { + def apply(sym: Symbol, rhs: Tree): DefDef = newDefDef(sym, rhs)() + def apply(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = newDefDef(sym, rhs)(vparamss = vparamss) + def apply(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = newDefDef(sym, rhs)(mods = mods) + def apply(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = newDefDef(sym, rhs)(mods = mods, vparamss = vparamss) + def apply(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = newDefDef(sym, rhs(sym.info.paramss))() + } case class TypeDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) extends MemberDef with TypeDefApi - object TypeDef extends TypeDefExtractor + object TypeDef extends TypeDefExtractor { + /** A TypeDef node which defines abstract type or type parameter for given `sym` */ + def apply(sym: Symbol): TypeDef = newTypeDef(sym, TypeBoundsTree(sym))() + def apply(sym: Symbol, rhs: Tree): TypeDef = newTypeDef(sym, rhs)() + } case class LabelDef(name: TermName, params: List[Ident], rhs: Tree) extends DefTree with TermTree with LabelDefApi - object LabelDef extends LabelDefExtractor + object LabelDef extends LabelDefExtractor { + def apply(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = + atPos(sym.pos) { + LabelDef(sym.name.toTermName, params map Ident, rhs) setSymbol sym + } + } case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) extends ImportSelectorApi object ImportSelector extends ImportSelectorExtractor { val wild = ImportSelector(nme.WILDCARD, -1, null, -1) - val wildList = List(wild) + val wildList = List(wild) // OPT This list is shared for performance. } case class Import(expr: Tree, selectors: List[ImportSelector]) @@ -416,6 +504,16 @@ trait Trees extends api.Trees { self: SymbolTable => def ApplyConstructor(tpt: Tree, args: List[Tree]) = Apply(Select(New(tpt), nme.CONSTRUCTOR), args) + // Creates a constructor call from the constructor symbol. This is + // to avoid winding up with an OverloadedType for the constructor call. + def NewFromConstructor(constructor: Symbol, args: Tree*) = { + assert(constructor.isConstructor, constructor) + val instance = New(TypeTree(constructor.owner.tpe)) + val init = Select(instance, nme.CONSTRUCTOR) setSymbol constructor + + Apply(init, args.toList) + } + case class ApplyDynamic(qual: Tree, args: List[Tree]) extends SymTree with TermTree case class Super(qual: Tree, mix: TypeName) extends TermTree with SuperApi { @@ -436,9 +534,9 @@ trait Trees extends api.Trees { self: SymbolTable => } object Select extends SelectExtractor - case class Ident(name: Name) extends RefTree with IdentContextApi { + case class Ident(name: Name) extends RefTree with IdentApi { def qualifier: Tree = EmptyTree - def isBackquoted = this.attachments.get[BackquotedIdentifierAttachment.type].isDefined + def isBackquoted = this.hasAttachment[BackquotedIdentifierAttachment.type] } object Ident extends IdentExtractor @@ -489,11 +587,11 @@ trait Trees extends api.Trees { self: SymbolTable => extends TypTree with TypeBoundsTreeApi object TypeBoundsTree extends TypeBoundsTreeExtractor - case class ExistentialTypeTree(tpt: Tree, whereClauses: List[Tree]) + case class ExistentialTypeTree(tpt: Tree, whereClauses: List[MemberDef]) extends TypTree with ExistentialTypeTreeApi object ExistentialTypeTree extends ExistentialTypeTreeExtractor - case class TypeTree() extends TypTree with TypeTreeContextApi { + case class TypeTree() extends TypTree with TypeTreeApi { private var orig: Tree = null /** Was this type tree originally empty? That is, does it now contain * an inferred type that must be forgotten in `resetAttrs` to @@ -511,7 +609,7 @@ trait Trees extends api.Trees { self: SymbolTable => case t => t } - orig = followOriginal(tree); setPos(tree.pos); + orig = followOriginal(tree); setPos(tree.pos) this } @@ -536,8 +634,16 @@ trait Trees extends api.Trees { self: SymbolTable => object TypeTree extends TypeTreeExtractor def TypeTree(tp: Type): TypeTree = TypeTree() setType tp + private def TypeTreeMemberType(sym: Symbol): TypeTree = { + // Needed for pos/t4970*.scala. See SI-7853 + val resType = (if (sym.isLocalToBlock) sym.tpe else (sym.owner.thisType memberType sym)).finalResultType + atPos(sym.pos.focus)(TypeTree(resType)) + } - override type TreeCopier <: InternalTreeCopierOps + def TypeBoundsTree(bounds: TypeBounds): TypeBoundsTree = TypeBoundsTree(TypeTree(bounds.lo), TypeTree(bounds.hi)) + def TypeBoundsTree(sym: Symbol): TypeBoundsTree = atPos(sym.pos)(TypeBoundsTree(sym.info.bounds)) + + override type TreeCopier >: Null <: InternalTreeCopierOps abstract class InternalTreeCopierOps extends TreeCopierOps { def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]): ApplyDynamic def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]): ArrayValue @@ -603,6 +709,7 @@ trait Trees extends api.Trees { self: SymbolTable => case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) case _: ApplyImplicitView => new ApplyImplicitView(fun, args) // TODO: ApplyConstructor ??? + case self.pendingSuperCall => self.pendingSuperCall case _ => new Apply(fun, args) }).copyAttrs(tree) def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = @@ -615,6 +722,8 @@ trait Trees extends api.Trees { self: SymbolTable => new Select(qualifier, selector).copyAttrs(tree) def Ident(tree: Tree, name: Name) = new Ident(name) copyAttrs tree + def RefTree(tree: Tree, qualifier: Tree, selector: Name) = + self.RefTree(qualifier, selector) copyAttrs tree def ReferenceToBoxed(tree: Tree, idt: Ident) = new ReferenceToBoxed(idt).copyAttrs(tree) def Literal(tree: Tree, value: Constant) = @@ -633,7 +742,7 @@ trait Trees extends api.Trees { self: SymbolTable => 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]) = + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[MemberDef]) = new ExistentialTypeTree(tpt, whereClauses).copyAttrs(tree) } @@ -805,6 +914,11 @@ trait Trees extends api.Trees { self: SymbolTable => if name0 == name => t case _ => treeCopy.Ident(tree, name) } + def RefTree(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ Select(qualifier0, selector0) + if (qualifier0 == qualifier) && (selector0 == selector) => t + case _ => treeCopy.RefTree(tree, qualifier, selector) + } def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match { case t @ ReferenceToBoxed(idt0) if (idt0 == idt) => t @@ -849,7 +963,7 @@ trait Trees extends api.Trees { self: SymbolTable => if (lo0 == lo) && (hi0 == hi) => t case _ => treeCopy.TypeBoundsTree(tree, lo, hi) } - def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[Tree]) = tree match { + def ExistentialTypeTree(tree: Tree, tpt: Tree, whereClauses: List[MemberDef]) = tree match { case t @ ExistentialTypeTree(tpt0, whereClauses0) if (tpt0 == tpt) && (whereClauses0 == whereClauses) => t case _ => treeCopy.ExistentialTypeTree(tree, tpt, whereClauses) @@ -866,7 +980,6 @@ trait Trees extends api.Trees { self: SymbolTable => /** 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 --------------------------------------- @@ -912,6 +1025,7 @@ trait Trees extends api.Trees { self: SymbolTable => if (flags1 == flags) this else Modifiers(flags1, privateWithin, annotations) setPositions positions } + def | (flag: Int): Modifiers = this | flag.toLong def | (flag: Long): Modifiers = { val flags1 = flags | flag if (flags1 == flags) this @@ -924,102 +1038,83 @@ trait Trees extends api.Trees { self: SymbolTable => 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 mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = { + val newAnns = f(annotations) + if (annotations == newAnns) this + else Modifiers(flags, privateWithin, newAnns) setPositions positions + } override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions) } - object Modifiers extends ModifiersCreator + object Modifiers extends ModifiersExtractor implicit val ModifiersTag = ClassTag[Modifiers](classOf[Modifiers]) // ---- values and creators --------------------------------------- - /** @param sym the class symbol - * @return the implementation template + /** @param sym the template's symbol + * @param body trees that constitute the body of the template + * @return the template */ - def ClassDef(sym: Symbol, impl: Template): ClassDef = + def Template(sym: Symbol, body: List[Tree]): Template = { atPos(sym.pos) { - ClassDef(Modifiers(sym.flags), - sym.name.toTypeName, - sym.typeParams map TypeDef, - impl) setSymbol sym + Template(sym.info.parents map TypeTree, + if (sym.thisSym == sym) noSelfType else ValDef(sym), + body) } - - /** - * @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) - - /** A DefDef with original trees attached to the TypeTree of each parameter */ - def DefDef(sym: Symbol, mods: Modifiers, originalParamTpts: Symbol => Tree, rhs: Tree): DefDef = { - val paramms = mapParamss(sym){ sym => - val vd = ValDef(sym, EmptyTree) - (vd.tpt : @unchecked) match { - case tt: TypeTree => tt setOriginal (originalParamTpts(sym) setPos sym.pos.focus) + trait CannotHaveAttrs extends Tree { + super.setPos(NoPosition) + super.setType(NoType) + + override def canHaveAttrs = false + override def setPos(pos: Position) = { requireLegal(pos, NoPosition, "pos"); this } + override def pos_=(pos: Position) = setPos(pos) + override def setType(t: Type) = { requireLegal(t, NoType, "tpe"); this } + override def tpe_=(t: Type) = setType(t) + + private def requireLegal(value: Any, allowed: Any, what: String) = ( + if (value != allowed) { + log(s"can't set $what for $self to value other than $allowed") + if (settings.debug && settings.developer) + (new Throwable).printStackTrace } - vd - } - DefDef(sym, mods, paramms, 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 - } + case object EmptyTree extends TermTree with CannotHaveAttrs { override def isEmpty = true; val asList = List(this) } + object noSelfType extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) with CannotHaveAttrs + object pendingSuperCall extends Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List()) with CannotHaveAttrs + + @deprecated("Use `noSelfType` instead", "2.11.0") lazy val emptyValDef = noSelfType + + def newValDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TermName = sym.name.toTermName, + tpt: Tree = TypeTreeMemberType(sym) + ): ValDef = ( + atPos(sym.pos)(ValDef(mods, name, tpt, rhs)) setSymbol sym + ) + + def newDefDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TermName = sym.name.toTermName, + tparams: List[TypeDef] = sym.typeParams map TypeDef.apply, + vparamss: List[List[ValDef]] = mapParamss(sym)(ValDef.apply), + tpt: Tree = TypeTreeMemberType(sym) + ): DefDef = ( + atPos(sym.pos)(DefDef(mods, name, tparams, vparamss, tpt, rhs)) setSymbol sym + ) + + def newTypeDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TypeName = sym.name.toTypeName, + tparams: List[TypeDef] = sym.typeParams map TypeDef.apply + ): TypeDef = ( + atPos(sym.pos)(TypeDef(mods, name, tparams, rhs)) setSymbol sym + ) /** casedef shorthand */ def CaseDef(pat: Tree, body: Tree): CaseDef = @@ -1050,6 +1145,9 @@ trait Trees extends api.Trees { self: SymbolTable => def New(tpe: Type, args: Tree*): Tree = ApplyConstructor(TypeTree(tpe), args.toList) + def New(tpe: Type, argss: List[List[Tree]]): Tree = + New(TypeTree(tpe), argss) + def New(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) @@ -1094,113 +1192,136 @@ trait Trees extends api.Trees { self: SymbolTable => 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) - } + + def traverseMemberDef(md: MemberDef, owner: Symbol): Unit = atOwner(owner) { + traverseModifiers(md.mods) + traverseName(md.name) + md match { + case ClassDef(_, _, tparams, impl) => traverseParams(tparams) ; traverse(impl) + case ModuleDef(_, _, impl) => traverse(impl) + case ValDef(_, _, tpt, rhs) => traverseTypeAscription(tpt) ; traverse(rhs) + case TypeDef(_, _, tparams, rhs) => traverseParams(tparams) ; traverse(rhs) + case DefDef(_, _, tparams, vparamss, tpt, rhs) => + traverseParams(tparams) + traverseParamss(vparamss) + traverseTypeAscription(tpt) + traverse(rhs) + } + } + def traverseComponents(): Unit = tree match { case LabelDef(name, params, rhs) => - traverseTrees(params); traverse(rhs) + traverseName(name) + traverseParams(params) + traverse(rhs) case Import(expr, selectors) => traverse(expr) + selectors foreach traverseImportSelector case Annotated(annot, arg) => - traverse(annot); traverse(arg) + traverse(annot) + traverse(arg) case Template(parents, self, body) => - traverseTrees(parents) - if (!self.isEmpty) traverse(self) + traverseParents(parents) + traverseSelfType(self) traverseStats(body, tree.symbol) case Block(stats, expr) => - traverseTrees(stats); traverse(expr) + traverseTrees(stats) + traverse(expr) case CaseDef(pat, guard, body) => - traverse(pat); traverse(guard); traverse(body) + traversePattern(pat) + traverseGuard(guard) + traverse(body) case Alternative(trees) => traverseTrees(trees) case Star(elem) => traverse(elem) case Bind(name, body) => + traverseName(name) traverse(body) case UnApply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTrees(args) case ArrayValue(elemtpt, trees) => - traverse(elemtpt); traverseTrees(trees) - case Function(vparams, body) => - atOwner(tree.symbol) { - traverseTrees(vparams); traverse(body) - } + traverse(elemtpt) + traverseTrees(trees) case Assign(lhs, rhs) => - traverse(lhs); traverse(rhs) + traverse(lhs) + traverse(rhs) case AssignOrNamedArg(lhs, rhs) => - traverse(lhs); traverse(rhs) + traverse(lhs) + traverse(rhs) case If(cond, thenp, elsep) => - traverse(cond); traverse(thenp); traverse(elsep) + traverse(cond) + traverse(thenp) + traverse(elsep) case Match(selector, cases) => - traverse(selector); traverseTrees(cases) + traverse(selector) + traverseCases(cases) case Return(expr) => traverse(expr) case Try(block, catches, finalizer) => - traverse(block); traverseTrees(catches); traverse(finalizer) + traverse(block) + traverseCases(catches) + traverse(finalizer) case Throw(expr) => traverse(expr) case New(tpt) => traverse(tpt) case Typed(expr, tpt) => - traverse(expr); traverse(tpt) + traverse(expr) + traverseTypeAscription(tpt) case TypeApply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTypeArgs(args) case Apply(fun, args) => - traverse(fun); traverseTrees(args) + traverse(fun) + traverseTrees(args) case ApplyDynamic(qual, args) => - traverse(qual); traverseTrees(args) - case Super(qual, _) => traverse(qual) - case This(_) => - ; + traverseTrees(args) + case Super(qual, mix) => + traverse(qual) + traverseName(mix) + case This(qual) => + traverseName(qual) case Select(qualifier, selector) => traverse(qualifier) - case Ident(_) => - ; + traverseName(selector) + case Ident(name) => + traverseName(name) case ReferenceToBoxed(idt) => traverse(idt) - case Literal(_) => - ; + case Literal(const) => + traverseConstant(const) case TypeTree() => ; case SingletonTypeTree(ref) => traverse(ref) case SelectFromTypeTree(qualifier, selector) => traverse(qualifier) + traverseName(selector) case CompoundTypeTree(templ) => traverse(templ) case AppliedTypeTree(tpt, args) => - traverse(tpt); traverseTrees(args) + traverse(tpt) + traverseTypeArgs(args) case TypeBoundsTree(lo, hi) => - traverse(lo); traverse(hi) + traverse(lo) + traverse(hi) case ExistentialTypeTree(tpt, whereClauses) => - traverse(tpt); traverseTrees(whereClauses) - case _ => xtraverse(traverser, tree) + traverse(tpt) + traverseTrees(whereClauses) + case _ => + xtraverse(traverser, tree) + } + + if (tree.canHaveAttrs) { + tree match { + case PackageDef(pid, stats) => traverse(pid) ; traverseStats(stats, mclass(tree.symbol)) + case md: ModuleDef => traverseMemberDef(md, mclass(tree.symbol)) + case md: MemberDef => traverseMemberDef(md, tree.symbol) + case Function(vparams, body) => atOwner(tree.symbol) { traverseParams(vparams) ; traverse(body) } + case _ => traverseComponents() + } } } @@ -1307,7 +1428,7 @@ trait Trees extends api.Trees { self: SymbolTable => case CompoundTypeTree(templ) => treeCopy.CompoundTypeTree(tree, transformTemplate(templ)) case ExistentialTypeTree(tpt, whereClauses) => - treeCopy.ExistentialTypeTree(tree, transform(tpt), transformTrees(whereClauses)) + treeCopy.ExistentialTypeTree(tree, transform(tpt), transformMemberDefs(whereClauses)) case Return(expr) => treeCopy.Return(tree, transform(expr)) case Alternative(trees) => @@ -1315,7 +1436,7 @@ trait Trees extends api.Trees { self: SymbolTable => case Star(elem) => treeCopy.Star(tree, transform(elem)) case UnApply(fun, args) => - treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala + treeCopy.UnApply(tree, transform(fun), transformTrees(args)) // bq: see test/.../unapplyContexts2.scala case ArrayValue(elemtpt, trees) => treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) case ApplyDynamic(qual, args) => @@ -1331,9 +1452,6 @@ trait Trees extends api.Trees { self: SymbolTable => // --- specific traversers and transformers - @deprecated("Moved to tree.duplicate", "2.10.0") - protected[scala] def duplicateTree(tree: Tree): Tree = tree.duplicate - class ForeachPartialTreeTraverser(pf: PartialFunction[Tree, Tree]) extends Traverser { override def traverse(tree: Tree) { val t = if (pf isDefinedAt tree) pf(tree) else tree @@ -1372,6 +1490,16 @@ trait Trees extends api.Trees { self: SymbolTable => if (tree eq orig) super.transform(tree) else tree } + + /** A transformer that replaces tree `from` with tree `to` in a given tree */ + class TreeReplacer(from: Tree, to: Tree, positionAware: Boolean) extends Transformer { + override def transform(t: Tree): Tree = { + if (t == from) to + else if (!positionAware || (t.pos includes from.pos) || t.pos.isTransparent) super.transform(t) + else t + } + } + // 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 ", ") @@ -1387,7 +1515,7 @@ trait Trees extends api.Trees { self: SymbolTable => 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); + else subst(from.tail, to.tail) subst(from, to) case _ => super.transform(tree) @@ -1400,7 +1528,7 @@ trait Trees extends api.Trees { self: SymbolTable => 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 modifyType (_.substThis(clazz, newtpe)) tree match { case This(_) if tree.symbol == clazz => to case _ => super.transform(tree) @@ -1410,8 +1538,7 @@ trait Trees extends api.Trees { self: SymbolTable => class TypeMapTreeSubstituter(val typeMap: TypeMap) extends Traverser { override def traverse(tree: Tree) { - if (tree.tpe ne null) - tree.tpe = typeMap(tree.tpe) + tree modifyType typeMap if (tree.isDef) tree.symbol modifyInfo typeMap @@ -1448,9 +1575,9 @@ trait Trees extends api.Trees { self: SymbolTable => if (tree.symbol == from.head) tree setSymbol to.head else subst(from.tail, to.tail) } + tree modifyType symSubst - if (tree.tpe ne null) tree.tpe = symSubst(tree.tpe) - if (tree.hasSymbol) { + if (tree.hasSymbolField) { subst(from, to) tree match { case _: DefTree => @@ -1492,7 +1619,7 @@ trait Trees extends api.Trees { self: SymbolTable => } class FilterTreeTraverser(p: Tree => Boolean) extends Traverser { - val hits = new ListBuffer[Tree] + val hits = mutable.ListBuffer[Tree]() override def traverse(t: Tree) { if (p(t)) hits += t super.traverse(t) @@ -1500,7 +1627,7 @@ trait Trees extends api.Trees { self: SymbolTable => } class CollectTreeTraverser[T](pf: PartialFunction[Tree, T]) extends Traverser { - val results = new ListBuffer[T] + val results = mutable.ListBuffer[T]() override def traverse(t: Tree) { if (pf.isDefinedAt(t)) results += pf(t) super.traverse(t) @@ -1517,6 +1644,15 @@ trait Trees extends api.Trees { self: SymbolTable => } } + private lazy val duplicator = new Duplicator(focusPositions = true) + private class Duplicator(focusPositions: Boolean) extends Transformer { + override val treeCopy = newStrictTreeCopier + override def transform(t: Tree) = { + val t1 = super.transform(t) + if ((t1 ne t) && t1.pos.isRange && focusPositions) t1 setPos t.pos.focus + t1 + } + } trait TreeStackTraverser extends Traverser { import collection.mutable val path: mutable.Stack[Tree] = mutable.Stack() @@ -1526,27 +1662,32 @@ trait Trees extends api.Trees { self: SymbolTable => } } - private lazy val duplicator = new Duplicator(focusPositions = true) - private class Duplicator(focusPositions: Boolean) extends Transformer { - override val treeCopy = newStrictTreeCopier - override def transform(t: Tree) = { - val t1 = super.transform(t) - if ((t1 ne t) && t1.pos.isRange && focusPositions) t1 setPos t.pos.focus - t1 + /** Tracks the classes currently under construction during a transform */ + trait UnderConstructionTransformer extends Transformer { + import collection.mutable + + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls contains clazz + + /** The stack of class symbols in which a call to this() or to the super + * constructor, or early definition is active */ + private val selfOrSuperCalls = mutable.Stack[Symbol]() + + abstract override def transform(tree: Tree) = { + if ((treeInfo isSelfOrSuperConstrCall tree) || (treeInfo isEarlyDef tree)) { + selfOrSuperCalls push currentOwner.owner + try super.transform(tree) + finally selfOrSuperCalls.pop() + } else super.transform(tree) } } def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree - def wrapIntoTerm(tree: Tree): Tree = { - if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree - } - // this is necessary to avoid crashes like https://github.com/scalamacros/paradise/issues/1 // when someone tries to c.typecheck a naked MemberDef def wrappingIntoTerm(tree0: Tree)(op: Tree => Tree): Tree = { val neededWrapping = !tree0.isTerm - val tree1 = wrapIntoTerm(tree0) + val tree1 = build.SyntacticBlock(tree0 :: Nil) op(tree1) match { case Block(tree2 :: Nil, Literal(Constant(()))) if neededWrapping => tree2 case tree2 => tree2 @@ -1591,6 +1732,22 @@ trait Trees extends api.Trees { self: SymbolTable => case t => sys.error("Not a ValDef: " + t + "/" + t.getClass) } + def copyTypeDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + tparams: List[TypeDef] = null, + rhs: Tree = null + ): TypeDef = tree match { + case TypeDef(mods0, name0, tparams0, rhs0) => + treeCopy.TypeDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (tparams eq null) tparams0 else tparams, + if (rhs eq null) rhs0 else rhs + ) + case t => + sys.error("Not a TypeDef: " + t + "/" + t.getClass) + } def copyClassDef(tree: Tree)( mods: Modifiers = null, name: Name = null, @@ -1608,6 +1765,21 @@ trait Trees extends api.Trees { self: SymbolTable => sys.error("Not a ClassDef: " + t + "/" + t.getClass) } + def copyModuleDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + impl: Template = null + ): ModuleDef = tree match { + case ModuleDef(mods0, name0, impl0) => + treeCopy.ModuleDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (impl eq null) impl0 else impl + ) + case t => + sys.error("Not a ModuleDef: " + 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)) @@ -1650,62 +1822,68 @@ trait Trees extends api.Trees { self: SymbolTable => case t => sys.error("Not a LabelDef: " + t + "/" + t.getClass) } + def deriveFunction(func: Tree)(applyToRhs: Tree => Tree): Function = func match { + case Function(params0, rhs0) => + treeCopy.Function(func, params0, applyToRhs(rhs0)) + case t => + sys.error("Not a Function: " + t + "/" + t.getClass) + } // -------------- Classtags -------------------------------------------------------- - 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 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 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 AlternativeTag = ClassTag[Alternative](classOf[Alternative]) + implicit val AnnotatedTag = ClassTag[Annotated](classOf[Annotated]) + implicit val AppliedTypeTreeTag = ClassTag[AppliedTypeTree](classOf[AppliedTypeTree]) + implicit val ApplyTag = ClassTag[Apply](classOf[Apply]) + implicit val AssignOrNamedArgTag = ClassTag[AssignOrNamedArg](classOf[AssignOrNamedArg]) + implicit val AssignTag = ClassTag[Assign](classOf[Assign]) + implicit val BindTag = ClassTag[Bind](classOf[Bind]) + implicit val BlockTag = ClassTag[Block](classOf[Block]) + implicit val CaseDefTag = ClassTag[CaseDef](classOf[CaseDef]) + implicit val ClassDefTag = ClassTag[ClassDef](classOf[ClassDef]) + implicit val CompoundTypeTreeTag = ClassTag[CompoundTypeTree](classOf[CompoundTypeTree]) + implicit val DefDefTag = ClassTag[DefDef](classOf[DefDef]) + implicit val DefTreeTag = ClassTag[DefTree](classOf[DefTree]) implicit val ExistentialTypeTreeTag = ClassTag[ExistentialTypeTree](classOf[ExistentialTypeTree]) - implicit val TypeTreeTag = ClassTag[TypeTree](classOf[TypeTree]) + implicit val FunctionTag = ClassTag[Function](classOf[Function]) + implicit val GenericApplyTag = ClassTag[GenericApply](classOf[GenericApply]) + implicit val IdentTag = ClassTag[Ident](classOf[Ident]) + implicit val IfTag = ClassTag[If](classOf[If]) + implicit val ImplDefTag = ClassTag[ImplDef](classOf[ImplDef]) + implicit val ImportSelectorTag = ClassTag[ImportSelector](classOf[ImportSelector]) + implicit val ImportTag = ClassTag[Import](classOf[Import]) + implicit val LabelDefTag = ClassTag[LabelDef](classOf[LabelDef]) + implicit val LiteralTag = ClassTag[Literal](classOf[Literal]) + implicit val MatchTag = ClassTag[Match](classOf[Match]) + implicit val MemberDefTag = ClassTag[MemberDef](classOf[MemberDef]) + implicit val ModuleDefTag = ClassTag[ModuleDef](classOf[ModuleDef]) + implicit val NameTreeTag = ClassTag[NameTree](classOf[NameTree]) + implicit val NewTag = ClassTag[New](classOf[New]) + implicit val PackageDefTag = ClassTag[PackageDef](classOf[PackageDef]) + implicit val ReferenceToBoxedTag = ClassTag[ReferenceToBoxed](classOf[ReferenceToBoxed]) + implicit val RefTreeTag = ClassTag[RefTree](classOf[RefTree]) + implicit val ReturnTag = ClassTag[Return](classOf[Return]) + implicit val SelectFromTypeTreeTag = ClassTag[SelectFromTypeTree](classOf[SelectFromTypeTree]) + implicit val SelectTag = ClassTag[Select](classOf[Select]) + implicit val SingletonTypeTreeTag = ClassTag[SingletonTypeTree](classOf[SingletonTypeTree]) + implicit val StarTag = ClassTag[Star](classOf[Star]) + implicit val SuperTag = ClassTag[Super](classOf[Super]) + implicit val SymTreeTag = ClassTag[SymTree](classOf[SymTree]) + implicit val TemplateTag = ClassTag[Template](classOf[Template]) + implicit val TermTreeTag = ClassTag[TermTree](classOf[TermTree]) + implicit val ThisTag = ClassTag[This](classOf[This]) + implicit val ThrowTag = ClassTag[Throw](classOf[Throw]) + implicit val TreeTag = ClassTag[Tree](classOf[Tree]) + implicit val TryTag = ClassTag[Try](classOf[Try]) + implicit val TypTreeTag = ClassTag[TypTree](classOf[TypTree]) + implicit val TypeApplyTag = ClassTag[TypeApply](classOf[TypeApply]) + implicit val TypeBoundsTreeTag = ClassTag[TypeBoundsTree](classOf[TypeBoundsTree]) + implicit val TypeDefTag = ClassTag[TypeDef](classOf[TypeDef]) + implicit val TypeTreeTag = ClassTag[TypeTree](classOf[TypeTree]) + implicit val TypedTag = ClassTag[Typed](classOf[Typed]) + implicit val UnApplyTag = ClassTag[UnApply](classOf[UnApply]) + implicit val ValDefTag = ClassTag[ValDef](classOf[ValDef]) + implicit val ValOrDefDefTag = ClassTag[ValOrDefDef](classOf[ValOrDefDef]) val treeNodeCount = Statistics.newView("#created tree nodes")(nodeCount) } diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala index 68b4fa69a1..63f897cd32 100644 --- a/src/reflect/scala/reflect/internal/TypeDebugging.scala +++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala @@ -3,16 +3,77 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package internal +import util._ + trait TypeDebugging { self: SymbolTable => import definitions._ - // @M toString that is safe during debugging (does not normalize, ...) + /** There's a whole lot of implementation detail which is nothing but noise when + * you are trying to see what's going on. This is my attempt to filter it out. + */ + object noPrint extends (Tree => Boolean) { + def skipScalaName(name: Name) = name match { + case tpnme.Any | tpnme.Nothing | tpnme.AnyRef => true + case _ => false + } + def skipRefTree(t: RefTree) = t match { + case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) if skipScalaName(name) => true + case Select(sel, name) if sel.symbol == ScalaPackage && skipScalaName(name) => true + case Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) => true + case Ident(nme.ROOTPKG) => true + case _ => skipSym(t.symbol) + } + def skipSym(sym: Symbol): Boolean = sym match { + case null => false + case NothingClass | AnyClass => true + case PredefModule => true + case ObjectClass => true + case _ => sym.hasPackageFlag + } + def skipType(tpe: Type): Boolean = (tpe eq null) || skipSym(tpe.typeSymbolDirect) + + def skip(t: Tree): Boolean = t match { + case EmptyTree => true + case PackageDef(_, _) => true + case t: RefTree => skipRefTree(t) + case TypeBoundsTree(lo, hi) => skip(lo) && skip(hi) + case Block(Nil, expr) => skip(expr) + case Apply(fn, Nil) => skip(fn) + case Block(stmt :: Nil, expr) => skip(stmt) && skip(expr) + case DefDef(_, nme.CONSTRUCTOR, Nil, ListOfNil, _, rhs) => skip(rhs) + case Literal(Constant(())) => true + case tt @ TypeTree() => skipType(tt.tpe) + case _ => skipSym(t.symbol) + } + def apply(t: Tree) = skip(t) + } + + /** Light color wrappers. + */ object typeDebug { + import scala.Console._ + + private val colorsOk = sys.props contains "scala.color" + private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s + private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s + + def inLightRed(s: String) = inColor(s, RED) + def inLightGreen(s: String) = inColor(s, GREEN) + def inLightMagenta(s: String) = inColor(s, MAGENTA) + def inLightCyan(s: String): String = inColor(s, CYAN) + def inGreen(s: String): String = inBold(s, GREEN) + def inRed(s: String): String = inBold(s, RED) + def inBlue(s: String): String = inBold(s, BLUE) + def inCyan(s: String): String = inBold(s, CYAN) + def inMagenta(s: String) = inBold(s, MAGENTA) + def resetColor(s: String): String = if (colorsOk) s + RESET else s + private def to_s(x: Any): String = x match { // otherwise case classes are caught looking like products case _: Tree | _: Type => "" + x @@ -20,7 +81,6 @@ trait TypeDebugging { 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 { @@ -31,16 +91,32 @@ trait TypeDebugging { 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 ptLine(pairs: (String, Any)*): String = ( + pairs + map { case (k, v) => (k, to_s(v)) } + filterNot { case (_, v) => v == "" } + map { case ("", v) => v ; case (k, v) => s"$k=$v" } + mkString ", " + ) + def ptTree(t: Tree): String = t match { + case PackageDef(pid, _) => s"package $pid" + case ModuleDef(_, name, _) => s"object $name" + case DefDef(_, name, tparams, _, _, _) => "def " + name + ptTypeParams(tparams) + case ClassDef(_, name, Nil, _) if t.symbol != null && t.symbol.isModuleClass => s"module class $name" + case ClassDef(_, name, tparams, _) => "class " + name + ptTypeParams(tparams) + case td: TypeDef => ptTypeParam(td) + case TypeBoundsTree(lo, hi) => + val lo_s = if (noPrint(lo)) "" else " >: " + ptTree(lo) + val hi_s = if (noPrint(hi)) "" else " <: " + ptTree(hi) + lo_s + hi_s + case _ if (t.symbol eq null) || (t.symbol eq NoSymbol) => to_s(t) + case _ => "" + t.symbol.tpe } - 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) + def ptTypeParam(td: TypeDef): String = { + val TypeDef(_, name, tparams, rhs) = td + name + ptTypeParams(tparams) + ptTree(rhs) } + def ptTypeParams(tparams: List[TypeDef]): String = str brackets (tparams map ptTypeParam) object str { def parentheses(xs: List[_]): String = xs.mkString("(", ", ", ")") @@ -48,24 +124,28 @@ trait TypeDebugging { def tparams(tparams: List[Type]): String = brackets(tparams map debug) def parents(ps: List[Type]): String = (ps map debug).mkString(" with ") def refine(defs: Scope): String = defs.toList.mkString("{", " ;\n ", "}") + def bounds(lo: Type, hi: Type): String = { + val lo_s = if (typeIsNothing(lo)) "" else s" >: $lo" + val hi_s = if (typeIsAny(hi)) "" else s" <: $hi" + lo_s + hi_s + } } - + import str._ 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... + case TypeRef(pre, sym, args) => s"${debug(pre)}.${sym.nameString}.${tparams(args)}" + case ThisType(sym) => s"${sym.nameString}.this" + case SingleType(pre, sym) => s"${debug(pre)}.${sym.nameString}.type" + case RefinedType(ps, decls) => s"${parents(ps)} ${refine(decls)}" + case ClassInfoType(ps, decls, clazz) => s"class ${clazz.nameString} ${parents(ps)} ${refine(decls)}" + case PolyType(tparams, result) => s"${brackets(tparams)}${debug(result)}" + case TypeBounds(lo, hi) => bounds(lo, hi) + case tv @ TypeVar(_, _) => "" + tv + case ExistentialType(tparams, qtpe) => s"forSome ${brackets(tparams)} ${debug(qtpe)}" + case _ => s"?${shortClassOfInstance(tp)}?" // 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 index 2f49995030..f26315c538 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3,19 +3,21 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package 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 scala.runtime.ObjectRef import util.ThreeValues._ +import Variance._ +import Depth._ +import TypeConstants._ /* A standard type pattern match: case ErrorType => @@ -41,7 +43,7 @@ import util.ThreeValues._ // parent1 with ... with parentn { defs } case ExistentialType(tparams, result) => // result forSome { tparams } - case AnnotatedType(annots, tp, selfsym) => + case AnnotatedType(annots, tp) => // tp @annots // the following are non-value types; you cannot write them down in Scala source. @@ -68,147 +70,63 @@ import util.ThreeValues._ // 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, args) - // for dependent method types: a type referring to a method parameter. - case ErasedValueType(tref) + case ErasedValueType(clazz, underlying) // only used during erasure of derived value classes. */ -trait Types extends api.Types { self: SymbolTable => +trait Types + extends api.Types + with tpe.TypeComparers + with tpe.TypeToStrings + with tpe.CommonOwners + with tpe.GlbLubs + with tpe.TypeMaps + with tpe.TypeConstraints + with tpe.FindMembers + with util.Collections { self: SymbolTable => + import definitions._ import TypesStats._ 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 + private final val breakCycles = settings.breakCycles.value + /** In case anyone wants to turn on type parameter bounds being used * to seed type constraints. */ private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" + private final val sharperSkolems = sys.props contains "scalac.experimental.sharper-skolems" 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]() + /** Caching the most recent map has a 75-90% hit rate. */ + private object substTypeMapCache { + private[this] var cached: SubstTypeMap = new SubstTypeMap(Nil, Nil) - /** 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 + def apply(from: List[Symbol], to: List[Type]): SubstTypeMap = { + if ((cached.from ne from) || (cached.to ne to)) + cached = new SubstTypeMap(from, to) - protected def newUndoLog = new UndoLog - - class UndoLog extends Clearable { - private type UndoPairs = List[(TypeVar, TypeConstraint)] - //OPT this method is public so we can do `manual inlining` - var log: UndoPairs = List() - - /* - * These two methods provide explicit locking mechanism that is overridden in SynchronizedUndoLog. - * - * The idea behind explicit locking mechanism is that all public methods that access mutable state - * will have to obtain the lock for their entire execution so both reads and writes can be kept in - * right order. Originally, that was achieved by overriding those public methods in - * `SynchronizedUndoLog` which was fine but expensive. The reason is that those public methods take - * thunk as argument and if we keep them non-final there's no way to make them inlined so thunks - * can go away. - * - * By using explicit locking we can achieve inlining. - * - * NOTE: They are made public for now so we can apply 'manual inlining' (copy&pasting into hot - * places implementation of `undo` or `undoUnless`). This should be changed back to protected - * once inliner is fixed. - */ - def lock(): Unit = () - def unlock(): Unit = () - - // register with the auto-clearing cache manager - perRunCaches.recordCache(this) - - /** Undo all changes to constraints to type variables upto `limit`. */ - //OPT this method is public so we can do `manual inlining` - def undoTo(limit: UndoPairs) { - assertCorrectThread() - 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() { - lock() - try { - if (settings.debug.value) - self.log("Clearing " + log.size + " entries from the undoLog.") - log = Nil - } finally unlock() - } - def size = { - lock() - try log.size finally unlock() - } - - // `block` should not affect constraints on typevars - def undo[T](block: => T): T = { - lock() - try { - val before = log - - try block - finally undoTo(before) - } finally unlock() - } - - // if `block` evaluates to false, it should not affect constraints on typevars - def undoUnless(block: => Boolean): Boolean = { - lock() - try { - val before = log - var result = false - - try result = block - finally if (!result) undoTo(before) - - result - } finally unlock() + cached } } + /** The current skolemization level, needed for the algorithms + * in isSameType, isSubType that do constraint solving under a prefix. + */ + private var _skolemizationLevel = 0 + def skolemizationLevel = _skolemizationLevel + def skolemizationLevel_=(value: Int) = _skolemizationLevel = value + /** 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]]() + private val _intersectionWitness = perRunCaches.newWeakMap[List[Type], WeakReference[Type]]() + def intersectionWitness = _intersectionWitness /** A proxy for a type (identified by field `underlying`) that forwards most * operations to it (for exceptions, see WrappingProxy, which forwards even more operations). @@ -223,12 +141,8 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -256,7 +170,14 @@ trait Types extends api.Types { self: SymbolTable => * 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 maybeRewrap(newtp: Type) = ( + if (newtp eq underlying) this + // BoundedWildcardTypes reach here during erroneous compilation: neg/t6258 + // Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800 + // Otherwise, if newtp =:= underlying, don't rewrap it. + else if (!newtp.isWildcard && !newtp.isHigherKinded && (newtp =:= 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 @@ -266,16 +187,15 @@ trait Types extends api.Types { self: SymbolTable => 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 etaExpand = maybeRewrap(underlying.etaExpand) override def dealias = maybeRewrap(underlying.dealias) override def cloneInfo(owner: Symbol) = maybeRewrap(underlying.cloneInfo(owner)) override def atOwner(owner: Symbol) = maybeRewrap(underlying.atOwner(owner)) @@ -297,7 +217,6 @@ trait Types extends api.Types { self: SymbolTable => 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 { @@ -325,18 +244,16 @@ trait Types extends api.Types { self: SymbolTable => def isSpliceable = { this.isInstanceOf[TypeRef] && typeSymbol.isAbstractType && !typeSymbol.isExistential } - } - /** Same as a call to narrow unless existentials are visible - * after widening the type. In that case, narrow from the widened - * type instead of the proxy. This gives buried existentials a - * chance to make peace with the other types. See SI-5330. - */ - private def narrowForFindMember(tp: Type): Type = { - val w = tp.widen - // Only narrow on widened type when we have to -- narrow is expensive unless the target is a singleton type. - if ((tp ne w) && containsExistential(w)) w.narrow - else tp.narrow + def companion = { + val sym = typeSymbolDirect + if (sym.isModule && !sym.isPackage) sym.companionSymbol.tpe + else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol.tpe + else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol.info + else NoType + } + + def paramLists: List[List[Symbol]] = paramss } /** The base class for all types */ @@ -351,7 +268,7 @@ trait Types extends api.Types { self: SymbolTable => def takesTypeArgs: Boolean = this.isHigherKinded /** Does this type denote a stable reference (i.e. singleton type)? */ - def isStable: Boolean = false + final def isStable: Boolean = definitions isStable this /** Is this type dangerous (i.e. it might contain conflicting * type information when empty, so that it can be constructed @@ -359,10 +276,7 @@ trait Types extends api.Types { self: SymbolTable => * 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 + final def isVolatile: Boolean = definitions isVolatile this /** Is this type a structural refinement type (it ''refines'' members that have not been inherited) */ def isStructuralRefinement: Boolean = false @@ -384,15 +298,11 @@ trait Types extends api.Types { self: SymbolTable => /** 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: Boolean = - typeSymbol.isFinal && (typeSymbol.typeParams forall symbolIsNonVariant) && prefix.isStable + def isFinalType = typeSymbol.hasOnlyBottomSubclasses && prefix.isStable /** Is this type completed (i.e. not a lazy type)? */ def isComplete: Boolean = true @@ -489,7 +399,7 @@ trait Types extends api.Types { self: SymbolTable => /** 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 + def firstParent = if (parents.nonEmpty) parents.head else ObjectTpe /** For a typeref or single-type, the prefix of the normalized type (@see normalize). * NoType for all other types. */ @@ -524,14 +434,9 @@ trait Types extends api.Types { self: SymbolTable => /** 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 + final def finalResultType: Type = definitions finalResultType this /** For a method type, the number of its value parameter sections, * 0 for all other types */ @@ -557,13 +462,6 @@ trait Types extends api.Types { self: SymbolTable => * 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 @@ -589,15 +487,39 @@ trait Types extends api.Types { self: SymbolTable => * 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))) + * + * Discussion: normalize is NOT usually what you want to be calling. + * The (very real) danger with normalize is that it will force types + * which would not otherwise have been forced, leading to mysterious + * behavioral differences, cycles, and other elements of mysteries. + * Under most conditions the method you should be calling is `dealiasWiden` + * (see that method for more info.) + * + * Here are a few of the side-effect-trail-leaving methods called + * by various implementations of normalize: + * + * - sym.info + * - tpe.etaExpand + * - tpe.betaReduce + * - tpe.memberType + * - sym.nextOverriddenSymbol + * - constraint.inst + * + * If you've been around the compiler a while that list must fill + * your heart with fear. */ def normalize = this // @MAT + def etaExpand = this + /** Expands type aliases. */ def dealias = this /** Repeatedly apply widen and dealias until they have no effect. * This compensates for the fact that type aliases can hide beneath * singleton types and singleton types can hide inside type aliases. + * !!! - and yet it is still inadequate, because aliases and singletons + * might lurk in the upper bounds of an abstract type. See SI-7051. */ def dealiasWiden: Type = ( if (this ne widen) widen.dealiasWiden @@ -615,8 +537,6 @@ trait Types extends api.Types { self: SymbolTable => else Nil ) - def etaExpand: Type = this - /** Performs a single step of beta-reduction on types. * Given: * @@ -686,16 +606,6 @@ trait Types extends api.Types { self: SymbolTable => def nonPrivateMember(name: Name): Symbol = memberBasedOnName(name, BridgeAndPrivateFlags) - /** All members with the given flags, excluding bridges. - */ - def membersWithFlags(requiredFlags: Long): Scope = - membersBasedOnFlags(BridgeFlags, requiredFlags) - - /** All non-private members with the given flags, excluding bridges. - */ - def nonPrivateMembersWithFlags(requiredFlags: Long): Scope = - 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 @@ -716,10 +626,9 @@ trait Types extends api.Types { self: SymbolTable => */ def membersBasedOnFlags(excludedFlags: Long, requiredFlags: Long): Scope = findMembers(excludedFlags, requiredFlags) -// findMember(nme.ANYNAME, excludedFlags, requiredFlags, false).alternatives def memberBasedOnName(name: Name, excludedFlags: Long): Symbol = - findMember(name, excludedFlags, 0, false) + findMember(name, excludedFlags, 0, stableOnly = false) /** The least type instance of given class which is a supertype * of this type. Example: @@ -750,7 +659,7 @@ trait Types extends api.Types { self: SymbolTable => ) if (trivial) this else { - val m = new AsSeenFromMap(pre.normalize, clazz) + val m = newAsSeenFromMap(pre.normalize, clazz) val tp = m(this) val tp1 = existentialAbstraction(m.capturedParams, tp) @@ -770,6 +679,7 @@ trait Types extends api.Types { self: SymbolTable => * }}} */ def memberInfo(sym: Symbol): Type = { + require(sym ne NoSymbol, this) sym.info.asSeenFrom(this, sym.owner) } @@ -785,15 +695,14 @@ trait Types extends api.Types { self: SymbolTable => case OverloadedType(_, alts) => OverloadedType(this, alts) case tp => - tp.asSeenFrom(this, sym.owner) + if (sym eq NoSymbol) NoType else 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 + if (from.isEmpty) this else substTypeMapCache(from, to)(this) /** Substitute symbols `to` for occurrences of symbols `from` in this type. * @@ -830,7 +739,6 @@ trait Types extends api.Types { self: SymbolTable => 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){ @@ -838,6 +746,8 @@ trait Types extends api.Types { self: SymbolTable => def map[T](f: Type => T): List[T] = collect(Type.this) map f } + @inline final def orElse(alt: => Type): Type = if (this ne NoType) this else alt + /** Returns optionally first type (in a preorder traversal) which satisfies predicate `p`, * or None if none exists. */ @@ -860,45 +770,46 @@ trait Types extends api.Types { self: SymbolTable => /** 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 (Statistics.canEnable) stat_<:<(that) else { (this eq that) || - (if (explainSwitch) explain("<:", isSubType, this, that) - else isSubType(this, that, AnyDepth)) + (if (explainSwitch) explain("<:", isSubType(_: Type, _: Type), this, that) + else isSubType(this, that)) } } /** Is this type a subtype of that type in a pattern context? - * Any type arguments on the right hand side are replaced with + * Dummy 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 matchesPattern(that: Type): Boolean = (this <:< that) || (that match { + case ArrayTypeRef(elem2) if elem2.typeConstructor.isHigherKinded => + this match { + case ArrayTypeRef(elem1) => elem1 matchesPattern elem2 + case _ => false + } + case TypeRef(_, sym, args) => + val that1 = existentialAbstraction(args map (_.typeSymbol), that) + (that ne that1) && (this <:< that1) && { + debuglog(s"$this.matchesPattern($that) depended on discarding args and testing <:< $that1") + true + } + case _ => + false + }) def stat_<:<(that: Type): Boolean = { if (Statistics.canEnable) Statistics.incCounter(subtypeCount) val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, subtypeNanos) else null val result = (this eq that) || - (if (explainSwitch) explain("<:", isSubType, this, that) - else isSubType(this, that, AnyDepth)) + (if (explainSwitch) explain("<:", isSubType(_: Type, _: Type), this, that) + else isSubType(this, that)) if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) result } @@ -921,12 +832,7 @@ trait Types extends api.Types { self: SymbolTable => (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? @@ -943,7 +849,7 @@ trait Types extends api.Types { self: SymbolTable => 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) + def looselyMatches(that: Type): Boolean = matchesType(this, that, alwaysMatchSimple = true) /** The shortest sorted upwards closed array of types that contains * this type as first element. @@ -968,10 +874,10 @@ trait Types extends api.Types { self: SymbolTable => /** The maximum depth (@see typeDepth) * of each type in the BaseTypeSeq of this type except the first. */ - def baseTypeSeqDepth: Int = 1 + def baseTypeSeqDepth: Depth = Depth(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 linearization order, starting with the class itself and ending * in class Any. */ def baseClasses: List[Symbol] = List() @@ -991,12 +897,12 @@ trait Types extends api.Types { self: SymbolTable => if (sym == btssym) return mid else if (sym isLess btssym) hi = mid - 1 else if (btssym isLess sym) lo = mid + 1 - else abort() + else abort("sym is neither `sym == btssym`, `sym isLess btssym` nor `btssym isLess sym`") } -1 } - /** If this is a poly- or methodtype, a copy with cloned type / value parameters + /** If this is a ExistentialType, PolyType or MethodType, a copy with cloned type / value parameters * owned by `owner`. Identity for all other types. */ def cloneInfo(owner: Symbol) = this @@ -1015,7 +921,11 @@ trait Types extends api.Types { self: SymbolTable => * after `maxTostringRecursions` recursion levels. Uses `safeToString` * to produce a string on each level. */ - override def toString: String = typeToString(this) + override final def toString: String = { + // see comments to internal#Symbol.typeSignature for an explanation why this initializes + if (!isCompilerUniverse) fullyInitializeType(this) + typeToString(this) + } /** Method to be implemented in subclasses. * Converts this type to a string in calling toString for its parts. @@ -1029,7 +939,9 @@ trait Types extends api.Types { self: SymbolTable => else if ((str endsWith ".type") && !typeSymbol.isModuleClass) widen match { case RefinedType(_, _) => "" + widen - case _ => s"$str (with underlying type $widen)" + case _ => + if (widen.toString.trim == "") str + else s"$str (with underlying type $widen)" } else str } @@ -1058,7 +970,7 @@ trait Types extends api.Types { self: SymbolTable => var sym: Symbol = NoSymbol var e: ScopeEntry = decls.lookupEntry(name) while (e ne null) { - if (!e.sym.hasFlag(excludedFlags)) { + if (!e.sym.hasFlag(excludedFlags.toLong)) { if (sym == NoSymbol) sym = e.sym else { if (alts.isEmpty) alts = sym :: Nil @@ -1071,187 +983,38 @@ trait Types extends api.Types { self: SymbolTable => else (baseClasses.head.newOverloaded(this, alts)) } + /** Find all members meeting the flag requirements. + * + * If you require a DEFERRED member, you will get it if it exists -- even if there's an overriding concrete member. + * If you exclude DEFERRED members, or don't specify any requirements, + * you won't get deferred members (whether they have an overriding concrete member or not) + * + * Thus, findMember requiring DEFERRED flags yields deferred members, + * while `findMember(excludedFlags = 0, requiredFlags = 0).filter(_.isDeferred)` may not (if there's a corresponding concrete member) + * + * Requirements take precedence over exclusions, so requiring and excluding DEFERRED will yield a DEFERRED member (if there is one). + * + */ def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = { - // 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) - - if (Statistics.canEnable) Statistics.incCounter(findMembersCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var members: Scope = null - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var continue = true - var self: Type = null - while (continue) { - continue = false - val bcs0 = baseClasses - var bcs = bcs0 - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.elems - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (members eq null) members = newFindMemberScope - var others: ScopeEntry = members.lookupEntry(sym.name) - var symtpe: Type = null - while ((others ne null) && { - val other = others.sym - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = members lookupNextEntry others - } - if (others eq null) members enter sym - } else if (excl == DEFERRED) { - continue = true - } - } - entry = entry.next - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (continue) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - if (members eq null) EmptyScope else members + def findMembersInternal = new FindMembers(this, excludedFlags, requiredFlags).apply() + if (this.isGround) findMembersInternal + else suspendingTypeVars(typeVarsInType(this))(findMembersInternal) } /** * 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 name The member's name * @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) - - if (Statistics.canEnable) Statistics.incCounter(findMemberCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var member: Symbol = NoSymbol - var members: List[Symbol] = null - var lastM: ::[Symbol] = null - var membertpe: Type = null - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var continue = true - var self: Type = null - - while (continue) { - continue = false - val bcs0 = baseClasses - var bcs = bcs0 - // omit PRIVATE LOCALS unless selector class is contained in class owning the def. - def admitPrivateLocal(owner: Symbol): Boolean = { - val selectorClass = this match { - case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class. - case _ => bcs0.head - } - selectorClass.hasTransOwner(owner) - } - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.lookupEntry(name) - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - ( - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - admitPrivateLocal(bcs.head))) { - if (name.isTypeName || stableOnly && sym.isStable) { - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - return sym - } else if (member eq NoSymbol) { - member = sym - } else if (members eq null) { - if ((member ne sym) && - ((member.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (membertpe eq null) membertpe = self.memberType(member) - !(membertpe matches self.memberType(sym)) - })) { - lastM = new ::(sym, null) - members = member :: lastM - } - } else { - var others: List[Symbol] = members - var symtpe: Type = null - while ((others ne null) && { - val other = others.head - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = others.tail - } - if (others eq null) { - val lastM1 = new ::(sym, null) - lastM.tl = lastM1 - lastM = lastM1 - } - } - } else if (excl == DEFERRED) { - continue = true - } - } - entry = decls lookupNextEntry entry - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (continue) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - if (members eq null) { - if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount) - member - } else { - if (Statistics.canEnable) Statistics.incCounter(multMemberCount) - lastM.tl = Nil - baseClasses.head.newOverloaded(this, members) - } + def findMemberInternal = new FindMember(this, name, excludedFlags, requiredFlags, stableOnly).apply() + + if (this.isGround) findMemberInternal + else suspendingTypeVars(typeVarsInType(this))(findMemberInternal) } /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ @@ -1280,17 +1043,6 @@ trait Types extends api.Types { self: SymbolTable => 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() } @@ -1314,29 +1066,16 @@ trait Types extends api.Types { self: SymbolTable => 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 baseTypeSeqDepth: Depth = 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 { + abstract class SingletonType extends SubType with SimpleTypeProxy with SingletonTypeApi { 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 = { if (Statistics.canEnable) Statistics.incCounter(singletonBaseTypeSeqCount) @@ -1350,7 +1089,6 @@ trait Types extends api.Types { self: SymbolTable => if (pre.isOmittablePrefix) pre.fullName + ".type" else prefixString + "type" } - /* override def typeOfThis: Type = typeSymbol.typeOfThis override def bounds: TypeBounds = TypeBounds(this, this) @@ -1376,7 +1114,6 @@ trait Types extends api.Types { self: SymbolTable => 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" } @@ -1386,7 +1123,6 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -1411,38 +1147,32 @@ trait Types extends api.Types { self: SymbolTable => 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 { - if (!sym.isClass) { + if (!sym.isClass && !sym.isFreeType) { // SI-6640 allow StubSymbols to reveal what's missing from the classpath before we trip the assertion. sym.failIfStub() abort(s"ThisType($sym) for sym which is not a class") } - //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." + if (settings.debug) sym.nameString + ".this." else if (sym.isAnonOrRefinementClass) "this." else if (sym.isOmittablePrefix) "" else if (sym.isModuleClass) sym.fullNameString + "." @@ -1460,7 +1190,7 @@ trait Types extends api.Types { self: SymbolTable => def apply(sym: Symbol): Type = ( if (!phase.erasedTypes) unique(new UniqueThisType(sym)) else if (sym.isImplClass) sym.typeOfThis - else sym.tpe + else sym.tpe_* ) } @@ -1475,8 +1205,6 @@ trait Types extends api.Types { self: SymbolTable => } 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 = { @@ -1490,8 +1218,6 @@ trait Types extends api.Types { self: SymbolTable => // 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 @@ -1531,7 +1257,7 @@ trait Types extends api.Types { self: SymbolTable => 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; + tpe.underlyingCache = if (tpe.sym == NoSymbol) ThisType(rootMirror.RootClass) else tpe.pre.memberType(tpe.sym).resultType assert(tpe.underlyingCache ne tpe, tpe) } } @@ -1543,7 +1269,6 @@ trait Types extends api.Types { self: SymbolTable => if (trivial == UNKNOWN) trivial = fromBoolean(thistpe.isTrivial && supertpe.isTrivial) toBoolean(trivial) } - override def isNotNull = true; override def typeSymbol = thistpe.typeSymbol override def underlying = supertpe override def prefix: Type = supertpe.prefix @@ -1571,31 +1296,52 @@ trait Types extends api.Types { self: SymbolTable => 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 = typeIsNothing(lo) - private def emptyUpperBound = typeIsAny(hi) + private def emptyLowerBound = typeIsNothing(lo) || lo.isWildcard + private def emptyUpperBound = typeIsAny(hi) || hi.isWildcard def isEmptyBounds = emptyLowerBound && emptyUpperBound - // override def isNullable: Boolean = NullClass.tpe <:< lo; - override def safeToString = lowerString + upperString + override def safeToString = scalaNotation(_.toString) + + /** Bounds notation used in Scala syntax. + * For example +This <: scala.collection.generic.Sorted[K,This]. + */ + private[internal] def scalaNotation(typeString: Type => String): String = { + (if (emptyLowerBound) "" else " >: " + typeString(lo)) + + (if (emptyUpperBound) "" else " <: " + typeString(hi)) + } + /** Bounds notation used in http://adriaanm.github.com/files/higher.pdf. + * For example *(scala.collection.generic.Sorted[K,This]). + */ + private[internal] def starNotation(typeString: Type => String): String = { + if (emptyLowerBound && emptyUpperBound) "" + else if (emptyLowerBound) "(" + typeString(hi) + ")" + else "(%s, %s)" format (typeString(lo), typeString(hi)) + } override def kind = "TypeBoundsType" } final class UniqueTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) 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 empty: TypeBounds = apply(NothingTpe, AnyTpe) + def upper(hi: Type): TypeBounds = apply(NothingTpe, hi) + def lower(lo: Type): TypeBounds = apply(lo, AnyTpe) def apply(lo: Type, hi: Type): TypeBounds = { unique(new UniqueTypeBounds(lo, hi)).asInstanceOf[TypeBounds] } } + object CompoundType { + def unapply(tp: Type): Option[(List[Type], Scope, Symbol)] = tp match { + case ClassInfoType(parents, decls, clazz) => Some((parents, decls, clazz)) + case RefinedType(parents, decls) => Some((parents, decls, tp.typeSymbol)) + case _ => None + } + } + /** A common base class for intersection types and class types */ - abstract class CompoundType extends Type { + abstract class CompoundType extends Type with CompoundTypeApi { private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ private[reflect] var baseTypeSeqPeriod = NoPeriod @@ -1615,7 +1361,7 @@ trait Types extends api.Types { self: SymbolTable => } } - override def baseTypeSeqDepth: Int = baseTypeSeq.maxDepth + override def baseTypeSeqDepth: Depth = baseTypeSeq.maxDepth override def baseClasses: List[Symbol] = { val cached = baseClassesCache @@ -1657,18 +1403,46 @@ trait Types extends api.Types { self: SymbolTable => } override def narrow: Type = typeSymbol.thisType - override def isNotNull: Boolean = parents exists typeIsNotNull override def isStructuralRefinement: Boolean = typeSymbol.isAnonOrRefinementClass && (decls exists symbolIsPossibleInRefinement) - // override def isNullable: Boolean = - // parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType); + protected def shouldForceScope = settings.debug || parents.isEmpty || !decls.isEmpty + protected def initDecls = fullyInitializeScope(decls) + protected def scopeString = if (shouldForceScope) initDecls.mkString("{", "; ", "}") else "" + override def safeToString = parentsString(parents) + scopeString + } - override def safeToString: String = parentsString(parents) + ( - (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) - fullyInitializeScope(decls).mkString("{", "; ", "}") else "") + protected def computeBaseClasses(tpe: Type): List[Symbol] = { + val parents = tpe.parents // adriaan says tpe.parents does work sometimes, so call it only once + val baseTail = ( + if (parents.isEmpty || parents.head.isInstanceOf[PackageTypeRef]) Nil + else { + //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = parents.head // parents.isEmpty was already excluded + var mixins = parents.tail + val sbcs = superclazz.baseClasses + var bcs = sbcs + def isNew(clazz: Symbol): Boolean = ( + superclazz.baseTypeIndex(clazz) < 0 && + { var p = bcs + while ((p ne sbcs) && (p.head != clazz)) p = p.tail + p eq sbcs + } + ) + while (!mixins.isEmpty) { + def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = + if (mbcs.isEmpty) bcs + else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) + else addMixinBaseClasses(mbcs.tail) + bcs = addMixinBaseClasses(mixins.head.baseClasses) + mixins = mixins.tail + } + bcs + } ) + tpe.typeSymbol :: baseTail } protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = { @@ -1690,7 +1464,7 @@ trait Types extends api.Types { self: SymbolTable => val paramToVarMap = varToParamMap map (_.swap) val varToParam = new TypeMap { def apply(tp: Type) = varToParamMap get tp match { - case Some(sym) => sym.tpe + case Some(sym) => sym.tpe_* case _ => mapOver(tp) } } @@ -1709,7 +1483,7 @@ trait Types extends api.Types { self: SymbolTable => tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) - tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) + tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe_*) else compoundBaseTypeSeq(tpe) } finally { @@ -1731,41 +1505,61 @@ trait Types extends api.Types { self: SymbolTable => 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 + object baseClassesCycleMonitor { + private var open: List[Symbol] = Nil + @inline private def cycleLog(msg: => String) { + if (settings.debug) + Console.err.println(msg) + } + def size = open.size + def push(clazz: Symbol) { + cycleLog("+ " + (" " * size) + clazz.fullNameString) + open ::= clazz + } + def pop(clazz: Symbol) { + assert(open.head eq clazz, (clazz, open)) + open = open.tail + } + def isOpen(clazz: Symbol) = open contains clazz + } + + protected def defineBaseClassesOfCompoundType(tpe: CompoundType) { + def define() = defineBaseClassesOfCompoundType(tpe, force = false) + if (!breakCycles || isPastTyper) define() + else tpe match { + // non-empty parents helpfully excludes all package classes + case tpe @ ClassInfoType(_ :: _, _, clazz) if !clazz.isAnonOrRefinementClass => + // Cycle: force update + if (baseClassesCycleMonitor isOpen clazz) + defineBaseClassesOfCompoundType(tpe, force = true) + else { + baseClassesCycleMonitor push clazz + try define() + finally baseClassesCycleMonitor pop clazz } - tpe.typeSymbol :: bcs - } + case _ => + define() + } + } + private def defineBaseClassesOfCompoundType(tpe: CompoundType, force: Boolean) { val period = tpe.baseClassesPeriod - if (period != currentPeriod) { + if (period == currentPeriod) { + if (force && breakCycles) { + def what = tpe.typeSymbol + " in " + tpe.typeSymbol.owner.fullNameString + val bcs = computeBaseClasses(tpe) + tpe.baseClassesCache = bcs + warning(s"Breaking cycle in base class computation of $what ($bcs)") + } + } + else { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, baseClassesNanos) else null try { tpe.baseClassesCache = null - tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) - } finally { + tpe.baseClassesCache = tpe.memo(computeBaseClasses(tpe))(tpe.typeSymbol :: _.baseClasses.tail) + } + finally { if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } @@ -1813,47 +1607,27 @@ trait Types extends api.Types { self: SymbolTable => } 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)) + etaExpand } 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 - })) + final override def etaExpand: Type = { + // 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 + // !!! inconsistent with TypeRef.etaExpand that uses initializedTypeParams + if (!isHigherKinded) this + else typeFun( + typeParams, + RefinedType( + parents map { + case TypeRef(pre, sym, List()) => TypeRef(pre, sym, dummyArgs) + case p => p + }, + decls, + typeSymbol)) } + override def kind = "RefinedType" } @@ -1960,8 +1734,8 @@ trait Types extends api.Types { self: SymbolTable => 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) + if (settings.debug && !sameLength(tparams, args)) + devWarning(s"Mismatched zip in computeRefs(): ${sym.info.typeParams}, $args") foreach2(tparams, args) { (tparam1, arg) => if (arg contains tparam) { @@ -2004,7 +1778,7 @@ trait Types extends api.Types { self: SymbolTable => var change = false for ((from, targets) <- refs(NonExpansive).iterator) for (target <- targets) { - var thatInfo = classInfo(target) + val thatInfo = classInfo(target) if (thatInfo.state != Initialized) change = change | thatInfo.propagate() addRefs(NonExpansive, from, thatInfo.getRefs(NonExpansive, target)) @@ -2012,7 +1786,7 @@ trait Types extends api.Types { self: SymbolTable => } for ((from, targets) <- refs(Expansive).iterator) for (target <- targets) { - var thatInfo = classInfo(target) + val thatInfo = classInfo(target) if (thatInfo.state != Initialized) change = change | thatInfo.propagate() addRefs(Expansive, from, thatInfo.getRefs(NonExpansive, target)) @@ -2023,27 +1797,13 @@ trait Types extends api.Types { self: SymbolTable => 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)) - fullyInitializeScope(decls).mkString(" {\n ", "\n ", "\n}") - else "" - ) + def formattedToString = parents.mkString("\n with ") + scopeString + override protected def shouldForceScope = settings.debug || decls.size > 1 + override protected def scopeString = initDecls.mkString(" {\n ", "\n ", "\n}") + override def safeToString = if (shouldForceScope) formattedToString else super.safeToString } object ClassInfoType extends ClassInfoTypeExtractor @@ -2052,19 +1812,14 @@ trait Types extends api.Types { self: SymbolTable => 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 deconst: Type = underlying.deconst 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" } @@ -2082,7 +1837,7 @@ trait Types extends api.Types { self: SymbolTable => private val pendingVolatiles = new mutable.HashSet[Symbol] class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) { - require(args0.nonEmpty, this) + require(args0 ne Nil, this) /** No unapplied type params size it has (should have) equally as many args. */ override def isHigherKinded = false @@ -2093,10 +1848,39 @@ trait Types extends api.Types { self: SymbolTable => // 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) + val tparams = sym.typeParams + if (tparams.size != args.size) + devWarning(s"$this.transform($tp), but tparams.isEmpty and args=$args") + def asSeenFromInstantiated(tp: Type) = + asSeenFromOwner(tp).instantiateTypeParams(tparams, args) + // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire + // type, we can end up with new symbols for the type parameters (clones from TypeMap). + // The subsequent substitution of type arguments would fail. This problem showed up during + // the fix for SI-8046, however the solution taken there wasn't quite right, and led to + // SI-8170. + // + // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate + // on the result type. + // + // TODO: Revisit this and explore the questions raised: + // + // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well? + // JZ: I think its largely irrelevant, as they are no longer referred to in the result type. + // In fact, you can get away with returning a type of kind * here and the sky doesn't fall: + // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)` + // But I thought it was better to retain the kind. + // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles. + // In general, it seems iffy the tparams can never occur in the result + // then we might as well represent the type as a no-arg typeref. + // AM: I've also been trying to track down uses of transform (pretty generic name for something that + // does not seem that widely applicable). + // It's kind of a helper for computing baseType (since it tries to propagate our type args to some + // other type, which has to be related to this type for that to make sense). + // + tp match { + case PolyType(`tparams`, result) => PolyType(tparams, asSeenFromInstantiated(result)) + case _ => asSeenFromInstantiated(tp) + } } // note: does not go through typeRef. There's no need to because @@ -2108,14 +1892,12 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -2140,7 +1922,7 @@ trait Types extends api.Types { self: SymbolTable => // 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 isHigherKinded = (typeParams ne Nil) override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym) @@ -2230,8 +2012,6 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -2252,7 +2032,7 @@ trait Types extends api.Types { self: SymbolTable => else ErrorType } - // isHKSubType0 introduces synthetic type params so that + // isHKSubType 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 @@ -2262,22 +2042,51 @@ trait Types extends api.Types { self: SymbolTable => // appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner) override 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 - } + /** SI-3731, SI-8177: when prefix is changed to `newPre`, maintain consistency of prefix and sym + * (where the symbol refers to a declaration "embedded" in the prefix). + * + * @returns newSym so that `newPre` binds `sym.name` to `newSym`, + * to remain consistent with `pre` previously binding `sym.name` to `sym`. + * + * `newSym` and `sym` are conceptually the same symbols, but some change to our `prefix` + * got them out of whack. (Usually triggered by substitution or `asSeenFrom`.) + * The only kind of "binds" we consider is where `prefix` (or its underlying type) + * is a refined type that declares `sym` (since the old prefix was discarded, + * the old symbol is now stale and we should update it, like in `def rebind`, + * except this is not for overriding symbols -- a vertical move -- but a "lateral" change.) + * + * The reason for this hack is that substitution and asSeenFrom clone RefinedTypes and + * their members, without updating the potential references to those members -- here, we aim to patch + * this up, so that: when changing a TypeRef(pre, sym, args) to a TypeRef(pre', sym', args'), and pre + * embeds a symbol sym (pre is a RefinedType(_, Scope(..., sym,...)) or a SingleType with such an + * underlying type), make sure that we update sym' to compensate for the change of pre -> pre' (which may + * have created a new symbol for the one the original sym referred to) + */ + override def coevolveSym(newPre: Type): Symbol = + if ((pre ne newPre) && embeddedSymbol(pre, sym.name) == sym) { + val newSym = embeddedSymbol(newPre, sym.name) + debuglog(s"co-evolve: ${pre} -> ${newPre}, $sym : ${sym.info} -> $newSym : ${newSym.info}") + // To deal with erroneous `preNew`, fallback via `orElse sym`, in case `preNew` does not have a decl named `sym.name`. + newSym orElse sym + } else sym + override def kind = "AliasTypeRef" } + // Return the symbol named `name` that's "embedded" in tp + // This is the case if `tp` is a `T{...; type/val $name ; ...}`, + // or a singleton type with such an underlying type. + private def embeddedSymbol(tp: Type, name: Name): Symbol = + // normalize to flatten nested RefinedTypes + // don't check whether tp is a RefinedType -- it may be a ThisType of one, for example + // TODO: check the resulting symbol is owned by the refinement class? likely an invariant... + if (tp.typeSymbol.isRefinementClass) tp.normalize.decls lookup name + else { + debuglog(s"no embedded symbol $name found in ${showRaw(tp)} --> ${tp.normalize.decls lookup name}") + NoSymbol + } + + trait AbstractTypeRef extends NonClassTypeRef { require(sym.isAbstractType, sym) @@ -2286,30 +2095,6 @@ trait Types extends api.Types { self: SymbolTable => 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)) { @@ -2324,9 +2109,7 @@ trait Types extends api.Types { self: SymbolTable => } 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" } @@ -2345,6 +2128,10 @@ trait Types extends api.Types { self: SymbolTable => trivial = fromBoolean(!sym.isTypeParameter && pre.isTrivial && areTrivialTypes(args)) toBoolean(trivial) } + private[scala] def invalidateCaches(): Unit = { + parentsPeriod = NoPeriod + baseTypeSeqPeriod = NoPeriod + } private[reflect] var parentsCache: List[Type] = _ private[reflect] var parentsPeriod = NoPeriod private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ @@ -2354,12 +2141,12 @@ trait Types extends api.Types { self: SymbolTable => //OPT specialize hashCode override final def computeHashCode = { import scala.util.hashing.MurmurHash3._ - val hasArgs = args.nonEmpty + val hasArgs = args ne Nil var h = productSeed h = mix(h, pre.hashCode) h = mix(h, sym.hashCode) if (hasArgs) - finalizeHash(mix(h, args.hashCode), 3) + finalizeHash(mix(h, args.hashCode()), 3) else finalizeHash(h, 2) } @@ -2390,7 +2177,7 @@ trait Types extends api.Types { self: SymbolTable => || pre.isGround && args.forall(_.isGround) ) - override def etaExpand: Type = { + final override def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams if (tpars.isEmpty) this @@ -2411,7 +2198,6 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -2420,9 +2206,6 @@ trait Types extends api.Types { self: SymbolTable => override def typeSymbol = sym override def typeSymbolDirect = sym - 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 @@ -2440,8 +2223,14 @@ trait Types extends api.Types { self: SymbolTable => } thisInfo.decls } - - protected[Types] def baseTypeSeqImpl: BaseTypeSeq = sym.info.baseTypeSeq map transform + protected[Types] def baseTypeSeqImpl: BaseTypeSeq = + if (sym.info.baseTypeSeq exists (_.typeSymbolDirect.isAbstractType)) + // SI-8046 base type sequence might have more elements in a subclass, we can't map it element wise. + transform(sym.info).baseTypeSeq + else + // Optimization: no abstract types, we can compute the BTS of this TypeRef as an element-wise map + // of the BTS of the referenced symbol. + sym.info.baseTypeSeq map transform override def baseTypeSeq: BaseTypeSeq = { val cache = baseTypeSeqCache @@ -2455,66 +2244,69 @@ trait Types extends api.Types { self: SymbolTable => baseTypeSeqCache } } - // ensure that symbol is not a local copy with a name coincidence private def needsPreString = ( - settings.debug.value + settings.debug || !shorthands(sym.fullName) || (sym.ownersIterator 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) ( - fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic) - map (_.defString) - mkString("{", "; ", "}") - ) + private def refinementDecls = fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic) + private def refinementString = ( + if (sym.isStructuralRefinement) + refinementDecls map (_.defString) mkString("{", "; ", "}") else "" ) - protected def finishPrefix(rest: String) = ( if (sym.isInitialized && sym.isAnonymousClass && !phase.erasedTypes) parentsString(thisInfo.parents) + refinementString else rest ) + private def noArgsString = finishPrefix(preString + sym.nameString) + private def tupleTypeString: String = args match { + case Nil => noArgsString + case arg :: Nil => s"($arg,)" + case _ => args.mkString("(", ", ", ")") + } private def customToString = sym match { - case RepeatedParamClass => args.head + "*" + case RepeatedParamClass | JavaRepeatedParamClass => args.head + "*" case ByNameParamClass => "=> " + args.head case _ => - def targs = normalize.typeArgs - - if (isFunctionType(this)) { + if (isFunctionTypeDirect(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) => + unspecializedTypeArgs(this) match { + // See neg/t588 for an example which arrives here - printing + // the type of a Function1 after erasure. + case Nil => noArgsString + case in :: out :: Nil if !isTupleTypeDirect(in) => // A => B => C should be (A => B) => C or A => (B => C). // Also if A is byname, then we want (=> A) => B because => is right associative and => A => B // would mean => (A => B) which is a different type - val in_s = if (isFunctionType(in) || isByNameParamType(in)) "(" + in + ")" else "" + in - val out_s = if (isFunctionType(out)) "(" + out + ")" else "" + out + val in_s = if (isFunctionTypeDirect(in) || isByNameParamType(in)) "(" + in + ")" else "" + in + val out_s = if (isFunctionTypeDirect(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 if (isTupleTypeDirect(this)) + tupleTypeString + else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne dealias)) + "" + dealias else "" } override def safeToString = { - val custom = if (settings.debug.value) "" else customToString + val custom = if (settings.debug) "" else customToString if (custom != "") custom else finishPrefix(preString + sym.nameString + argsString) } override def prefixString = "" + ( - if (settings.debug.value) + if (settings.debug) super.prefixString else if (sym.isOmittablePrefix) "" @@ -2525,23 +2317,33 @@ trait Types extends api.Types { self: SymbolTable => else super.prefixString ) + // Suppressing case class copy method which risks subverting our single point of creation. + private def copy = null override def kind = "TypeRef" } + // No longer defined as anonymous classes in `object TypeRef` to avoid an unnecessary outer pointer. + private final class AliasArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AliasTypeRef + private final class AbstractArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AbstractTypeRef + private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with ClassTypeRef + private final class AliasNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AliasTypeRef + private final class AbstractNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AbstractTypeRef + private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with ClassTypeRef + 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 + if (args ne Nil) { + if (sym.isAliasType) new AliasArgsTypeRef(pre, sym, args) + else if (sym.isAbstractType) new AbstractArgsTypeRef(pre, sym, args) + else new ClassArgsTypeRef(pre, sym, args) } else { - if (sym.isAliasType) new NoArgsTypeRef(pre, sym) with AliasTypeRef - else if (sym.isAbstractType) new NoArgsTypeRef(pre, sym) with AbstractTypeRef + if (sym.isAliasType) new AliasNoArgsTypeRef(pre, sym) + else if (sym.isAbstractType) new AbstractNoArgsTypeRef(pre, sym) 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 + else new ClassNoArgsTypeRef(pre, sym) } }) } @@ -2553,7 +2355,7 @@ trait Types extends api.Types { self: SymbolTable => 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) + tpe.parentsCache = List(AnyTpe) } } } @@ -2603,7 +2405,7 @@ trait Types extends api.Types { self: SymbolTable => true } - def isImplicit = params.nonEmpty && params.head.isImplicit + def isImplicit = (params ne Nil) && 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 @@ -2611,7 +2413,7 @@ trait Types extends api.Types { self: SymbolTable => override def paramss: List[List[Symbol]] = params :: resultType.paramss - override def paramTypes = params map (_.tpe) + override def paramTypes = mapList(params)(symTpe) // OPT use mapList rather than .map override def boundSyms = resultType.boundSyms ++ params @@ -2634,8 +2436,6 @@ trait Types extends api.Types { self: SymbolTable => //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) = { @@ -2662,17 +2462,15 @@ trait Types extends api.Types { self: SymbolTable => 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 baseTypeSeqDepth: Depth = 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" } @@ -2707,12 +2505,10 @@ trait Types extends api.Types { self: SymbolTable => 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 baseTypeSeqDepth: Depth = 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 @@ -2757,7 +2553,6 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -2781,8 +2576,59 @@ trait Types extends api.Types { self: SymbolTable => 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) + // TODO: check invariant that all quantifiers have the same (existing) owner + private def quantifierOwner = quantified collectFirst { case q if q.owner.exists => q.owner } getOrElse NoSymbol + + // Is this existential of the form: T[Q1, ..., QN] forSome { type Q1 >: L1 <: U1, ..., QN >: LN <: UN} + private def isStraightApplication = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a } + + /** [SI-6169, SI-8197 -- companion to SI-1786] + * + * Approximation to improve the bounds of a Java-defined existential type, + * based on the bounds of the type parameters of the quantified type + * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_] + * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does: + * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types + * + * Also tried doing this once during class file parsing or when creating the existential type, + * but that causes cyclic errors because it happens too early. + * + * NOTE: we're only modifying the skolems to avoid leaking the sharper bounds to `quantified` (SI-8283) + * + * TODO: figure out how to do this earlier without running into cycles, so this can subsume the fix for SI-1786 + */ + override def skolemizeExistential(owner0: Symbol, origin: AnyRef) = { + val owner = owner0 orElse quantifierOwner + + // do this here because it's quite close to what Java does: + // when checking subtyping/type equality, enter constraints + // derived from the existentially quantified type into the typing environment + // (aka \Gamma, which tracks types for variables and constraints/kinds for types) + // as a nice bonus, delaying this until we need it avoids cyclic errors + def tpars = underlying.typeSymbol.initialize.typeParams + + def newSkolem(quant: Symbol) = owner.newExistentialSkolem(quant, origin) + def newSharpenedSkolem(quant: Symbol, tparam: Symbol): Symbol = { + def emptyBounds(sym: Symbol) = sym.info.bounds.isEmptyBounds + + // avoid creating cycles [pos/t2940] that consist of an existential quantifier's + // bounded by an existential type that unhygienically has that quantifier as its own quantifier + // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now) + val canSharpen = ( + emptyBounds(quant) && !emptyBounds(tparam) + && (existentialsInType(tparam.info) intersect quantified).isEmpty + ) + + val skolemInfo = if (!canSharpen) quant.info else tparam.info.substSym(tpars, quantified) + + owner.newExistentialSkolem(quant.name.toTypeName, skolemInfo, quant.flags, quant.pos, origin) + } + + val canSharpenBounds = (underlying.typeSymbol.isJavaDefined || sharperSkolems) && isStraightApplication + + if (canSharpenBounds) deriveType2(quantified, tpars, newSharpenedSkolem)(underlying) + else deriveType(quantified, newSkolem)(underlying) + } private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map { case TypeRef(_, sym, _) if (qset contains sym) => @@ -2820,10 +2666,10 @@ trait Types extends api.Types { self: SymbolTable => override def safeToString: String = { def clauses = { val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") - if (settings.explaintypes.value) "(" + str + ")" else str + if (settings.explaintypes) "(" + str + ")" else str } underlying match { - case TypeRef(pre, sym, args) if !settings.debug.value && isRepresentableWithWildcards => + case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards => "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => "(" + underlying + ")" + clauses @@ -2842,13 +2688,13 @@ trait Types extends api.Types { self: SymbolTable => def withTypeVars(op: Type => Boolean): Boolean = withTypeVars(op, AnyDepth) - def withTypeVars(op: Type => Boolean, depth: Int): Boolean = { + def withTypeVars(op: Type => Boolean, depth: Depth): 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)) + solve(tvars, quantifiedFresh, quantifiedFresh map (_ => Invariant), upper = false, depth) && + isWithinBounds(NoPrefix, NoSymbol, quantifiedFresh, tvars map (_.inst)) } } } @@ -2865,9 +2711,17 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "OverloadedType" } - def overloadedType(pre: Type, alternatives: List[Symbol]): Type = - if (alternatives.tail.isEmpty) pre memberType alternatives.head - else OverloadedType(pre, alternatives) + /** The canonical creator for OverloadedTypes. + */ + def overloadedType(pre: Type, alternatives: List[Symbol]): Type = alternatives match { + case Nil => NoType + case alt :: Nil => pre memberType alt + case _ => OverloadedType(pre, alternatives) + } + + case class ImportType(expr: Tree) extends Type { + override def safeToString = "ImportType("+expr+")" + } /** A class remembering a type instantiation for some a set of overloaded * polymorphic symbols. @@ -2875,23 +2729,12 @@ trait Types extends api.Types { self: SymbolTable => */ case class AntiPolyType(pre: Type, targs: List[Type]) extends Type { override def safeToString = - pre.toString + targs.mkString("(with type arguments ", ", ", ")"); + 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) @@ -2906,13 +2749,10 @@ trait Types extends api.Types { self: SymbolTable => } } - // 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 + object ArrayTypeRef { + def unapply(tp: Type) = tp match { + case TypeRef(_, ArrayClass, arg :: Nil) => Some(arg) + case _ => None } } @@ -2941,10 +2781,10 @@ trait Types extends api.Types { self: SymbolTable => * 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. + /* 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 typeIsNonClassType) @@ -2985,20 +2825,6 @@ trait Types extends api.Types { self: SymbolTable => 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 typeIsExistentiallyBound - - def existentialsInType(tpe: Type) = - tpe withFilter typeIsExistentiallyBound map (_.typeSymbol) - /** Precondition: params.nonEmpty. (args.nonEmpty enforced structurally.) */ class HKTypeVar( @@ -3009,7 +2835,6 @@ trait Types extends api.Types { self: SymbolTable => 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.) @@ -3025,9 +2850,7 @@ trait Types extends api.Types { self: SymbolTable => 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 + "[", ", ", "]") - ) + override def safeToString: String = super.safeToString + typeArgs.map(_.safeToString).mkString("[", ", ", "]") } trait UntouchableTypeVar extends TypeVar { @@ -3057,7 +2880,7 @@ trait Types extends api.Types { self: SymbolTable => * Precondition for this class, enforced structurally: args.isEmpty && params.isEmpty. */ abstract case class TypeVar( - val origin: Type, + origin: Type, var constr: TypeConstraint ) extends Type { @@ -3077,9 +2900,10 @@ trait Types extends api.Types { self: SymbolTable => * 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 + def inst = constr.inst + def instWithinBounds = constr.instWithinBounds + override def isGround = instValid && inst.isGround /** The variable's skolemization level */ val level = skolemizationLevel @@ -3098,6 +2922,7 @@ trait Types extends api.Types { self: SymbolTable => this else if (newArgs.size == params.size) { val tv = TypeVar(origin, constr, newArgs, params) + tv.linkSuspended(this) TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) } else @@ -3120,8 +2945,7 @@ trait Types extends api.Types { self: SymbolTable => // 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. + // typevar, which means we'll need to repack our 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 @@ -3132,7 +2956,10 @@ trait Types extends api.Types { self: SymbolTable => // 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): this.type = { -// assert(!(tp containsTp this), this) + if (tp eq this) { + log(s"TypeVar cycle: called setInst passing $this to itself.") + return 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 @@ -3144,20 +2971,43 @@ trait Types extends api.Types { self: SymbolTable => 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) + if (!sharesConstraints(tp)) { + 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) + if (!sharesConstraints(tp)) { + undoLog record this + constr.addHiBound(tp, isNumericBound) + } } // </region> // ignore subtyping&equality checks while true -- see findMember - private[Types] var suspended = false + // OPT: This could be Either[TypeVar, Boolean], but this encoding was chosen instead to save allocations. + private var _suspended: Type = ConstantFalse + private[Types] def suspended: Boolean = (_suspended: @unchecked) match { + case ConstantFalse => false + case ConstantTrue => true + case tv: TypeVar => tv.suspended + } + + /** `AppliedTypeVar`s share the same `TypeConstraint` with the `HKTypeVar` that it was spawned from. + * A type inference session can also have more than one ATV. + * If we don't detect that, we end up with "cyclic constraint" when we try to instantiate type parameters + * after solving in, pos/t8237 + */ + protected final def sharesConstraints(other: Type): Boolean = other match { + case other: TypeVar => constr == other.constr // SI-8237 avoid cycles. Details in pos/t8237.scala + case _ => false + } + private[Types] def suspended_=(b: Boolean): Unit = _suspended = if (b) ConstantTrue else ConstantFalse + // SI-7785 Link the suspended attribute of a TypeVar created in, say, a TypeMap (e.g. AsSeenFrom) to its originator + private[Types] def linkSuspended(origin: TypeVar): Unit = _suspended = origin /** 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, @@ -3187,7 +3037,7 @@ trait Types extends api.Types { self: SymbolTable => else lhs <:< rhs } - /** Simple case: type arguments can be ignored, because either this typevar has + /* 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 @@ -3214,7 +3064,7 @@ trait Types extends api.Types { self: SymbolTable => } else false } - /** Full case: involving a check of the form + /* Full case: involving a check of the form * {{{ * TC1[T1,..., TN] <: TC2[T'1,...,T'N] * }}} @@ -3263,8 +3113,8 @@ trait Types extends api.Types { self: SymbolTable => // 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 if (instValid) // type var is already set + checkSubtype(tp, inst) else isRelatable(tp) && { unifySimple || unifyFull(tp) || ( // only look harder if our gaze is oriented toward Any @@ -3280,17 +3130,20 @@ trait Types extends api.Types { self: SymbolTable => } 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 +// println("regTypeEq: "+(safeToString, debugString(tp), tp.getClass, if (typeVarLHS) "in LHS" else "in RHS", if (suspended) "ZZ" else if (instValid) "IV" else "")) //@MDEBUG + def checkIsSameType(tp: Type) = ( + if (typeVarLHS) inst =:= tp + else tp =:= inst + ) if (suspended) tp =:= origin - else if (constr.instValid) checkIsSameType(tp) + else if (instValid) checkIsSameType(tp) else isRelatable(tp) && { val newInst = wildcardToTypeVarMap(tp) - (constr isWithinBounds newInst) && { setInst(tp); true } + (constr isWithinBounds newInst) && { + setInst(newInst) + true + } } } @@ -3301,7 +3154,7 @@ trait Types extends api.Types { self: SymbolTable => * (`T` corresponds to @param sym) */ def registerTypeSelection(sym: Symbol, tp: Type): Boolean = { - registerBound(HasTypeMember(sym.name.toTypeName, tp), false) + registerBound(HasTypeMember(sym.name.toTypeName, tp), isLowerBound = false) } private def isSkolemAboveLevel(tp: Type) = tp.typeSymbol match { @@ -3323,15 +3176,17 @@ trait Types extends api.Types { self: SymbolTable => ) override def normalize: Type = ( - if (constr.instValid) constr.inst + if (instValid) inst // get here when checking higher-order subtyping of the typevar by itself // TODO: check whether this ever happens? - else if (isHigherKinded) logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor)))) + else if (isHigherKinded) etaExpand else super.normalize ) + override def etaExpand: Type = ( + if (!isHigherKinded) this + else logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor)))) + ) 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 => @@ -3352,13 +3207,13 @@ trait Types extends api.Types { self: SymbolTable => 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 + private def levelString = if (settings.explaintypes) level else "" override def safeToString = ( - if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>" - else if (constr.inst ne NoType) "=?" + constr.inst + if ((constr eq null) || (inst eq null)) "TVar<" + originName + "=null>" + else if (inst ne NoType) "=?" + inst else (if(untouchable) "!?" else "?") + levelString + originName ) + def originString = s"$originName in $originLocation" override def kind = "TypeVar" def cloneInternal = { @@ -3376,13 +3231,9 @@ trait Types extends api.Types { self: SymbolTable => * * @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) + override val underlying: Type) extends RewrappingTypeProxy with AnnotatedTypeApi { assert(!annotations.isEmpty, "" + underlying) @@ -3415,9 +3266,6 @@ trait Types extends api.Types { self: SymbolTable => */ 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. */ @@ -3432,7 +3280,7 @@ trait Types extends api.Types { self: SymbolTable => 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) + else AnnotatedType(annotations1, underlying1) } /** Return the base type sequence of tp, dropping the annotations, unless the base type sequence of tp @@ -3451,12 +3299,19 @@ trait Types extends api.Types { self: SymbolTable => /** 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 = + def annotatedType(annots: List[AnnotationInfo], underlying: Type): Type = if (annots.isEmpty) underlying - else AnnotatedType(annots, underlying, selfsym) + else AnnotatedType(annots, underlying) object AnnotatedType extends AnnotatedTypeExtractor + object StaticallyAnnotatedType { + def unapply(tp: Type): Option[(List[AnnotationInfo], Type)] = tp.staticAnnotations match { + case Nil => None + case annots => Some((annots, tp.withoutAnnotations)) + } + } + /** A class representing types with a name. When an application uses * named arguments, the named argument types for calling isApplicable * are represented as NamedType. @@ -3464,39 +3319,36 @@ trait Types extends api.Types { self: SymbolTable => 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. + /** As with NamedType, used only when calling isApplicable. + * Records that the application has a wildcard star (aka _*) + * at the end of it. */ - 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+")" - } + case class RepeatedType(tp: Type) extends Type { + override def safeToString: String = tp + ": _*" } /** A temporary type representing the erasure of a user-defined value type. * Created during phase erasure, eliminated again in posterasure. * - * @param original The underlying type before erasure + * SI-6385 Erasure's creation of bridges considers method signatures `exitingErasure`, + * which contain `ErasedValueType`-s. In order to correctly consider the overriding + * and overriden signatures as equivalent in `run/t6385.scala`, it is critical that + * this type contains the erasure of the wrapped type, rather than the unerased type + * of the value class itself, as was originally done. + * + * @param valueClazz The value class symbol + * @param erasedUnderlying The erased type of the unboxed value */ - abstract case class ErasedValueType(original: TypeRef) extends UniqueType { - override def safeToString = "ErasedValueType("+original+")" + abstract case class ErasedValueType(valueClazz: Symbol, erasedUnderlying: Type) extends UniqueType { + override def safeToString = s"ErasedValueType($valueClazz, $erasedUnderlying)" } - final class UniqueErasedValueType(original: TypeRef) extends ErasedValueType(original) + final class UniqueErasedValueType(valueClazz: Symbol, erasedUnderlying: Type) extends ErasedValueType(valueClazz, erasedUnderlying) object ErasedValueType { - def apply(original: TypeRef): Type = { - assert(original.sym ne NoSymbol, "ErasedValueType over NoSymbol") - unique(new UniqueErasedValueType(original)) + def apply(valueClazz: Symbol, erasedUnderlying: Type): Type = { + assert(valueClazz ne NoSymbol, "ErasedValueType over NoSymbol") + unique(new UniqueErasedValueType(valueClazz, erasedUnderlying)) } } @@ -3524,17 +3376,19 @@ trait Types extends api.Types { self: SymbolTable => (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 + else pre.nonPrivateMember(sym.name).suchThat { sym => + // SI-7928 `isModuleNotMethod` is here to avoid crashing with spuriously "overloaded" module accessor and module symbols. + // These appear after refchecks eliminates ModuleDefs that implement an interface. + // Here, we exclude the module symbol, which allows us to bind to the accessor. + // SI-8054 We must only do this after refchecks, otherwise we exclude the module symbol which does not yet have an accessor! + val isModuleWithAccessor = phase.refChecked && sym.isModuleNotMethod + sym.isType || (!isModuleWithAccessor && sym.isStable && !sym.hasVolatileType) + } orElse sym } /** Convert a `super` prefix to a this-type if `sym` is abstract or final. */ @@ -3563,7 +3417,7 @@ trait Types extends api.Types { self: SymbolTable => /** 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 + if (parents.isEmpty) ObjectTpe else parents.head else { val clazz = owner.newRefinementClass(pos) val result = RefinedType(parents, decls, clazz) @@ -3573,10 +3427,6 @@ trait Types extends api.Types { self: SymbolTable => } /** 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) @@ -3584,7 +3434,7 @@ trait Types extends api.Types { self: SymbolTable => 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 owner = original.typeSymbol.owner val result = refinedType(parents, owner) val syms1 = decls.toList for (sym <- syms1) @@ -3685,7 +3535,7 @@ trait Types extends api.Types { self: SymbolTable => if (args.isEmpty) return tycon //@M! `if (args.isEmpty) tycon' is crucial (otherwise we create new types in phases after typer and then they don't get adapted (??)) - /** Disabled - causes cycles in tcpoly tests. */ + /* Disabled - causes cycles in tcpoly tests. */ if (false && isDefinitionsInitialized) { assert(isUseableAsTypeArgs(args), { val tapp_s = s"""$tycon[${args mkString ", "}]""" @@ -3696,43 +3546,30 @@ trait Types extends api.Types { self: SymbolTable => 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 TypeRef(pre, sym, Nil) => copyTypeRef(tycon, pre, sym, args) + case TypeRef(pre, sym, bogons) => devWarning(s"Dropping $bogons from $tycon in appliedType.") ; 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 RefinedType(parents, decls) => RefinedType(parents map (appliedType(_, args)), decls) // @PP: Can this be right? + case TypeBounds(lo, hi) => TypeBounds(appliedType(lo, args), appliedType(hi, args)) // @PP: Can this be right? 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 AnnotatedType(annots, underlying) => AnnotatedType(annots, appliedType(underlying, args)) + case ErrorType | WildcardType => tycon case _ => abort(debugString(tycon)) } } + def appliedType(tycon: Type, args: Type*): Type = + appliedType(tycon, args.toList) + + def appliedType(tyconSym: Symbol, args: List[Type]): Type = + appliedType(tyconSym.typeConstructor, args) + /** 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). @@ -3759,7 +3596,7 @@ trait Types extends api.Types { self: SymbolTable => } def genPolyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) - @deprecated("use genPolyType(...) instead", "2.10.0") + @deprecated("use genPolyType(...) instead", "2.10.0") // Used in reflection API 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. @@ -3806,131 +3643,7 @@ trait Types extends api.Types { self: SymbolTable => newExistentialType(tparams1, tpe1) } - /** Normalize any type aliases within this type (@see Type#normalize). - * Note that this depends very much on the call to "normalize", not "dealias", - * so it is no longer carries the too-stealthy name "deAlias". - */ - object normalizeAliases extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(_, sym, _) if sym.isAliasType => - def msg = if (tp.isHigherKinded) s"Normalizing type alias function $tp" else s"Dealiasing type alias $tp" - mapOver(logResult(msg)(tp.normalize)) - case _ => mapOver(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) => - parents filter (_.typeSymbol != SingletonClass) match { - case Nil => AnyClass.tpe - case p :: Nil if decls.isEmpty => mapOver(p) - case ps => mapOver(copyRefinedType(tp1, ps, 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 - - /** Turn any T* types into Seq[T] except when - * in method parameter position. - */ - object dropRepeatedParamType extends TypeMap { - def apply(tp: Type): Type = tp match { - case MethodType(params, restpe) => - // Not mapping over params - val restpe1 = apply(restpe) - if (restpe eq restpe1) tp - else MethodType(params, restpe1) - 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 -------------------------------------------------------------- @@ -3942,7 +3655,11 @@ trait Types extends api.Types { self: SymbolTable => if (Statistics.canEnable) Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { uniques = util.WeakHashSet[Type](initialUniquesCapacity) - perRunCaches.recordCache(uniques) + // JZ: We used to register this as a perRunCache so it would be cleared eagerly at + // the end of the compilation run. But, that facility didn't actually clear this map (SI-8129)! + // When i fixed that bug, run/tpeCache-tyconCache.scala started failing. Why was that? + // I've removed the registration for now. I don't think its particularly harmful anymore + // as a) this is now a weak set, and b) it is discarded completely before the next run. uniqueRunId = currentRunId } (uniques findEntryOrUpdate tp).asInstanceOf[T] @@ -3950,124 +3667,9 @@ trait Types extends api.Types { self: SymbolTable => // 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 typeIsNothing - private var hibounds = hi0 filterNot typeIsAny - 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) { - // For some reason which is still a bit fuzzy, we must let Nothing through as - // a lower bound despite the fact that Nothing is always a lower bound. My current - // supposition is that the side-effecting type constraint accumulation mechanism - // depends on these subtype tests being performed to make forward progress when - // there are mutally recursive type vars. - // See pos/t6367 and pos/t6499 for the competing test cases. - val mustConsider = tp.typeSymbol match { - case NothingClass => true - case _ => !(lobounds contains tp) - } - if (mustConsider) { - 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) { - // My current test case only demonstrates the need to let Nothing through as - // a lower bound, but I suspect the situation is symmetrical. - val mustConsider = tp.typeSymbol match { - case AnyClass => true - case _ => !(hibounds contains tp) - } - if (mustConsider) { - 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 typeIsNothing - val hi = hiBounds filterNot typeIsAny - 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 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) @@ -4075,309 +3677,69 @@ trait Types extends api.Types { self: SymbolTable => } } class ClassUnwrapper(existential: Boolean) extends TypeUnwrapper(poly = true, existential, annotated = true, nullary = false) { - override def apply(tp: Type) = super.apply(tp.normalize) + override def apply(tp: Type) = super.apply(tp.normalize) // normalize is required here } 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]) = - //OPT inline from forall to save on #closures - origSyms match { - case sym :: rest => - val v = variance - if (sym.isAliasType) variance = 0 - val result = this(sym.info) - variance = v - (result eq sym.info) && noChangeToSymbols(rest) - case _ => - true - } - - 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) + def elementExtract(container: Symbol, tp: Type): Type = { + assert(!container.isAliasType, container) + unwrapWrapperTypes(tp baseType container).dealiasWiden match { + case TypeRef(_, `container`, arg :: Nil) => arg + case _ => NoType } } - - // 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: _*) + def elementExtractOption(container: Symbol, tp: Type): Option[Type] = { + elementExtract(container, tp) match { + case NoType => None + case tp => Some(tp) } - - /** 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 elementTest(container: Symbol, tp: Type)(f: Type => Boolean): Boolean = { + elementExtract(container, tp) match { + case NoType => false + case tp => f(tp) } - - def mapOverAnnotations(annots: List[AnnotationInfo]): List[AnnotationInfo] = { - val annots1 = annots mapConserve mapOver - if (annots1 eq annots) annots - else annots1 filterNot (_ eq UnmappableAnnotation) + } + def elementTransform(container: Symbol, tp: Type)(f: Type => Type): Type = { + elementExtract(container, tp) match { + case NoType => NoType + case tp => f(tp) } + } - /** 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) - } + def transparentShallowTransform(container: Symbol, tp: Type)(f: Type => Type): Type = { + def loop(tp: Type): Type = tp match { + case tp @ AnnotatedType(_, underlying) => tp.copy(underlying = loop(underlying)) + case tp @ ExistentialType(_, underlying) => tp.copy(underlying = loop(underlying)) + case tp @ PolyType(_, resultType) => tp.copy(resultType = loop(resultType)) + case tp @ NullaryMethodType(resultType) => tp.copy(resultType = loop(resultType)) + case tp => elementTransform(container, tp)(el => appliedType(container, f(el))).orElse(f(tp)) } + loop(tp) } - abstract class TypeTraverser extends TypeMap { - def traverse(tp: Type): Unit - def apply(tp: Type): Type = { traverse(tp); tp } - } + /** 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) + ) - abstract class TypeTraverserWithResult[T] extends TypeTraverser { - def result: T - def clear(): Unit - } + def containsExistential(tpe: Type) = tpe exists typeIsExistentiallyBound + def existentialsInType(tpe: Type) = tpe withFilter typeIsExistentiallyBound map (_.typeSymbol) - abstract class TypeCollector[T](initial: T) extends TypeTraverser { - var result: T = _ - def collect(tp: Type) = { - result = initial - traverse(tp) - result - } + private def isDummyOf(tpe: Type)(targ: Type) = { + val sym = targ.typeSymbol + sym.isTypeParameter && sym.owner == tpe.typeSymbol + } + def isDummyAppliedType(tp: Type) = tp.dealias match { + case tr @ TypeRef(_, _, args) => args exists isDummyOf(tr) + case _ => false } - - /** 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) => @@ -4388,630 +3750,23 @@ trait Types extends api.Types { self: SymbolTable => def typeParamsToExistentials(clazz: Symbol): List[Symbol] = typeParamsToExistentials(clazz, clazz.typeParams) + def isRawIfWithoutArgs(sym: Symbol) = sym.isClass && sym.typeParams.nonEmpty && sym.isJavaDefined + /** Is type tp a ''raw type''? */ // 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))) - - /** Might the given symbol be important when calculating the prefix - * of a type? When tp.asSeenFrom(pre, clazz) is called on `tp`, - * the result will be `tp` unchanged if `pre` is trivial and `clazz` - * is a symbol such that isPossiblePrefix(clazz) == false. - */ - def isPossiblePrefix(clazz: Symbol) = clazz.isClass && !clazz.isPackageClass + def isRawType(tp: Type) = !phase.erasedTypes && (tp match { + case TypeRef(_, sym, Nil) => isRawIfWithoutArgs(sym) + case _ => false + }) - private def skipPrefixOf(pre: Type, clazz: Symbol) = ( - (pre eq NoType) || (pre eq NoPrefix) || !isPossiblePrefix(clazz) + @deprecated("Use isRawType", "2.10.1") // presently used by sbt + def isRaw(sym: Symbol, args: List[Type]) = ( + !phase.erasedTypes + && args.isEmpty + && isRawIfWithoutArgs(sym) ) - /** 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() - - override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { - object annotationArgRewriter extends TypeMapTransformer { - private def canRewriteThis(sym: Symbol) = ( - (sym isNonBottomSubClass clazz) - && (pre.widen.typeSymbol isNonBottomSubClass sym) - && (pre.isStable || giveup()) - ) - // what symbol should really be used? - private def newTermSym() = { - val p = pre.typeSymbol - p.owner.newValue(p.name.toTermName, p.pos) setInfo pre - } - /** Rewrite `This` trees in annotation argument trees */ - override def transform(tree: Tree): Tree = super.transform(tree) match { - case This(_) if canRewriteThis(tree.symbol) => gen.mkAttributedQualifier(pre, newTermSym()) - case tree => tree - } - } - annotationArgRewriter.transform(tree) - } - - def stabilize(pre: Type, clazz: Symbol): Type = { - capturedParams find (_.owner == clazz) match { - case Some(qvar) => qvar.tpe - case _ => - val qvar = clazz freshExistential nme.SINGLETON_SUFFIX setInfo singletonBounds(pre) - capturedParams ::= qvar - qvar.tpe - } - } - - def apply(tp: Type): Type = - tp match { - case ThisType(sym) => - def toPrefix(pre: Type, clazz: Symbol): Type = - if (skipPrefixOf(pre, clazz)) 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(pre.baseType(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 (skipPrefixOf(pre, clazz)) 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 typeIsErroneous) - 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(pre.baseType(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.nonEmpty && (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) { - def this(pairs: (Symbol, Symbol)*) = this(pairs.toList.map(_._1), pairs.toList.map(_._2)) - - 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) - // mapOver takes care of subst'ing in args - mapOver ( if (sym eq newSym) tp else copyTypeRef(tp, pre, newSym, args) ) - // assert(newSym.typeParams.length == sym.typeParams.length, "typars mismatch in SubstSymMap: "+(sym, sym.typeParams, newSym, newSym.typeParams)) - case SingleType(pre, sym) if pre ne NoPrefix => - val newSym = subst(sym, from, to) - mapOver( if (sym eq newSym) tp else singleType(pre, newSym) ) - case _ => - super.apply(tp) - } - } - - object mapTreeSymbols extends TypeMapTransformer { - val strictCopy = newStrictTreeCopier - - def termMapsTo(sym: Symbol) = from indexOf sym match { - case -1 => None - case idx => Some(to(idx)) - } - - // if tree.symbol is mapped to another symbol, passes the new symbol into the - // constructor `trans` and sets the symbol and the type on the resulting tree. - def transformIfMapped(tree: Tree)(trans: Symbol => Tree) = termMapsTo(tree.symbol) match { - case Some(toSym) => trans(toSym) setSymbol toSym setType tree.tpe - case None => tree - } - - // changes trees which refer to one of the mapped symbols. trees are copied before attributes are modified. - override def transform(tree: Tree) = { - // super.transform maps symbol references in the types of `tree`. it also copies trees where necessary. - super.transform(tree) match { - case id @ Ident(_) => - transformIfMapped(id)(toSym => - strictCopy.Ident(id, toSym.name)) - - case sel @ Select(qual, name) => - transformIfMapped(sel)(toSym => - strictCopy.Select(sel, qual, toSym.name)) - - case tree => tree - } - } - } - override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { - mapTreeSymbols.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.dealias) - } - } - - object ApproximateDependentMap extends TypeMap { - def apply(tp: Type): Type = - if (tp.isImmediatelyDependent) WildcardType - else mapOver(tp) - } - - /** Note: This map is needed even for non-dependent method types, despite what the name might imply. - */ - 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(param.name.toTypeName append nme.SINGLETON_SUFFIX, 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) - } - } - } + def singletonBounds(hi: Type) = TypeBounds.upper(intersectionType(List(hi, SingletonClass.tpe))) /** * A more persistent version of `Type#memberType` which does not require @@ -5059,194 +3814,15 @@ trait Types extends api.Types { self: SymbolTable => result } - /** 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 - } - } - - final case class SubTypePair(tp1: Type, tp2: Type) { - // SI-8146 we used to implement equality here in terms of pairwise =:=. - // But, this was inconsistent with hashCode, which was based on the - // Type#hashCode, based on the structure of types, not the meaning. - // Now, we use `Type#{equals,hashCode}` as the (consistent) basis for - // detecting cycles (aka keeping subtyping decidable.) - // - // I added tests to show that we detect the cycle: neg/t8146-no-finitary* - - override def toString = tp1+" <:<? "+tp2 - } - // Helper Methods ------------------------------------------------------------- /** The maximum allowable depth of lubs or glbs over types `ts`. */ - def lubDepth(ts: List[Type]): Int = { + def lubDepth(ts: List[Type]): Depth = { val td = typeDepth(ts) val bd = baseTypeSeqDepth(ts) lubDepthAdjust(td, td max bd) @@ -5256,48 +3832,17 @@ trait Types extends api.Types { self: SymbolTable => * as a function over the maximum depth `td` of these types, and * the maximum depth `bd` of all types in the base type sequences of these types. */ - private def lubDepthAdjust(td: Int, bd: Int): Int = - if (settings.XfullLubs.value) bd - else if (bd <= 3) bd - else if (bd <= 5) td max (bd - 1) - else if (bd <= 7) td max (bd - 2) - else (td - 1) max (bd - 3) - - /** The maximum depth of type `tp` */ - def typeDepth(tp: Type): Int = tp match { - case TypeRef(pre, sym, args) => - typeDepth(pre) max typeDepth(args) + 1 - case RefinedType(parents, decls) => - typeDepth(parents) max typeDepth(decls.toList.map(_.info)) + 1 - case TypeBounds(lo, hi) => - typeDepth(lo) max typeDepth(hi) - case MethodType(paramtypes, result) => - typeDepth(result) - case NullaryMethodType(result) => - typeDepth(result) - case PolyType(tparams, result) => - typeDepth(result) max typeDepth(tparams map (_.info)) + 1 - case ExistentialType(tparams, result) => - typeDepth(result) max typeDepth(tparams map (_.info)) + 1 - case _ => - 1 - } - - private def maxDepth(tps: List[Type], by: Type => Int): Int = { - //OPT replaced with tailrecursive function to save on #closures - // was: - // var d = 0 - // for (tp <- tps) d = d max by(tp) //!!!OPT!!! - // d - def loop(tps: List[Type], acc: Int): Int = tps match { - case tp :: rest => loop(rest, acc max by(tp)) - case _ => acc - } - loop(tps, 0) - } + private def lubDepthAdjust(td: Depth, bd: Depth): Depth = ( + if (settings.XfullLubs) bd + else if (bd <= Depth(3)) bd + else if (bd <= Depth(5)) td max bd.decr + else if (bd <= Depth(7)) td max (bd decr 2) + else td.decr max (bd decr 3) + ) - private def typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) - private def baseTypeSeqDepth(tps: List[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) + private def symTypeDepth(syms: List[Symbol]): Depth = typeDepth(syms map (_.info)) + private def typeDepth(tps: List[Type]): Depth = maxDepth(tps) + private def baseTypeSeqDepth(tps: List[Type]): Depth = maxbaseTypeSeqDepth(tps) /** Is intersection of given types populated? That is, * for all types tp1, tp2 in intersection @@ -5310,32 +3855,35 @@ trait Types extends api.Types { self: SymbolTable => 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 - } + assert(sym1 == sym2, (sym1, sym2)) + ( pre1 =:= pre2 + && forall3(args1, args2, sym1.typeParams) { (arg1, arg2, tparam) => + // 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.isInvariant) + arg1 =:= arg2 + else !arg1.isInstanceOf[TypeVar] || { + if (tparam.variance.isContravariant) arg1 <:< arg2 + else arg2 <:< arg1 + } + } + ) case (et: ExistentialType, _) => et.withTypeVars(isConsistent(_, tp2)) case (_, et: ExistentialType) => et.withTypeVars(isConsistent(tp1, _)) } - def check(tp1: Type, tp2: Type) = + 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. + check(tp1, tp2) && check(tp2, tp1) } /** Does a pattern of type `patType` need an outer test when executed against @@ -5378,85 +3926,14 @@ trait Types extends api.Types { self: SymbolTable => } } - 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 || sym1.owner.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 { - if (Statistics.canEnable) Statistics.incCounter(sametypeCount) - subsametypeRecursions += 1 - //OPT cutdown on Function0 allocation - //was: -// undoLog undoUnless { -// isSameType1(tp1, tp2) -// } - - undoLog.lock() - try { - val before = undoLog.log - var result = false - - try result = { - isSameType1(tp1, tp2) - } finally if (!result) undoLog.undoTo(before) - result - } finally undoLog.unlock() - } 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) = + def normalizePlus(tp: Type) = ( if (isRawType(tp)) rawToExistential(tp) - else tp.normalize + else tp.normalize match { + // Unify the two representations of module classes + case st @ SingleType(_, sym) if sym.isModule => st.underlying.normalize + case _ => tp.normalize + } + ) /* todo: change to: @@ -5468,288 +3945,7 @@ trait Types extends api.Types { self: SymbolTable => 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 => - 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)(_ =:= _) @@ -5767,103 +3963,49 @@ trait Types extends api.Types { self: SymbolTable => */ 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 - - //OPT cutdown on Function0 allocation - //was: -// 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) -// } -// } - - undoLog.lock() - try { - val before = undoLog.log - var result = false - - try result = { // if subtype test fails, it should not affect constraints on typevars - if (subsametypeRecursions >= LogPendingSubTypesThreshold) { - val p = new SubTypePair(tp1, tp2) - if (pendingSubTypes(p)) - false // see neg/t8146-no-finitary* - else - try { - pendingSubTypes += p - isSubType2(tp1, tp2, depth) - } finally { - pendingSubTypes -= p - } - } else { - isSubType2(tp1, tp2, depth) - } - } finally if (!result) undoLog.undoTo(before) + private var _basetypeRecursions: Int = 0 + def basetypeRecursions = _basetypeRecursions + def basetypeRecursions_=(value: Int) = _basetypeRecursions = value - result - } finally undoLog.unlock() - } 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() - } + private val _pendingBaseTypes = new mutable.HashSet[Type] + def pendingBaseTypes = _pendingBaseTypes /** 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 (as determined by `=:=`) prefixes are * considered equal in regard to `=:=`. */ - 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 - } - - @deprecated("The compiler doesn't use this so you shouldn't either - it will be removed", "2.10.0") - 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 isEligibleForPrefixUnification(tp: Type): Boolean = tp match { + case SingleType(pre, sym) => !(sym hasFlag PACKAGE) && isEligibleForPrefixUnification(pre) + case tv@TypeVar(_, constr) => !tv.instValid || isEligibleForPrefixUnification(constr.inst) + case RefinedType(_, _) => true + case _ => false } def isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType) + /** This appears to be equivalent to tp.isInstanceof[SingletonType], + * except it excludes ConstantTypes. + */ def isSingleType(tp: Type) = tp match { case ThisType(_) | SuperType(_, _) | SingleType(_, _) => true - case _ => false + case _ => false } def isConstantType(tp: Type) = tp match { case ConstantType(_) => true - case _ => false + case _ => false + } + + def isExistentialType(tp: Type): Boolean = tp match { + case _: ExistentialType => true + case tp: Type if tp.dealias ne tp => isExistentialType(tp.dealias) + case _ => false + } + + def isImplicitMethodType(tp: Type) = tp match { + case mt: MethodType => mt.isImplicit + case _ => false } /** This is defined and named as it is because the goal is to exclude source @@ -5891,10 +4033,11 @@ trait Types extends api.Types { self: SymbolTable => * types which are used internally in type applications and * types which are not. */ + /**** Not used right now, but kept around to document which Types + * land in which bucket. private def isInternalTypeNotUsedAsTypeArg(tp: Type): Boolean = tp match { case AntiPolyType(pre, targs) => true case ClassInfoType(parents, defs, clazz) => true - case DeBruijnIndex(level, index, args) => true case ErasedValueType(tref) => true case NoPrefix => true case NoType => true @@ -5902,6 +4045,7 @@ trait Types extends api.Types { self: SymbolTable => case TypeBounds(lo, hi) => true case _ => false } + ****/ private def isInternalTypeUsedAsTypeArg(tp: Type): Boolean = tp match { case WildcardType => true case BoundedWildcardType(_) => true @@ -5929,7 +4073,7 @@ trait Types extends api.Types { self: SymbolTable => private def isValueElseNonValue(tp: Type): Boolean = tp match { case tp if isAlwaysValueType(tp) => true case tp if isAlwaysNonValueType(tp) => false - case AnnotatedType(_, underlying, _) => isValueElseNonValue(underlying) + case AnnotatedType(_, underlying) => isValueElseNonValue(underlying) case SingleType(_, sym) => sym.isValue // excludes packages and statics case TypeRef(_, _, _) if tp.isHigherKinded => false // excludes type constructors case ThisType(sym) => !sym.isPackageClass // excludes packages @@ -5947,7 +4091,7 @@ trait Types extends api.Types { self: SymbolTable => * useful as documentation; it is likely that !isNonValueType(tp) * will serve better than isValueType(tp). */ - def isValueType(tp: Type) = isValueElseNonValue(tp) + /** def isValueType(tp: Type) = isValueElseNonValue(tp) */ /** SLS 3.3, Non-Value Types * Is the given type definitely a non-value type, as defined in SLS 3.3? @@ -5958,7 +4102,7 @@ trait Types extends api.Types { self: SymbolTable => * not designated non-value types because there is code which depends on using * them as type arguments, but their precise status is unclear. */ - def isNonValueType(tp: Type) = !isValueElseNonValue(tp) + /** def isNonValueType(tp: Type) = !isValueElseNonValue(tp) */ def isNonRefinementClassType(tpe: Type) = tpe match { case SingleType(_, sym) => sym.isModuleClass @@ -5967,294 +4111,70 @@ trait Types extends api.Types { self: SymbolTable => 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 isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol], depth: Int): Boolean = { - def isSubArg(t1: Type, t2: Type, variance: Int) = - (variance > 0 || isSubType(t2, t1, depth)) && - (variance < 0 || isSubType(t1, t2, depth)) - 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 || sym1.owner.hasPackageFlag || isSubType(pre1, pre2, depth) - 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, depth)) - || - sym2.isClass && { - val base = tr1 baseType sym2 - (base ne tr1) && isSubType(base, tr2, depth) - } - || - thirdTryRef(tr1, tr2)) - case _ => - secondTry - } - case AnnotatedType(_, _, _) => - isSubType(tp1.withoutAnnotations, tp2.withoutAnnotations, depth) && - annotationsConform(tp1, tp2) - case BoundedWildcardType(bounds) => - isSubType(tp1, bounds.hi, depth) - 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(_, _, _) => - isSubType(tp1.withoutAnnotations, tp2.withoutAnnotations, depth) && - annotationsConform(tp1, tp2) - case BoundedWildcardType(bounds) => - isSubType(tp1.bounds.lo, tp2, depth) - case tv @ TypeVar(_,_) => - tv.registerBound(tp2, false) - case ExistentialType(_, _) => - try { - skolemizationLevel += 1 - isSubType(tp1.skolemizeExistential, tp2, depth) - } 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) && - isSubType(tp1, tp2a, depth) || - fourthTry - } else { - isSubType(tp1.normalize, tp2.normalize, depth) - } - case _ => - fourthTry - } - } + def isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol], depth: Depth): Boolean = { + def isSubArg(t1: Type, t2: Type, variance: Variance) = ( + (variance.isCovariant || isSubType(t2, t1, depth)) // The order of these two checks can be material for performance (SI-8478) + && (variance.isContravariant || isSubType(t1, t2, depth)) + ) - /** 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 (isSubType(tp1, _, depth))) && - (rt2.decls forall (specializesSym(tp1, _, depth))) - case et2: ExistentialType => - et2.withTypeVars(isSubType(tp1, _, depth), depth) || fourthTry - case nn2: NotNullType => - tp1.isNotNull && isSubType(tp1, nn2.underlying, depth) - 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) && - isSubType(res1.substSym(params1, params2), res2, depth)) - // 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(_) => - isSubType(pt1.resultType, pt2.resultType, depth) - case _ => - false - } - case TypeBounds(lo2, hi2) => - tp1 match { - case TypeBounds(lo1, hi1) => - isSubType(lo2, lo1, depth) && isSubType(hi1, hi2, depth) - case _ => - false - } - case _ => - fourthTry - } + corresponds3(tps1, tps2, mapList(tparams)(_.variance))(isSubArg) + } - /** 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) && isSubType(tp1, tp2.widen, depth) - } - 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) && isSubType(tp1a, tp2, depth) - } else { - isSubType(tp1.normalize, tp2.normalize, depth) - } - case _ => - false - } - case RefinedType(parents1, _) => - parents1 exists (isSubType(_, tp2, depth)) - case _: SingletonType | _: NotNullType => - isSubType(tp1.underlying, tp2, depth) - case _ => - false - } + def specializesSym(tp: Type, sym: Symbol, depth: Depth): Boolean = { + def directlySpecializedBy(member: Symbol): Boolean = ( + member == sym + || specializesSym(tp.narrow, member, sym.owner.thisType, sym, depth) + ) + // Closure reduction, else this would be simply `member exists directlySpecializedBy` + def specializedBy(member: Symbol): Boolean = ( + if (member eq NoSymbol) false + else if (member.isOverloaded) member.alternatives exists directlySpecializedBy + else directlySpecializedBy(member) + ) - firstTry + ( (tp.typeSymbol isBottomSubClass sym.owner) + || specializedBy(tp nonPrivateMember sym.name) + ) } - 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`? + /** Does member `symLo` of `tpLo` have a stronger type + * than member `symHi` of `tpHi`? */ - def isSubTypes(tps1: List[Type], tps2: List[Type]): Boolean = (tps1 corresponds tps2)(_ <:< _) + protected[internal] def specializesSym(preLo: Type, symLo: Symbol, preHi: Type, symHi: Symbol, depth: Depth): Boolean = + (symHi.isAliasType || symHi.isTerm || symHi.isAbstractType) && { + // only now that we know symHi is a viable candidate ^^^^^^^, do the expensive checks: ----V + require((symLo ne NoSymbol) && (symHi ne NoSymbol), ((preLo, symLo, preHi, symHi, depth))) + + val tpHi = preHi.memberInfo(symHi).substThis(preHi.typeSymbol, preLo) + + // Should we use memberType or memberInfo? + // memberType transforms (using `asSeenFrom`) `sym.tpe`, + // whereas memberInfo performs the same transform on `sym.info`. + // For term symbols, this ends up being the same thing (`sym.tpe == sym.info`). + // For type symbols, however, the `.info` of an abstract type member + // is defined by its bounds, whereas its `.tpe` is a `TypeRef` to that type symbol, + // so that `sym.tpe <:< sym.info`, but not the other way around. + // + // Thus, for the strongest (correct) result, + // we should use `memberType` on the low side. + // + // On the high side, we should use the result appropriate + // for the right side of the `<:<` above (`memberInfo`). + val tpLo = preLo.memberType(symLo) - /** 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 = - specializesSym(tp, sym, AnyDepth) - - def specializesSym(tp: Type, sym: Symbol, depth: Int): Boolean = - tp.typeSymbol == NothingClass || - tp.typeSymbol == NullClass && containsNull(sym.owner) || { - def specializedBy(membr: Symbol): Boolean = - membr == sym || specializesSym(tp.narrow, membr, sym.owner.thisType, sym, depth) - val member = tp.nonPrivateMember(sym.name) - if (member eq NoSymbol) false - else if (member.isOverloaded) member.alternatives exists specializedBy - else specializedBy(member) - // was - // (tp.nonPrivateMember(sym.name).alternatives exists - // (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym, depth))) - } + debuglog(s"specializesSymHi: $preHi . $symHi : $tpHi") + debuglog(s"specializesSymLo: $preLo . $symLo : $tpLo") - /** 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, depth: Int): 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 && isSubType(info1, info2, depth) && (!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 - ) - } + if (symHi.isTerm) + (isSubType(tpLo, tpHi, depth) && + (!symHi.isStable || symLo.isStable) && // sub-member must remain stable + (!symLo.hasVolatileType || symHi.hasVolatileType || tpHi.isWildcard)) // sub-member must not introduce volatility + else if (symHi.isAbstractType) + ((tpHi.bounds containsType tpLo) && + kindsConform(symHi :: Nil, tpLo :: Nil, preLo, symLo.owner)) + else // we know `symHi.isAliasType` (see above) + tpLo =:= tpHi + } /** A function implementing `tp1` matches `tp2`. */ final def matchesType(tp1: Type, tp2: Type, alwaysMatchSimple: Boolean): Boolean = { @@ -6265,7 +4185,7 @@ trait Types extends api.Types { self: SymbolTable => def lastTry = tp2 match { case ExistentialType(_, res2) if alwaysMatchSimple => - matchesType(tp1, res2, true) + matchesType(tp1, res2, alwaysMatchSimple = true) case MethodType(_, _) => false case PolyType(_, _) => @@ -6285,7 +4205,7 @@ trait Types extends api.Types { self: SymbolTable => if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple) else matchesType(tp1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => - alwaysMatchSimple && matchesType(tp1, res2, true) + alwaysMatchSimple && matchesType(tp1, res2, alwaysMatchSimple = true) case TypeRef(_, sym, Nil) => params1.isEmpty && sym.isModuleClass && matchesType(res1, tp2, alwaysMatchSimple) case _ => @@ -6298,7 +4218,7 @@ trait Types extends api.Types { self: SymbolTable => case NullaryMethodType(res2) => matchesType(res1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => - alwaysMatchSimple && matchesType(tp1, res2, true) + alwaysMatchSimple && matchesType(tp1, res2, alwaysMatchSimple = true) case TypeRef(_, sym, Nil) if sym.isModuleClass => matchesType(res1, tp2, alwaysMatchSimple) case _ => @@ -6312,7 +4232,7 @@ trait Types extends api.Types { self: SymbolTable => else matchesQuantified(tparams1, tparams2, res1, res2) case ExistentialType(_, res2) => - alwaysMatchSimple && matchesType(tp1, res2, true) + alwaysMatchSimple && matchesType(tp1, res2, alwaysMatchSimple = true) case _ => false // remember that tparams1.nonEmpty is now an invariant of PolyType } @@ -6321,7 +4241,7 @@ trait Types extends api.Types { self: SymbolTable => case ExistentialType(tparams2, res2) => matchesQuantified(tparams1, tparams2, res1, res2) case _ => - if (alwaysMatchSimple) matchesType(res1, tp2, true) + if (alwaysMatchSimple) matchesType(res1, tp2, alwaysMatchSimple = true) else lastTry } case TypeRef(_, sym, Nil) if sym.isModuleClass => @@ -6374,7 +4294,7 @@ trait Types extends api.Types { self: SymbolTable => */ /** 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 { + protected[internal] def matchingParams(syms1: List[Symbol], syms2: List[Symbol], syms1isJava: Boolean, syms2isJava: Boolean): Boolean = syms1 match { case Nil => syms2.isEmpty case sym1 :: rest1 => @@ -6391,99 +4311,6 @@ trait Types extends api.Types { self: SymbolTable => } } - /** 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) { - log(s"$tvar addHiBound $bound.instantiateTypeParams($tparams, $tvars)") - tvar addHiBound bound.instantiateTypeParams(tparams, tvars) - } - for (tparam2 <- tparams) - tparam2.info.bounds.lo.dealias match { - case TypeRef(_, `tparam`, _) => - log(s"$tvar addHiBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)") - tvar addHiBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars) - case _ => - } - } else { - if (bound.typeSymbol != NothingClass && bound.typeSymbol != tparam) { - log(s"$tvar addLoBound $bound.instantiateTypeParams($tparams, $tvars)") - tvar addLoBound bound.instantiateTypeParams(tparams, tvars) - } - for (tparam2 <- tparams) - tparam2.info.bounds.hi.dealias match { - case TypeRef(_, `tparam`, _) => - log(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)") - 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))) - val newInst = ( - 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) - } - ) - log(s"$tvar setInst $newInst") - tvar setInst newInst - //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 = { @@ -6494,167 +4321,7 @@ trait Types extends api.Types { self: SymbolTable => } 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 typeListIsEmpty)) 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 regarding 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 of a list of types (as determined by `Symbol.isLess`). */ - 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 List(t) => List(t) - case t :: ts1 => - val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) - if (rest exists (t1 => t1 <:< t)) rest else t :: rest - } + mapList(tparams)(_.info.asSeenFrom(pre, owner).instantiateTypeParams(tparams, targs).bounds) def elimAnonymousClass(t: Type) = t match { case TypeRef(pre, clazz, Nil) if clazz.isAnonymousClass => @@ -6662,397 +4329,6 @@ trait Types extends api.Types { self: SymbolTable => 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 List(t) => List(t) - 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.dealiasWiden)) - 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): Type = tp match { - case ExistentialType(_, res) => - res - case tv@TypeVar(_, constr) => - if (tv.instValid) stripType(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 exists typeHasAnnotations) - (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 exists typeHasAnnotations) { - (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): Boolean = ( - 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 _ => - if (Statistics.canEnable) Statistics.incCounter(lubCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null - try { - lub(ts, lubDepth(ts)) - } finally { - lubResults.clear() - glbResults.clear() - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - } - } - - /** 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 @ (mt @ MethodType(params, _)) :: rest => - MethodType(params, lub0(matchingRestypes(ts, mt.paramTypes))) - 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 @ AnnotatedType(annots, tpe, _) :: rest => - annotationsLub(lub0(ts map (_.withoutAnnotations)), ts) - 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, depth))) - } - // 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, depth) - } 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 => - isSubType(t, lubRefined, depth) || { - 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 - } - } - // dropRepeatedParamType is a localized fix for SI-6897. We should probably - // integrate that transformation at a lower level in master, but lubs are - // the likely and maybe only spot they escape, so fixing here for 2.10.1. - existentialAbstraction(tparams, dropRepeatedParamType(lubType)) - } - if (printLubs) { - println(indent + "lub of " + ts + " at depth "+depth)//debug - indent = indent + " " - assert(indent.length <= 100) - } - if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) - val res = lub0(ts) - if (printLubs) { - indent = indent stripSuffix " " - println(indent + "lub of " + ts + " is " + res)//debug - } - if (ts forall typeIsNotNull) 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 of a list of types (as determined by `<:<`). */ - def glb(ts: List[Type]): Type = elimSuper(ts) match { - case List() => AnyClass.tpe - case List(t) => t - case ts0 => - if (Statistics.canEnable) Statistics.incCounter(lubCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null - try { - glbNorm(ts0, lubDepth(ts0)) - } finally { - lubResults.clear() - glbResults.clear() - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - } - } - - 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 of a list of types (as determined by `<:<`), which have been normalized - * with regard to `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 @ (mt @ MethodType(params, _)) :: rest => - MethodType(params, glbNorm(matchingRestypes(ts, mt.paramTypes), 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 && !specializesSym(glbThisType, sym, depth)) - try { - addMember(glbThisType, glbRefined, glbsym(sym), depth) - } 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 - - if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) - 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 typeIsNotNull) res.notNull else res - } /** A list of the typevars in a type. */ def typeVarsInType(tp: Type): List[TypeVar] = { @@ -7063,29 +4339,32 @@ trait Types extends api.Types { self: SymbolTable => } 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... + + // 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 pos/t0851 for a situation where this happens. + @inline final def suspendingTypeVars[T](tvs: List[TypeVar])(op: => T): T = { + val saved = tvs map (_.suspended) tvs foreach (_.suspended = true) - tvs + + try op + finally foreach2(tvs, saved)(_.suspended = _) } - /** Compute lub (if `variance == 1`) or glb (if `variance == -1`) of given list + /** Compute lub (if `variance == Covariant`) or glb (if `variance == Contravariant`) 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. + * Return `x` if the computation succeeds with result `x`. + * Return `NoType` if the computation fails. */ - def mergePrefixAndArgs(tps: List[Type], variance: Int, depth: Int): Option[Type] = tps match { - case List(tp) => - Some(tp) + def mergePrefixAndArgs(tps: List[Type], variance: Variance, depth: Depth): Type = tps match { + case tp :: Nil => 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 pre = if (variance.isPositive) 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 { @@ -7094,12 +4373,13 @@ trait Types extends api.Types { self: SymbolTable => // 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 typeListIsEmpty) { - None // something is wrong: an array without a type arg. - } else { + NoType // 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)))) + if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head)) + else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe + else typeRef(pre, sym, List(lub(args))) } } else transposeSafe(argss) match { @@ -7107,30 +4387,28 @@ trait Types extends api.Types { self: SymbolTable => // 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 + debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss") + NoType 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 + val args = map2(sym.typeParams, argsst) { (tparam, as0) => + val as = as0.distinct + if (as.size == 1) as.head + else if (depth.isZero) { + log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString)) + // Don't return "Any" (or "Nothing") when we have to give up due to + // recursion depth. Return NoType, which prevents us from poisoning + // lublist's results. It can recognize the recursion and deal with it, but + // only if we aren't returning invalid types. + NoType } else { - if (tparam.variance == variance) lub(as, decr(depth)) - else if (tparam.variance == -variance) glb(as, decr(depth)) + if (tparam.variance == variance) lub(as, depth.decr) + else if (tparam.variance == variance.flip) glb(as, depth.decr) else { - val l = lub(as, decr(depth)) - val g = glb(as, decr(depth)) + val l = lub(as, depth.decr) + val g = glb(as, depth.decr) 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 + 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 @@ -7141,22 +4419,22 @@ trait Types extends api.Types { self: SymbolTable => } } } - if (args contains NoType) None - else Some(existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))) + if (args contains NoType) NoType + else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args)) } } catch { - case ex: MalformedType => None + case ex: MalformedType => NoType } 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 - } + val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth) + try singleType(pre, sym) + catch { case ex: MalformedType => NoType } case ExistentialType(tparams, quantified) :: rest => - mergePrefixAndArgs(quantified :: rest, variance, depth) map (existentialAbstraction(tparams, _)) + mergePrefixAndArgs(quantified :: rest, variance, depth) match { + case NoType => NoType + case tpe => existentialAbstraction(tparams, tpe) + } case _ => abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps") } @@ -7166,14 +4444,14 @@ trait Types extends api.Types { self: SymbolTable => /** 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, depth: Int) { + def addMember(thistp: Type, tp: Type, sym: Symbol, depth: Depth) { assert(sym != NoSymbol) // debuglog("add member " + sym+":"+sym.info+" to "+thistp) //DEBUG if (!specializesSym(thistp, sym, depth)) { if (sym.isTerm) for (alt <- tp.nonPrivateDecl(sym.name).alternatives) if (specializesSym(thistp, sym, thistp, alt, depth)) - tp.decls unlink alt; + tp.decls unlink alt tp.decls enter sym } } @@ -7186,51 +4464,6 @@ trait Types extends api.Types { self: SymbolTable => def inheritsJavaVarArgsMethod(clazz: Symbol) = clazz.thisType.baseClasses exists isJavaVarargsAncestor - /** 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 mt @ MethodType(params1, res) if isSameTypes(mt.paramTypes, pts) => - res - case NullaryMethodType(res) if pts.isEmpty => - res - case _ => - throw new NoCommonType(tps) - } - // Errors and Diagnostics ----------------------------------------------------- /** A throwable signalling a type error */ @@ -7243,7 +4476,7 @@ trait Types extends api.Types { self: SymbolTable => /** 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() + if (settings.debug) printStackTrace() } class NoCommonType(tps: List[Type]) extends Throwable( @@ -7255,26 +4488,28 @@ trait Types extends api.Types { self: SymbolTable => } /** The current indentation string for traces */ - private var indent: String = "" + private var _indent: String = "" + protected def indent = _indent + protected def indent_=(value: String) = _indent = value /** 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+")"*/) + inform(indent + tp1 + " " + op + " " + arg2 + "?" /* + "("+tp1.getClass+","+arg2.getClass+")"*/) indent = indent + " " val result = p(tp1, arg2) indent = indent stripSuffix " " - Console.println(indent + result) + inform(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 (settings.explaintypes) 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)) + if (settings.explaintypes) withTypesExplained(op(found, required)) } /** Execute `op` while printing a trace of the operations on types executed. */ @@ -7284,18 +4519,18 @@ trait Types extends api.Types { self: SymbolTable => } def isUnboundedGeneric(tp: Type) = tp match { - case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefClass.tpe) + case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefTpe) case _ => false } def isBoundedGeneric(tp: Type) = tp match { - case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe) + case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefTpe) 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 typeIsSubTypeOfSerializable) ps.toList - else (ps :+ SerializableClass.tpe).toList + else (ps :+ SerializableTpe).toList ) /** Adds the @uncheckedBound annotation if the given `tp` has type arguments */ @@ -7307,11 +4542,14 @@ trait Types extends api.Types { self: SymbolTable => /** Members of the given class, other than those inherited * from Any or AnyRef. */ - def nonTrivialMembers(clazz: Symbol): Iterable[Symbol] = - clazz.info.members filterNot (sym => sym.owner == ObjectClass || sym.owner == AnyClass) + def nonTrivialMembers(clazz: Symbol): Scope = clazz.info.members filterNot isUniversalMember + + /** Members which can be imported into other scopes. + */ + def importableMembers(pre: Type): Scope = pre.members filter isImportable def objToAny(tp: Type): Type = - if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe + if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyTpe else tp val shorthands = Set( @@ -7324,46 +4562,57 @@ trait Types extends api.Types { self: SymbolTable => "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) { - debugwarn("Exceeded recursion depth attempting to print type.") - if (settings.debug.value) - (new Throwable).printStackTrace - - "..." - } - else - try { - tostringRecursions += 1 - tpe.safeToString - } finally { - tostringRecursions -= 1 - } - // ----- Hoisted closures and convenience methods, for compile time reductions ------- - private[scala] val typeIsNotNull = (tp: Type) => tp.isNotNull private[scala] val isTypeVar = (tp: Type) => tp.isInstanceOf[TypeVar] private[scala] val typeContainsTypeVar = (tp: Type) => tp exists isTypeVar private[scala] val typeIsNonClassType = (tp: Type) => tp.typeSymbolDirect.isNonClassType private[scala] val typeIsExistentiallyBound = (tp: Type) => tp.typeSymbol.isExistentiallyBound private[scala] val typeIsErroneous = (tp: Type) => tp.isErroneous - private[scala] val typeIsError = (tp: Type) => tp.isError - private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations.nonEmpty + private[scala] val symTypeIsError = (sym: Symbol) => sym.tpe.isError + private[scala] val treeTpe = (t: Tree) => t.tpe + private[scala] val symTpe = (sym: Symbol) => sym.tpe + private[scala] val symInfo = (sym: Symbol) => sym.info + private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations ne Nil private[scala] val boundsContainType = (bounds: TypeBounds, tp: Type) => bounds containsType tp private[scala] val typeListIsEmpty = (ts: List[Type]) => ts.isEmpty - private[scala] val typeIsSubTypeOfSerializable = (tp: Type) => tp <:< SerializableClass.tpe + private[scala] val typeIsSubTypeOfSerializable = (tp: Type) => tp <:< SerializableTpe private[scala] val typeIsNothing = (tp: Type) => tp.typeSymbolDirect eq NothingClass private[scala] val typeIsAny = (tp: Type) => tp.typeSymbolDirect eq AnyClass private[scala] val typeIsHigherKinded = (tp: Type) => tp.isHigherKinded + /** The maximum depth of type `tp` */ + def typeDepth(tp: Type): Depth = tp match { + case TypeRef(pre, sym, args) => typeDepth(pre) max typeDepth(args).incr + case RefinedType(parents, decls) => typeDepth(parents) max symTypeDepth(decls.toList).incr + case TypeBounds(lo, hi) => typeDepth(lo) max typeDepth(hi) + case MethodType(paramtypes, result) => typeDepth(result) + case NullaryMethodType(result) => typeDepth(result) + case PolyType(tparams, result) => typeDepth(result) max symTypeDepth(tparams).incr + case ExistentialType(tparams, result) => typeDepth(result) max symTypeDepth(tparams).incr + case _ => Depth(1) + } + + //OPT replaced with tailrecursive function to save on #closures + // was: + // var d = 0 + // for (tp <- tps) d = d max by(tp) //!!!OPT!!! + // d + private[scala] def maxDepth(tps: List[Type]): Depth = { + @tailrec def loop(tps: List[Type], acc: Depth): Depth = tps match { + case tp :: rest => loop(rest, acc max typeDepth(tp)) + case _ => acc + } + loop(tps, Depth.Zero) + } + private[scala] def maxbaseTypeSeqDepth(tps: List[Type]): Depth = { + @tailrec def loop(tps: List[Type], acc: Depth): Depth = tps match { + case tp :: rest => loop(rest, acc max tp.baseTypeSeqDepth) + case _ => acc + } + loop(tps, Depth.Zero) + } + @tailrec private def typesContain(tps: List[Type], sym: Symbol): Boolean = tps match { case tp :: rest => (tp contains sym) || typesContain(rest, sym) case _ => false @@ -7400,10 +4649,15 @@ trait Types extends api.Types { self: SymbolTable => } +object TypeConstants { + final val DefaultLogThreshhold = 50 + final val LogPendingBaseTypesThreshold = DefaultLogThreshhold + final val LogVolatileThreshold = DefaultLogThreshhold +} + object TypesStats { import BaseTypeSeqsStats._ val rawTypeCount = Statistics.newCounter ("#raw type creations") - val asSeenFromCount = Statistics.newCounter ("#asSeenFrom ops") val subtypeCount = Statistics.newCounter ("#subtype ops") val sametypeCount = Statistics.newCounter ("#sametype ops") val lubCount = Statistics.newCounter ("#toplevel lubs/glbs") @@ -7425,7 +4679,7 @@ object TypesStats { val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) val typeOpsStack = Statistics.newTimerStack() - /** Commented out, because right now this does not inline, so creates a closure which will distort statistics + /* Commented out, because right now this does not inline, so creates a closure which will distort statistics @inline final def timedTypeOp[T](c: Statistics.StackableTimer)(op: => T): T = { val start = Statistics.pushTimer(typeOpsStack, c) try op diff --git a/src/reflect/scala/reflect/internal/Variance.scala b/src/reflect/scala/reflect/internal/Variance.scala new file mode 100644 index 0000000000..ecc5d99a40 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Variance.scala @@ -0,0 +1,90 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect +package internal + +import Variance._ + +/** Variances form a lattice: + * + * - Covariant - + * / \ + * Invariant Bivariant + * \ / + * Contravariant + * + * The variance of a symbol within a type is calculated based on variance + * annotations, e.g. +A or -A, and the positions of the types in which the + * symbol appears. The actual mechanics are beyond the scope of this + * comment, but the essential operations on a Variance are: + * + * '&' - like bitwise AND. Unless all inputs have compatible variance, + * folding them across & will be invariant. + * '*' - like multiplication across { -1, 0, 1 } with contravariance as -1. + * flip - if contravariant or covariant, flip to the other; otherwise leave unchanged. + * cut - if bivariant, remain bivariant; otherwise become invariant. + * + * There is an important distinction between "isPositive" and "isCovariant". + * The former is true for both Covariant and Bivariant, but the latter is true + * only for Covariant. + */ +final class Variance private (val flags: Int) extends AnyVal { + def isBivariant = flags == 2 + def isCovariant = flags == 1 // excludes bivariant + def isInvariant = flags == 0 + def isContravariant = flags == -1 // excludes bivariant + def isPositive = flags > 0 // covariant or bivariant + + def &(other: Variance): Variance = ( + if (this == other) this + else if (this.isBivariant) other + else if (other.isBivariant) this + else Invariant + ) + + def *(other: Variance): Variance = ( + if (other.isPositive) this + else if (other.isContravariant) this.flip + else this.cut + ) + + /** Flip between covariant and contravariant. I chose not to use unary_- because it doesn't stand out enough. */ + def flip = if (isCovariant) Contravariant else if (isContravariant) Covariant else this + + /** Map everything below bivariant to invariant. */ + def cut = if (isBivariant) this else Invariant + + /** The symbolic annotation used to indicate the given kind of variance. */ + def symbolicString = ( + if (isCovariant) "+" + else if (isContravariant) "-" + else "" + ) + + override def toString = ( + if (isContravariant) "contravariant" + else if (isCovariant) "covariant" + else if (isInvariant) "invariant" + else "" // noisy to print bivariant on everything without type parameters + ) +} + +object Variance { + implicit class SbtCompat(val v: Variance) { + def < (other: Int) = v.flags < other + def > (other: Int) = v.flags > other + } + + def fold(variances: List[Variance]): Variance = ( + if (variances.isEmpty) Bivariant + else variances reduceLeft (_ & _) + ) + val Bivariant = new Variance(2) + val Covariant = new Variance(1) + val Contravariant = new Variance(-1) + val Invariant = new Variance(0) +} diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala new file mode 100644 index 0000000000..cfe2ad8b87 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -0,0 +1,218 @@ +/* NSC -- new scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala +package reflect +package internal + +import Variance._ +import scala.collection.{ mutable, immutable } +import scala.annotation.tailrec + +/** See comments at scala.reflect.internal.Variance. + */ +trait Variances { + self: SymbolTable => + + /** Used in Refchecks. + * TODO - eliminate duplication with varianceInType + */ + class VarianceValidator extends Traverser { + private val escapedLocals = mutable.HashSet[Symbol]() + // A flag for when we're in a refinement, meaning method parameter types + // need to be checked. + private var inRefinement = false + @inline private def withinRefinement(body: => Type): Type = { + val saved = inRefinement + inRefinement = true + try body finally inRefinement = saved + } + + /** Is every symbol in the owner chain between `site` and the owner of `sym` + * either a term symbol or private[this]? If not, add `sym` to the set of + * esacped locals. + * @pre sym.isLocalToThis + */ + @tailrec final def checkForEscape(sym: Symbol, site: Symbol) { + if (site == sym.owner || site == sym.owner.moduleClass || site.hasPackageFlag) () // done + else if (site.isTerm || site.isPrivateLocal) checkForEscape(sym, site.owner) // ok - recurse to owner + else escapedLocals += sym + } + + protected def issueVarianceError(base: Symbol, sym: Symbol, required: Variance): Unit = () + + // Flip occurrences of type parameters and parameters, unless + // - it's a constructor, or case class factory or extractor + // - it's a type parameter of tvar's owner. + def shouldFlip(sym: Symbol, tvar: Symbol) = ( + sym.isParameter + && !(tvar.isTypeParameterOrSkolem && sym.isTypeParameterOrSkolem && tvar.owner == sym.owner) + ) + // return Bivariant if `sym` is local to a term + // or is private[this] or protected[this] + def isLocalOnly(sym: Symbol) = !sym.owner.isClass || ( + sym.isTerm // ?? shouldn't this be sym.owner.isTerm according to the comments above? + && (sym.isLocalToThis || sym.isSuperAccessor) // super accessors are implicitly local #4345 + && !escapedLocals(sym) + ) + + private object ValidateVarianceMap extends TypeMap(trackVariance = true) { + private var base: Symbol = _ + + /** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`. + * The search proceeds from `base` to the owner of `tvar`. + * Initially the state is covariant, but it might change along the search. + * + * A local alias type is treated as Bivariant; + * this is OK because we always expand aliases for variance checking. + * However, for an alias which might be externally visible, we must assume Invariant, + * because there may be references to the type parameter that are not checked, + * leading to unsoundness (see SI-6566). + */ + def relativeVariance(tvar: Symbol): Variance = { + def nextVariance(sym: Symbol, v: Variance): Variance = ( + if (shouldFlip(sym, tvar)) v.flip + else if (isLocalOnly(sym)) Bivariant + else if (sym.isAliasType) ( + // Unsound pre-2.11 behavior preserved under -Xsource:2.10 + if (settings.isScala211 || sym.isOverridingSymbol) Invariant + else { + deprecationWarning(sym.pos, s"Construct depends on unsound variance analysis and will not compile in scala 2.11 and beyond") + Bivariant + } + ) + else v + ) + def loop(sym: Symbol, v: Variance): Variance = ( + if (sym == tvar.owner || v.isBivariant) v + else loop(sym.owner, nextVariance(sym, v)) + ) + loop(base, Covariant) + } + def isUncheckedVariance(tp: Type) = tp match { + case AnnotatedType(annots, _) => annots exists (_ matches definitions.uncheckedVarianceClass) + case _ => false + } + + private def checkVarianceOfSymbol(sym: Symbol) { + val relative = relativeVariance(sym) + val required = relative * variance + if (!relative.isBivariant) { + def sym_s = s"$sym (${sym.variance}${sym.locationString})" + def base_s = s"$base in ${base.owner}" + (if (base.owner.isClass) "" else " in " + base.owner.enclClass) + log(s"verifying $sym_s is $required at $base_s") + if (sym.variance != required) + issueVarianceError(base, sym, required) + } + } + override def mapOver(decls: Scope): Scope = { + decls foreach (sym => withVariance(if (sym.isAliasType) Invariant else variance)(this(sym.info))) + decls + } + private def resultTypeOnly(tp: Type) = tp match { + case mt: MethodType => !inRefinement + case pt: PolyType => true + case _ => false + } + + /** For PolyTypes, type parameters are skipped because they are defined + * explicitly (their TypeDefs will be passed here.) For MethodTypes, the + * same is true of the parameters (ValDefs) unless we are inside a + * refinement, in which case they are checked from here. + */ + def apply(tp: Type): Type = tp match { + case _ if isUncheckedVariance(tp) => tp + case _ if resultTypeOnly(tp) => this(tp.resultType) + case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize) + case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp) + case RefinedType(_, _) => withinRefinement(mapOver(tp)) + case ClassInfoType(parents, _, _) => parents foreach this ; tp + case mt @ MethodType(_, result) => flipped(mt.paramTypes foreach this) ; this(result) + case _ => mapOver(tp) + } + def validateDefinition(base: Symbol) { + val saved = this.base + this.base = base + try apply(base.info) + finally this.base = saved + } + } + + /** Validate variance of info of symbol `base` */ + private def validateVariance(base: Symbol) { + ValidateVarianceMap validateDefinition base + } + + override def traverse(tree: Tree) { + def sym = tree.symbol + // No variance check for object-private/protected methods/values. + // Or constructors, or case class factory or extractor. + def skip = ( + sym == NoSymbol + || sym.isLocalToThis + || sym.owner.isConstructor + || sym.owner.isCaseApplyOrUnapply + ) + tree match { + case defn: MemberDef if skip => + debuglog(s"Skipping variance check of ${sym.defString}") + case ClassDef(_, _, _, _) | TypeDef(_, _, _, _) => + validateVariance(sym) + super.traverse(tree) + // ModuleDefs need not be considered because they have been eliminated already + case ValDef(_, _, _, _) => + validateVariance(sym) + case DefDef(_, _, tparams, vparamss, _, _) => + validateVariance(sym) + traverseTrees(tparams) + traverseTreess(vparamss) + case Template(_, _, _) => + super.traverse(tree) + case CompoundTypeTree(templ) => + super.traverse(tree) + + // SI-7872 These two cases make sure we don't miss variance exploits + // in originals, e.g. in `foo[({type l[+a] = List[a]})#l]` + case tt @ TypeTree() if tt.original != null => + super.traverse(tt.original) + case tt : TypTree => + super.traverse(tt) + + case _ => + } + } + } + + /** Compute variance of type parameter `tparam` in all types `tps`. */ + def varianceInTypes(tps: List[Type])(tparam: Symbol): Variance = + fold(tps map (tp => varianceInType(tp)(tparam))) + + /** Compute variance of type parameter `tparam` in type `tp`. */ + def varianceInType(tp: Type)(tparam: Symbol): Variance = { + def inArgs(sym: Symbol, args: List[Type]): Variance = fold(map2(args, sym.typeParams)((a, p) => inType(a) * p.variance)) + def inSyms(syms: List[Symbol]): Variance = fold(syms map inSym) + def inTypes(tps: List[Type]): Variance = fold(tps map inType) + + def inSym(sym: Symbol): Variance = if (sym.isAliasType) inType(sym.info).cut else inType(sym.info) + def inType(tp: Type): Variance = tp match { + case ErrorType | WildcardType | NoType | NoPrefix => Bivariant + case ThisType(_) | ConstantType(_) => Bivariant + case TypeRef(_, `tparam`, _) => Covariant + case BoundedWildcardType(bounds) => inType(bounds) + case NullaryMethodType(restpe) => inType(restpe) + case SingleType(pre, sym) => inType(pre) + case TypeRef(pre, _, _) if tp.isHigherKinded => inType(pre) // a type constructor cannot occur in tp's args + case TypeRef(pre, sym, args) => inType(pre) & inArgs(sym, args) + case TypeBounds(lo, hi) => inType(lo).flip & inType(hi) + case RefinedType(parents, defs) => inTypes(parents) & inSyms(defs.toList) + case MethodType(params, restpe) => inSyms(params).flip & inType(restpe) + case PolyType(tparams, restpe) => inSyms(tparams).flip & inType(restpe) + case ExistentialType(tparams, restpe) => inSyms(tparams) & inType(restpe) + case AnnotatedType(annots, tp) => inTypes(annots map (_.atp)) & inType(tp) + } + + inType(tp) + } +} diff --git a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala deleted file mode 100644 index 058ff61fbf..0000000000 --- a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala +++ /dev/null @@ -1,31 +0,0 @@ -package scala.reflect -package internal -package annotations - -import scala.annotation.meta._ - -/** - * An annotation that designates a member should not be referred to after - * type checking (which includes macro expansion); it must only be used in - * the arguments of some other macro that will eliminate it from the AST. - * - * Later on, this annotation should be removed and implemented with domain-specific macros. - * If a certain method `inner` mustn't be called outside the context of a given macro `outer`, - * then it should itself be declared as a macro. - * - * Approach #1. Expansion of `inner` checks whether its enclosures contain `outer` and - * report an error if `outer` is not detected. In principle, we could use this approach right now, - * but currently enclosures are broken, because contexts aren't exactly famous for keeping precise - * track of the stack of the trees being typechecked. - * - * Approach #2. Default implementation of `inner` is just an invocation of `c.abort`. - * `outer` is an untyped macro, which expands into a block, which contains a redefinition of `inner` - * and a call to itself. The redefined `inner` could either be a stub like `Expr.splice` or carry out - * domain-specific logic. - * - * @param message the error message to print during compilation if a reference remains - * after type checking - * @since 2.10.1 - */ -@getter @setter @beanGetter @beanSetter -final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/internal/annotations/package.scala b/src/reflect/scala/reflect/internal/annotations/package.scala new file mode 100644 index 0000000000..ef299a600c --- /dev/null +++ b/src/reflect/scala/reflect/internal/annotations/package.scala @@ -0,0 +1,6 @@ +package scala.reflect.internal + +package object annotations { + @deprecated("Use scala.annotation.compileTimeOnly instead", "2.11.0") + type compileTimeOnly = scala.annotation.compileTimeOnly +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala b/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala index 367a3b8b19..8615e34fad 100644 --- a/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala +++ b/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala @@ -5,7 +5,8 @@ ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ -package scala.reflect.internal.pickling +package scala +package reflect.internal.pickling object ByteCodecs { @@ -127,7 +128,7 @@ object ByteCodecs { var j = 0 val dstlen = (srclen * 7 + 7) / 8 while (i + 7 < srclen) { - var out: Int = src(i) + var out: Int = src(i).toInt var in: Byte = src(i + 1) src(j) = (out | (in & 0x01) << 7).toByte out = in >>> 1 @@ -152,7 +153,7 @@ object ByteCodecs { j += 7 } if (i < srclen) { - var out: Int = src(i) + var out: Int = src(i).toInt if (i + 1 < srclen) { var in: Byte = src(i + 1) src(j) = (out | (in & 0x01) << 7).toByte; j += 1 @@ -195,10 +196,10 @@ object ByteCodecs { * * Sometimes returns (length+1) of the decoded array. Example: * - * scala> val enc = reflect.generic.ByteCodecs.encode(Array(1,2,3)) + * scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3)) * enc: Array[Byte] = Array(2, 5, 13, 1) * - * scala> reflect.generic.ByteCodecs.decode(enc) + * scala> scala.reflect.generic.ByteCodecs.decode(enc) * res43: Int = 4 * * scala> enc @@ -211,11 +212,3 @@ object ByteCodecs { decode7to8(xs, len) } } - - - - - - - - diff --git a/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala b/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala index 6170fcbb90..a814256f8e 100644 --- a/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala +++ b/src/reflect/scala/reflect/internal/pickling/PickleBuffer.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal package pickling @@ -62,11 +63,8 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { writeByte((x & 0x7f).toInt) } - /** Write a natural number <code>x</code> at position <code>pos</code>. + /** Write a natural number `x` at position `pos`. * 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) { @@ -81,7 +79,7 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { if (y != 0) patchNatPrefix(y) } - /** Write a long number <code>x</code> in signed big endian format, base 256. + /** Write a long number `x` in signed big endian format, base 256. * * @param x The long number to be written. */ @@ -94,12 +92,9 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { // -- 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 + val x = bytes(readIndex).toInt; readIndex += 1; x } /** Read a natural number in big endian format, base 128. @@ -110,9 +105,9 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { var b = 0L var x = 0L do { - b = readByte() + b = readByte().toLong x = (x << 7) + (b & 0x7f) - } while ((b & 0x80) != 0L); + } while ((b & 0x80) != 0L) x } @@ -151,18 +146,14 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { result.toIndexedSeq } - /** Perform operation <code>op</code> until the condition - * <code>readIndex == end</code> is satisfied. + /** Perform operation `op` until the condition + * `readIndex == end` 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); + if (readIndex == end) List() else op() :: until(end, op) - /** Perform operation <code>op</code> the number of + /** Perform operation `op` the number of * times specified. Concatenate the results into a list. */ def times[T](n: Int, op: ()=>T): List[T] = diff --git a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala index 16747af08a..ce0ceec688 100644 --- a/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala +++ b/src/reflect/scala/reflect/internal/pickling/PickleFormat.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package pickling @@ -56,7 +57,7 @@ object PickleFormat { * | 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 + * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat /* no longer needed */ * | 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} @@ -115,7 +116,6 @@ object PickleFormat { */ val MajorVersion = 5 val MinorVersion = 0 - def VersionString = "V" + MajorVersion + "." + MinorVersion final val TERMname = 1 final val TYPEname = 2 @@ -161,7 +161,7 @@ object PickleFormat { final val ANNOTARGARRAY = 44 final val SUPERtpe = 46 - final val DEBRUIJNINDEXtpe = 47 + final val DEBRUIJNINDEXtpe = 47 // no longer generated final val EXISTENTIALtpe = 48 final val TREE = 49 // prefix code that means a tree is coming diff --git a/src/reflect/scala/reflect/internal/pickling/Translations.scala b/src/reflect/scala/reflect/internal/pickling/Translations.scala new file mode 100644 index 0000000000..d924cb3a0c --- /dev/null +++ b/src/reflect/scala/reflect/internal/pickling/Translations.scala @@ -0,0 +1,129 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect +package internal +package pickling + +import PickleFormat._ +import util.shortClassOfInstance + +trait Translations { + self: SymbolTable => + + def isTreeSymbolPickled(code: Int): Boolean = (code: @annotation.switch) match { + case PACKAGEtree | CLASStree | MODULEtree | VALDEFtree | DEFDEFtree | TYPEDEFtree | LABELtree => true + case IMPORTtree | TEMPLATEtree | BINDtree | FUNCTIONtree | RETURNtree => true + case APPLYDYNAMICtree | SUPERtree | THIStree | SELECTtree | IDENTtree => true + case _ => false + } + /** This method should be equivalent to tree.hasSymbolField, but that method + * doesn't do us any good when we're unpickling because we need to know based + * on the Int tag - the tree doesn't exist yet. Thus, this method is documentation only. + */ + def isTreeSymbolPickled(tree: Tree): Boolean = isTreeSymbolPickled(picklerSubTag(tree)) + + // The ad hoc pattern matching of tuples out of AnyRefs is a + // truly terrible idea. It reaches the height of its powers in + // combination with scala's insistence on helpfully tupling + // multiple arguments passed to a single-arg AnyRef. + def picklerTag(ref: AnyRef): Int = ref match { + case tp: Type => picklerTag(tp) + case sym: Symbol => picklerTag(sym) + case const: Constant => LITERAL + const.tag + case _: Tree => TREE // its sub tag more precisely identifies it + case _: TermName => TERMname + case _: TypeName => TYPEname + case _: ArrayAnnotArg => ANNOTARGARRAY // an array of annotation arguments + case _: AnnotationInfo => ANNOTINFO // annotations on types (not linked to a symbol) + case (_: Symbol, _: AnnotationInfo) => SYMANNOT // symbol annotations, i.e. on terms + case (_: Symbol, _: List[_]) => CHILDREN // the direct subclasses of a sealed symbol + case _: Modifiers => MODIFIERS + case _ => sys.error(s"unpicklable entry ${shortClassOfInstance(ref)} $ref") + } + + /** Local symbols only. The assessment of locality depends + * on convoluted conditions which depends in part on the root + * symbol being pickled, so it cannot be reproduced here. + * The pickler tags at stake are EXTMODCLASSref and EXTref. + * Those tags are never produced here - such symbols must be + * excluded prior to calling this method. + */ + def picklerTag(sym: Symbol): Int = sym match { + case NoSymbol => NONEsym + case _: ClassSymbol => CLASSsym + case _: TypeSymbol if sym.isAbstractType => TYPEsym + case _: TypeSymbol => ALIASsym + case _: TermSymbol if sym.isModule => MODULEsym + case _: TermSymbol => VALsym + } + + def picklerTag(tpe: Type): Int = tpe match { + case NoType => NOtpe + case NoPrefix => NOPREFIXtpe + case _: ThisType => THIStpe + case _: SingleType => SINGLEtpe + case _: SuperType => SUPERtpe + case _: ConstantType => CONSTANTtpe + case _: TypeBounds => TYPEBOUNDStpe + case _: TypeRef => TYPEREFtpe + case _: RefinedType => REFINEDtpe + case _: ClassInfoType => CLASSINFOtpe + case _: MethodType => METHODtpe + case _: PolyType => POLYtpe + case _: NullaryMethodType => POLYtpe // bad juju, distinct ints are not at a premium! + case _: ExistentialType => EXISTENTIALtpe + case StaticallyAnnotatedType(_, _) => ANNOTATEDtpe + case _: AnnotatedType => picklerTag(tpe.underlying) + } + + def picklerSubTag(tree: Tree): Int = tree match { + case EmptyTree => EMPTYtree + case _: PackageDef => PACKAGEtree + case _: ClassDef => CLASStree + case _: ModuleDef => MODULEtree + case _: ValDef => VALDEFtree + case _: DefDef => DEFDEFtree + case _: TypeDef => TYPEDEFtree + case _: LabelDef => LABELtree + case _: Import => IMPORTtree + // case _: DocDef => DOCDEFtree + case _: Template => TEMPLATEtree + case _: Block => BLOCKtree + case _: CaseDef => CASEtree + case _: Alternative => ALTERNATIVEtree + case _: Star => STARtree + case _: Bind => BINDtree + case _: UnApply => UNAPPLYtree + case _: ArrayValue => ARRAYVALUEtree + case _: Function => FUNCTIONtree + case _: Assign => ASSIGNtree + case _: If => IFtree + case _: Match => MATCHtree + case _: Return => RETURNtree + case _: Try => TREtree // TREtree? + case _: Throw => THROWtree + case _: New => NEWtree + case _: Typed => TYPEDtree + case _: TypeApply => TYPEAPPLYtree + case _: Apply => APPLYtree + case _: ApplyDynamic => APPLYDYNAMICtree + case _: Super => SUPERtree + case _: This => THIStree + case _: Select => SELECTtree + case _: Ident => IDENTtree + case _: Literal => LITERALtree + case _: TypeTree => TYPEtree + case _: Annotated => ANNOTATEDtree + case _: SingletonTypeTree => SINGLETONTYPEtree + case _: SelectFromTypeTree => SELECTFROMTYPEtree + case _: CompoundTypeTree => COMPOUNDTYPEtree + case _: AppliedTypeTree => APPLIEDTYPEtree + case _: TypeBoundsTree => TYPEBOUNDStree + case _: ExistentialTypeTree => EXISTENTIALTYPEtree + } +} + diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 603fff4f1c..64a1a44722 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal package pickling @@ -21,15 +22,15 @@ import scala.annotation.switch * @version 1.0 */ abstract class UnPickler { - val global: SymbolTable - import global._ + val symbolTable: SymbolTable + import symbolTable._ /** 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 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) { @@ -64,36 +65,38 @@ abstract class UnPickler { /** A map from symbols to their associated `decls` scopes */ private val symScopes = mutable.HashMap[Symbol, Scope]() + private def expect(expected: Int, msg: => String) { + val tag = readByte() + if (tag != expected) + errorBadSignature(s"$msg ($tag)") + } + //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + @inline private def runAtIndex[T](i: Int)(body: => T): T = { + val saved = readIndex + readIndex = index(i) + try body finally readIndex = saved + } + // 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 - } + if (entries(i) == null && isSymbolEntry(i)) + runAtIndex(i)(entries(i) = readSymbol()) + 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 - } + if (isSymbolAnnotationEntry(i)) + runAtIndex(i)(readSymbolAnnotation()) + else if (isChildrenEntry(i)) + runAtIndex(i)(readChildren()) } i += 1 } @@ -144,6 +147,11 @@ abstract class UnPickler { tag == CHILDREN } + private def maybeReadSymbol(): Either[Int, Symbol] = readNat() match { + case index if isSymbolRef(index) => Right(at(index, readSymbol)) + case index => Left(index) + } + /** Does entry represent a refinement symbol? * pre: Entry is a class symbol */ @@ -159,9 +167,9 @@ abstract class UnPickler { 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. + /** If entry at `i` is undefined, define it by performing + * operation `op` with `readIndex at start of i'th + * entry. Restore `readIndex` afterwards. */ protected def at[T <: AnyRef](i: Int, op: () => T): T = { var r = entries(i) @@ -186,13 +194,12 @@ abstract class UnPickler { case _ => errorBadSignature("bad name tag: " + tag) } } - protected def readTermName(): TermName = readName().toTermName - protected def readTypeName(): TypeName = readName().toTypeName + private def readEnd() = readNat() + readIndex /** Read a symbol */ protected def readSymbol(): Symbol = { val tag = readByte() - val end = readNat() + readIndex + val end = readEnd() def atEnd = readIndex == end def readExtSymbol(): Symbol = { @@ -222,6 +229,20 @@ abstract class UnPickler { NoSymbol } + def moduleAdvice(missing: String): String = { + val module = + if (missing.startsWith("scala.xml")) Some(("org.scala-lang.modules", "scala-xml")) + else if (missing.startsWith("scala.util.parsing")) Some(("org.scala-lang.modules", "scala-parser-combinators")) + else if (missing.startsWith("scala.swing")) Some(("org.scala-lang.modules", "scala-swing")) + else if (missing.startsWith("scala.util.continuations")) Some(("org.scala-lang.plugins", "scala-continuations-library")) + else None + + (module map { case (group, art) => + s"""\n(NOTE: It looks like the $art module is missing; try adding a dependency on "$group" : "$art". + | See http://docs.scala-lang.org/overviews/core/scala-2.11.html for more information.)""".stripMargin + } getOrElse "") + } + // (1) Try name. fromName(name) orElse { // (2) Try with expanded name. Can happen if references to private @@ -233,11 +254,12 @@ abstract class UnPickler { // (4) Call the mirror's "missing" hook. adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { // (5) Create a stub symbol to defer hard failure a little longer. + val fullName = s"${owner.fullName}.$name" val missingMessage = - s"""|bad symbolic reference. A signature in $filename refers to ${name.longString} - |in ${owner.kindString} ${owner.fullName} which is not available. - |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $filename.""".stripMargin + s"""|bad symbolic reference to $fullName encountered in class file '$filename'. + |Cannot access ${name.longString} in ${owner.kindString} ${owner.fullName}. The current classpath may be + |missing a definition for $fullName, or $filename may have been compiled against a version that's + |incompatible with the one found on the current classpath.${moduleAdvice(fullName)}""".stripMargin owner.newStubSymbol(name, missingMessage) } } @@ -256,14 +278,11 @@ abstract class UnPickler { 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 - } + + val (privateWithin, inforef) = maybeReadSymbol() match { + case Left(index) => NoSymbol -> index + case Right(sym) => sym -> readNat() + } def isModuleFlag = (flags & MODULE) != 0L def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) @@ -271,6 +290,7 @@ abstract class UnPickler { def pflags = flags & PickledFlags def finishSym(sym: Symbol): Symbol = { + markFlagsCompleted(sym)(mask = AllFlags) sym.privateWithin = privateWithin sym.info = ( if (atEnd) { @@ -305,7 +325,7 @@ abstract class UnPickler { sym case MODULEsym => - val clazz = at(inforef, () => readType()).typeSymbol // after the NMT_TRANSITION period, we can leave off the () => ... () + val clazz = at(inforef, () => readType()).typeSymbol // after NMT_TRANSITION, we can leave off the () => ... () if (isModuleRoot) moduleRoot setFlag pflags else owner.newLinkedModule(clazz, pflags) case VALsym => @@ -317,84 +337,48 @@ abstract class UnPickler { }) } - /** 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 + val end = readEnd() + @inline def all[T](body: => T): List[T] = until(end, () => body) + + def readTypes() = all(readTypeRef) + def readSymbols() = all(readSymbolRef) + def readAnnots() = all(readAnnotationRef) + + // 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. + def MethodTypeRef(restpe: Type, params: List[Symbol]): Type = ( + if (restpe == NoType || (params contains NoSymbol)) NoType + else MethodType(params, restpe) + ) + def PolyOrNullaryType(restpe: Type, tparams: List[Symbol]): Type = tparams match { + case Nil => NullaryMethodType(restpe) + case _ => PolyType(tparams, restpe) + } + def CompoundType(clazz: Symbol, parents: List[Type]): Type = tag match { + case REFINEDtpe => RefinedType(parents, symScope(clazz), clazz) + case CLASSINFOtpe => ClassInfoType(parents, symScope(clazz), clazz) + } + + // We're stuck with the order types are pickled in, but with judicious use + // of named parameters we can recapture a declarative flavor in a few cases. + // But it's still a rat's nest of adhockery. (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() - newExistentialType(until(end, readSymbolRef), restpe) - - case ANNOTATEDtpe => - var typeRef = readNat() - val selfsym = if (isSymbolRef(typeRef)) { - val s = at(typeRef, readSymbol) - typeRef = readNat() - s - } else NoSymbol // selfsym can go. - val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION - val annots = until(end, readAnnotationRef) - if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) - else tp - case _ => - noSuchTypeTag(tag, end) + case NOtpe => NoType + case NOPREFIXtpe => NoPrefix + case THIStpe => ThisType(readSymbolRef()) + case SINGLEtpe => SingleType(readTypeRef(), readSymbolRef()) + case SUPERtpe => SuperType(readTypeRef(), readTypeRef()) + case CONSTANTtpe => ConstantType(readConstantRef()) + case TYPEREFtpe => TypeRef(readTypeRef(), readSymbolRef(), readTypes()) + case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) + case REFINEDtpe | CLASSINFOtpe => CompoundType(readSymbolRef(), readTypes()) + case METHODtpe => MethodTypeRef(readTypeRef(), readSymbols()) + case POLYtpe => PolyOrNullaryType(readTypeRef(), readSymbols()) + case EXISTENTIALtpe => ExistentialType(underlying = readTypeRef(), quantified = readSymbols()) + case ANNOTATEDtpe => AnnotatedType(underlying = readTypeRef(), annotations = readAnnots()) } } @@ -431,7 +415,7 @@ abstract class UnPickler { protected def readChildren() { val tag = readByte() assert(tag == CHILDREN) - val end = readNat() + readIndex + val end = readEnd() val target = readSymbolRef() while (readIndex != end) target addChild readSymbolRef() } @@ -450,7 +434,7 @@ abstract class UnPickler { */ private def readArrayAnnot() = { readByte() // skip the `annotargarray` tag - val end = readNat() + readIndex + val end = readEnd() until(end, () => readClassfileAnnotArg(readNat())).toArray(JavaArgumentTag) } protected def readClassfileAnnotArg(i: Int): ClassfileAnnotArg = bytes(index(i)) match { @@ -483,10 +467,8 @@ abstract class UnPickler { * 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 + expect(SYMANNOT, "symbol annotation expected") + val end = readEnd() val target = readSymbolRef() target.addAnnotation(readAnnotationInfo(end)) } @@ -497,264 +479,105 @@ abstract class UnPickler { val tag = readByte() if (tag != ANNOTINFO) errorBadSignature("annotation expected (" + tag + ")") - val end = readNat() + readIndex + val end = readEnd() 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() + private def readNonEmptyTree(tag: Int, end: Int): Tree = { + @inline def all[T](body: => T): List[T] = until(end, () => body) + @inline def rep[T](body: => T): List[T] = times(readNat(), () => body) + + // !!! What is this doing here? + def fixApply(tree: Apply, tpe: Type): Apply = { + val Apply(fun, args) = tree + if (fun.symbol.isOverloaded) { + fun setType fun.symbol.info + inferMethodAlternative(fun, args map (_.tpe), tpe) + } + tree } - /** Read a Symbol and a Name */ - def setSymName() { - symbol = readSymbolRef() - name = readNameRef() + def ref() = readTreeRef() + def caseRef() = readCaseDefRef() + def modsRef() = readModifiersRef() + def implRef() = readTemplateRef() + def nameRef() = readNameRef() + def tparamRef() = readTypeDefRef() + def vparamRef() = readValDefRef() + def memberRef() = readMemberDefRef() + def constRef() = readConstantRef() + def idRef() = readIdentRef() + def termNameRef() = readNameRef().toTermName + def typeNameRef() = readNameRef().toTypeName + def refTreeRef() = ref() match { + case t: RefTree => t + case t => errorBadSignature("RefTree expected, found " + t.shortClass) } - /** Read a Symbol */ - def setSym() { - symbol = readSymbolRef() + def selectorsRef() = all(ImportSelector(nameRef(), -1, nameRef(), -1)) + + /** A few of the most popular trees have been pulled to the top for + * switch efficiency purposes. + */ + def readTree(tpe: Type): Tree = (tag: @switch) match { + case IDENTtree => Ident(nameRef) + case SELECTtree => Select(ref, nameRef) + case APPLYtree => fixApply(Apply(ref, all(ref)), tpe) // !!! + case BINDtree => Bind(nameRef, ref) + case BLOCKtree => all(ref) match { case stats :+ expr => Block(stats, expr) } + case IFtree => If(ref, ref, ref) + case LITERALtree => Literal(constRef) + case TYPEAPPLYtree => TypeApply(ref, all(ref)) + case TYPEDtree => Typed(ref, ref) + case ALTERNATIVEtree => Alternative(all(ref)) + case ANNOTATEDtree => Annotated(ref, ref) + case APPLIEDTYPEtree => AppliedTypeTree(ref, all(ref)) + case APPLYDYNAMICtree => ApplyDynamic(ref, all(ref)) + case ARRAYVALUEtree => ArrayValue(ref, all(ref)) + case ASSIGNtree => Assign(ref, ref) + case CASEtree => CaseDef(ref, ref, ref) + case CLASStree => ClassDef(modsRef, typeNameRef, rep(tparamRef), implRef) + case COMPOUNDTYPEtree => CompoundTypeTree(implRef) + case DEFDEFtree => DefDef(modsRef, termNameRef, rep(tparamRef), rep(rep(vparamRef)), ref, ref) + case EXISTENTIALTYPEtree => ExistentialTypeTree(ref, all(memberRef)) + case FUNCTIONtree => Function(rep(vparamRef), ref) + case IMPORTtree => Import(ref, selectorsRef) + case LABELtree => LabelDef(termNameRef, rep(idRef), ref) + case MATCHtree => Match(ref, all(caseRef)) + case MODULEtree => ModuleDef(modsRef, termNameRef, implRef) + case NEWtree => New(ref) + case PACKAGEtree => PackageDef(refTreeRef, all(ref)) + case RETURNtree => Return(ref) + case SELECTFROMTYPEtree => SelectFromTypeTree(ref, typeNameRef) + case SINGLETONTYPEtree => SingletonTypeTree(ref) + case STARtree => Star(ref) + case SUPERtree => Super(ref, typeNameRef) + case TEMPLATEtree => Template(rep(ref), vparamRef, all(ref)) + case THIStree => This(typeNameRef) + case THROWtree => Throw(ref) + case TREtree => Try(ref, rep(caseRef), ref) + case TYPEBOUNDStree => TypeBoundsTree(ref, ref) + case TYPEDEFtree => TypeDef(modsRef, typeNameRef, rep(tparamRef), ref) + case TYPEtree => TypeTree() + case UNAPPLYtree => UnApply(ref, all(ref)) + case VALDEFtree => ValDef(modsRef, termNameRef, ref, ref) + case _ => noSuchTreeTag(tag, end) } - 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) + val tpe = readTypeRef() + val sym = if (isTreeSymbolPickled(tag)) readSymbolRef() else null + val result = readTree(tpe) - case _ => - noSuchTreeTag(tag, end) - } + if (sym ne null) result setSymbol sym + result setType tpe + } - if (symbol == null) t setType tpe - else t setSymbol symbol setType tpe + /* Read an abstract syntax tree */ + protected def readTree(): Tree = { + expect(TREE, "tree expected") + val end = readEnd() + readByte() match { + case EMPTYtree => EmptyTree + case tag => readNonEmptyTree(tag, end) + } } def noSuchTreeTag(tag: Int, end: Int) = @@ -764,7 +587,8 @@ abstract class UnPickler { val tag = readNat() if (tag != MODIFIERS) errorBadSignature("expected a modifiers tag (" + tag + ")") - val end = readNat() + readIndex + + readEnd() val pflagsHi = readNat() val pflagsLo = readNat() val pflags = (pflagsHi.toLong << 32) + pflagsLo @@ -796,7 +620,6 @@ abstract class UnPickler { 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 { @@ -828,6 +651,12 @@ abstract class UnPickler { case other => errorBadSignature("expected an TypeDef (" + other + ")") } + protected def readMemberDefRef(): MemberDef = + readTreeRef() match { + case tree:MemberDef => tree + case other => + errorBadSignature("expected an MemberDef (" + other + ")") + } protected def errorBadSignature(msg: String) = throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg) @@ -843,7 +672,6 @@ abstract class UnPickler { * error reporting, so we rely on the typechecker to report the error). */ def toTypeError(e: MissingRequirementError) = { - // e.printStackTrace() new TypeError(e.msg) } @@ -851,15 +679,20 @@ abstract class UnPickler { private class LazyTypeRef(i: Int) extends LazyType with FlagAgnosticCompleter { private val definedAtRunId = currentRunId private val p = phase - override def complete(sym: Symbol) : Unit = try { + protected def completeInternal(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 (p ne null) + slowButSafeEnteringPhase(p) (sym setInfo tp) if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) } catch { case e: MissingRequirementError => throw toTypeError(e) } + override def complete(sym: Symbol) : Unit = { + completeInternal(sym) + if (!isCompilerUniverse) markAllCompleted(sym) + } override def load(sym: Symbol) { complete(sym) } } @@ -867,11 +700,12 @@ abstract class UnPickler { * 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) + override def completeInternal(sym: Symbol) = try { + super.completeInternal(sym) + var alias = at(j, readSymbol) if (alias.isOverloaded) - alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + alias = slowButSafeEnteringPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) sym.asInstanceOf[TermSymbol].setAlias(alias) } diff --git a/src/reflect/scala/reflect/internal/settings/AbsSettings.scala b/src/reflect/scala/reflect/internal/settings/AbsSettings.scala index a6fb4187ca..859f703d97 100644 --- a/src/reflect/scala/reflect/internal/settings/AbsSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/AbsSettings.scala @@ -3,7 +3,8 @@ * @author Paul Phillips */ -package scala.reflect.internal +package scala +package reflect.internal package settings /** A Settings abstraction boiled out of the original highly mutable Settings diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 81368df7a6..a494c7f0d0 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -4,7 +4,8 @@ */ // $Id$ -package scala.reflect.internal +package scala +package reflect.internal package settings /** A mutable Settings object. @@ -32,19 +33,32 @@ abstract class MutableSettings extends AbsSettings { } } - def overrideObjects: BooleanSetting - def printtypes: BooleanSetting + def Xexperimental: BooleanSetting + def XfullLubs: BooleanSetting + def XnoPatmatAnalysis: BooleanSetting + def Xprintpos: BooleanSetting + def strictInference: BooleanSetting + def Yposdebug: BooleanSetting + def Yrangepos: BooleanSetting + def Yshowsymowners: BooleanSetting + def Yshowsymkinds: BooleanSetting + def breakCycles: BooleanSetting def debug: BooleanSetting - def Ynotnull: BooleanSetting + def developer: BooleanSetting def explaintypes: BooleanSetting - def verbose: BooleanSetting + def overrideObjects: BooleanSetting + def printtypes: BooleanSetting def uniqid: BooleanSetting - def Yshowsymkinds: BooleanSetting - def Xprintpos: BooleanSetting + def verbose: BooleanSetting + def Yrecursion: IntSetting def maxClassfileName: IntSetting - def Xexperimental: BooleanSetting - def XoldPatmat: BooleanSetting - def XnoPatmatAnalysis: BooleanSetting - def XfullLubs: BooleanSetting + + def isScala211: Boolean +} + +object MutableSettings { + import scala.language.implicitConversions + /** Support the common use case, `if (settings.debug) println("Hello, martin.")` */ + @inline implicit def reflectSettingToBoolean(s: MutableSettings#BooleanSetting): Boolean = s.value } diff --git a/src/reflect/scala/reflect/internal/tpe/CommonOwners.scala b/src/reflect/scala/reflect/internal/tpe/CommonOwners.scala new file mode 100644 index 0000000000..f879960407 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/CommonOwners.scala @@ -0,0 +1,51 @@ +package scala +package reflect +package internal +package tpe + +private[internal] trait CommonOwners { + self: SymbolTable => + + /** The most deeply nested owner that contains all the symbols + * of thistype or prefixless typerefs/singletype occurrences in given type. + */ + protected[internal] 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. + */ + protected[internal] 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 +} diff --git a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala new file mode 100644 index 0000000000..de54f3768e --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala @@ -0,0 +1,288 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL + * @author Jason Zaugg + */ +package scala.reflect.internal +package tpe + +import Flags._ +import util.Statistics +import TypesStats._ + +trait FindMembers { + this: SymbolTable => + + /** Implementatation of `Type#{findMember, findMembers}` */ + private[internal] abstract class FindMemberBase[T](tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long) { + protected val initBaseClasses: List[Symbol] = tpe.baseClasses + + // The first base class, or the symbol of the ThisType + // e.g in: + // trait T { self: C => } + // + // The selector class of `T.this.type` is `T`, and *not* the first base class, `C`. + private[this] var _selectorClass: Symbol = null + private def selectorClass: Symbol = { + if (_selectorClass eq null) { + _selectorClass = tpe match { + case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class. + case _ => initBaseClasses.head + } + } + _selectorClass + } + + // Cache for the narrowed type of `tp` (in `tp.findMember`). + // This is needed to avoid mismatched existential types are reported in SI-5330. + private[this] var _self: Type = null + protected def self: Type = { + // TODO: use narrow only for modules? (correct? efficiency gain?) (<-- Note: this comment predates SI-5330) + if (_self eq null) _self = narrowForFindMember(tpe) + _self + } + + // Main entry point + def apply(): T = { + if (Statistics.canEnable) Statistics.incCounter(findMemberCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null + try searchConcreteThenDeferred + finally if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + } + + protected def result: T + + // SLS 5.1.3 First, a concrete definition always overrides an abstract definition + private def searchConcreteThenDeferred: T = { + val deferredSeen = walkBaseClasses(requiredFlags, excludedFlags | DEFERRED) + if (deferredSeen) // OPT: the `if` avoids a second pass if the first pass didn't spot any candidates. + walkBaseClasses(requiredFlags | DEFERRED, excludedFlags & ~(DEFERRED.toLong)) + result + } + + /* + * Walk up through the decls of each base class. + * + * Called in two passes: first excluding deferred, then mandating it. + * + * @return if a potential deferred member was seen on the first pass that calls for a second pass, + and `excluded & DEFERRED != 0L` + */ + private def walkBaseClasses(required: Long, excluded: Long): Boolean = { + var bcs = initBaseClasses + + // Have we seen a candidate deferred member? + var deferredSeen = false + + // All direct parents of refinement classes in the base class sequence + // from the current `walkBaseClasses` + var refinementParents: List[Symbol] = Nil + + // Has the current `walkBaseClasses` encountered a non-refinement class? + var seenFirstNonRefinementClass = false + + val findAll = name == nme.ANYname + + while (!bcs.isEmpty) { + val currentBaseClass = bcs.head + val decls = currentBaseClass.info.decls + var entry = if (findAll) decls.elems else decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + val meetsRequirements = (flags & required) == required + if (meetsRequirements) { + val excl: Long = flags & excluded + val isExcluded: Boolean = excl != 0L + if (!isExcluded && isPotentialMember(sym, flags, currentBaseClass, seenFirstNonRefinementClass, refinementParents)) { + if (shortCircuit(sym)) return false + else addMemberIfNew(sym) + } else if (excl == DEFERRED) { + deferredSeen = true + } + } + entry = if (findAll) entry.next else decls lookupNextEntry entry + } + + // SLS 5.2 The private modifier can be used with any definition or declaration in a template. + // They are not inherited by subclasses [...] + if (currentBaseClass.isRefinementClass) + // SLS 3.2.7 A compound type T1 with . . . with Tn {R } represents objects with members as given in + // the component types T1, ..., Tn and the refinement {R } + // + // => private members should be included from T1, ... Tn. (SI-7475) + refinementParents :::= currentBaseClass.parentSymbols + else if (currentBaseClass.isClass) + seenFirstNonRefinementClass = true // only inherit privates of refinement parents after this point + + bcs = bcs.tail + } + deferredSeen + } + + /* Should this symbol be returned immediately as the sole result? */ + protected def shortCircuit(sym: Symbol): Boolean + + /* Add this member to the final result, unless an already-found member matches it. */ + protected def addMemberIfNew(sym: Symbol): Unit + + // Is `sym` a potentially member of `baseClass`? + // + // Q. When does a potential member fail to be a an actual member? + // A. if it is subsumed by an member in a subclass. + private def isPotentialMember(sym: Symbol, flags: Long, owner: Symbol, + seenFirstNonRefinementClass: Boolean, refinementParents: List[Symbol]): Boolean = { + // conservatively (performance wise) doing this with flags masks rather than `sym.isPrivate` + // to avoid multiple calls to `Symbol#flags`. + val isPrivate = (flags & PRIVATE) == PRIVATE + val isPrivateLocal = (flags & PrivateLocal) == PrivateLocal + + // TODO Is the special handling of `private[this]` vs `private` backed up by the spec? + def admitPrivate(sym: Symbol): Boolean = + (selectorClass == owner) || ( + !isPrivateLocal // private[this] only a member from within the selector class. (Optimization only? Does the spec back this up?) + && ( + !seenFirstNonRefinementClass + || refinementParents.contains(owner) + ) + ) + + (!isPrivate || admitPrivate(sym)) && (sym.name != nme.CONSTRUCTOR || owner == initBaseClasses.head) + } + + // True unless the already-found member of type `memberType` matches the candidate symbol `other`. + protected def isNewMember(member: Symbol, other: Symbol): Boolean = + ( (other ne member) + && ( (member.owner eq other.owner) // same owner, therefore overload + || (member.flags & PRIVATE) != 0 // (unqualified) private members never participate in overriding + || (other.flags & PRIVATE) != 0 // ... as overrider or overridee. + || !(memberTypeLow(member) matches memberTypeHi(other)) // do the member types match? If so, its an override. Otherwise it's an overload. + ) + ) + + // Cache for the member type of a candidate member when comparing against multiple, already-found existing members + // + // TODO this cache is probably unnecessary, `tp.memberType(sym: MethodSymbol)` is already cached internally. + private[this] var _memberTypeHiCache: Type = null + private[this] var _memberTypeHiCacheSym: Symbol = null + + protected def memberTypeHi(sym: Symbol): Type = { + if (_memberTypeHiCacheSym ne sym) { + _memberTypeHiCache = self.memberType(sym) + _memberTypeHiCacheSym = sym + } + _memberTypeHiCache + } + + // member type of the LHS of `matches` call. This is an extension point to enable a cache in + // FindMember. + protected def memberTypeLow(sym: Symbol): Type = self.memberType(sym) + + /** Same as a call to narrow unless existentials are visible + * after widening the type. In that case, narrow from the widened + * type instead of the proxy. This gives buried existentials a + * chance to make peace with the other types. See SI-5330. + */ + private def narrowForFindMember(tp: Type): Type = { + val w = tp.widen + // Only narrow on widened type when we have to -- narrow is expensive unless the target is a singleton type. + if ((tp ne w) && containsExistential(w)) w.narrow + else tp.narrow + } + } + + private[reflect] final class FindMembers(tpe: Type, excludedFlags: Long, requiredFlags: Long) + extends FindMemberBase[Scope](tpe, nme.ANYname, excludedFlags, requiredFlags) { + private[this] var _membersScope: Scope = null + private def membersScope: Scope = { + if (_membersScope eq null) _membersScope = newFindMemberScope + _membersScope + } + + protected def shortCircuit(sym: Symbol): Boolean = false + protected def result: Scope = membersScope + + protected def addMemberIfNew(sym: Symbol): Unit = { + val members = membersScope + var others = members.lookupEntry(sym.name) + var isNew = true + while ((others ne null) && isNew) { + val member = others.sym + if (!isNewMember(member, sym)) + isNew = false + others = members lookupNextEntry others // next existing member with the same name. + } + if (isNew) members.enter(sym) + } + } + + private[reflect] final class FindMember(tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean) + extends FindMemberBase[Symbol](tpe, name, excludedFlags, requiredFlags) { + // Gathering the results into a hand rolled ListBuffer + // TODO Try just using a ListBuffer to see if this low-level-ness is worth it. + private[this] var member0: Symbol = NoSymbol + private[this] var members: List[Symbol] = null + private[this] var lastM: ::[Symbol] = null + + private def clearAndAddResult(sym: Symbol): Unit = { + member0 = sym + members = null + lastM = null + } + + protected def shortCircuit(sym: Symbol): Boolean = (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) && { + clearAndAddResult(sym) + true + } + + protected def addMemberIfNew(sym: Symbol): Unit = + if (member0 eq NoSymbol) { + member0 = sym // The first found member + } else if (members eq null) { + // We've found exactly one member so far... + if (isNewMember(member0, sym)) { + // ... make that two. + lastM = new ::(sym, null) + members = member0 :: lastM + } + } else { + // Already found 2 or more members + var ms: List[Symbol] = members + + var isNew = true + while ((ms ne null) && isNew) { + val member = ms.head + if (!isNewMember(member, sym)) + isNew = false + ms = ms.tail + } + if (isNew) { + val lastM1 = new ::(sym, null) + lastM.tl = lastM1 + lastM = lastM1 + } + } + + // Cache for the member type of the first member we find. + private[this] var _member0Tpe: Type = null + private[this] def member0Tpe: Type = { + assert(member0 != null) + if (_member0Tpe eq null) _member0Tpe = self.memberType(member0) + _member0Tpe + } + + override protected def memberTypeLow(sym: Symbol): Type = + if (sym eq member0) member0Tpe else super.memberTypeLow(sym) + + // Assemble the result from the hand-rolled ListBuffer + protected def result: Symbol = if (members eq null) { + if (member0 == NoSymbol) { + if (Statistics.canEnable) Statistics.incCounter(noMemberCount) + NoSymbol + } else member0 + } else { + if (Statistics.canEnable) Statistics.incCounter(multMemberCount) + lastM.tl = Nil + initBaseClasses.head.newOverloaded(tpe, members) + } + } +} diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala new file mode 100644 index 0000000000..876685e24a --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -0,0 +1,611 @@ +package scala +package reflect +package internal +package tpe + +import scala.collection.mutable +import scala.annotation.tailrec +import util.Statistics +import Variance._ + +private[internal] trait GlbLubs { + self: SymbolTable => + import definitions._ + import TypesStats._ + + private final val printLubs = scala.sys.props contains "scalac.debug.lub" + private final val strictInference = settings.strictInference + + /** In case anyone wants to turn off lub verification without reverting anything. */ + private final val verifyLubs = true + + private def printLubMatrix(btsMap: Map[Type, List[Type]], depth: Depth) { + 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)), left = 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 + } + } + + // only called when strictInference + private def willViolateRecursiveBounds(tp: Type, ts: List[Type], tsElimSub: List[Type]) = { + val typeSym = ts.head.typeSymbol // we're uniform, the `.head` is as good as any. + def fbounds = findRecursiveBounds(ts) map (_._2) + def isRecursive = typeSym.typeParams exists fbounds.contains + + isRecursive && (transposeSafe(tsElimSub map (_.normalize.typeArgs)) match { + case Some(arggsTransposed) => + val mergedTypeArgs = (tp match { case et: ExistentialType => et.underlying; case _ => tp}).typeArgs + exists3(typeSym.typeParams, mergedTypeArgs, arggsTransposed) { + (param, arg, lubbedArgs) => + val isExistential = arg.typeSymbol.isExistentiallyBound + val isInFBound = fbounds contains param + val wasLubbed = !lubbedArgs.exists(_ =:= arg) + (!isExistential && isInFBound && wasLubbed) + } + case None => false + }) + } + + /** 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. + */ + def lubList(ts: List[Type], depth: Depth): List[Type] = { + var lubListDepth = Depth.Zero + // This catches some recursive situations which would otherwise + // befuddle us, e.g. pos/hklub0.scala + def isHotForTs(xs: List[Type]) = ts exists (_.typeParams == xs.map(_.typeSymbol)) + + def elimHigherOrderTypeParam(tp: Type) = tp match { + case TypeRef(_, _, args) if args.nonEmpty && isHotForTs(args) => + logResult("Retracting dummies from " + tp + " in lublist")(tp.typeConstructor) + case _ => tp + } + // pretypes is a tail-recursion-preserving accumulator. + @tailrec + def loop(pretypes: List[Type], tsBts: List[List[Type]]): List[Type] = { + lubListDepth = lubListDepth.incr + + if (tsBts.isEmpty || (tsBts exists typeListIsEmpty)) pretypes.reverse + else if (tsBts.tail.isEmpty) pretypes.reverse ++ 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 regarding 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) { + val tails = tsBts map (_.tail) + val ts1 = elimSub(ts0, depth) map elimHigherOrderTypeParam + mergePrefixAndArgs(ts1, Covariant, depth) match { + case NoType => loop(pretypes, tails) + case tp if strictInference && willViolateRecursiveBounds(tp, ts0, ts1) => + log(s"Breaking recursion in lublist, advancing frontier and discaring merged prefix/args from $tp") + loop(pretypes, tails) + case tp => + loop(tp :: pretypes, 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(pretypes, newtps) + } + } + } + + val initialBTSes = ts map (_.baseTypeSeq.toList) + if (printLubs) + printLubMatrix((ts zip initialBTSes).toMap, depth) + + loop(Nil, initialBTSes) + } + + /** The minimal symbol of a list of types (as determined by `Symbol.isLess`). */ + 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 List(t) => List(t) + case t :: ts1 => + val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) + if (rest exists (t1 => t1 <:< t)) rest else t :: rest + } + + /** 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: Depth): List[Type] = { + def elimSub0(ts: List[Type]): List[Type] = ts match { + case List() => List() + case List(t) => List(t) + case t :: ts1 => + val rest = elimSub0(ts1 filter (t1 => !isSubType(t1, t, depth.decr))) + if (rest exists (t1 => isSubType(t, t1, depth.decr))) rest else t :: rest + } + val ts0 = elimSub0(ts) + if (ts0.isEmpty || ts0.tail.isEmpty) ts0 + else { + val ts1 = ts0 mapConserve (t => elimAnonymousClass(t.dealiasWiden)) + 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): Type = tp match { + case ExistentialType(_, res) => + res + case tv@TypeVar(_, constr) => + if (tv.instValid) stripType(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) + } + + /** Does this set of types have the same weak lub as + * it does regular lub? This is exposed so lub callers + * can discover whether the trees they are typing will + * may require further adaptation. It may return false + * negatives, but it will not return false positives. + */ + def sameWeakLubAsLub(tps: List[Type]) = tps match { + case Nil => true + case tp :: Nil => !typeHasAnnotations(tp) + case tps => !(tps exists typeHasAnnotations) && !(tps forall isNumericValueType) + } + + /** If the arguments are all numeric value types, the numeric + * lub according to the weak conformance spec. If any argument + * has type annotations, take the lub of the unannotated type + * and call the analyzerPlugin method annotationsLub so it can + * be further altered. Otherwise, the regular lub. + */ + def weakLub(tps: List[Type]): Type = ( + if (tps.isEmpty) + NothingTpe + else if (tps forall isNumericValueType) + numericLub(tps) + else if (tps exists typeHasAnnotations) + annotationsLub(lub(tps map (_.withoutAnnotations)), tps) + else + lub(tps) + ) + + def numericLub(ts: List[Type]) = + ts reduceLeft ((t1, t2) => + if (isNumericSubType(t1, t2)) t2 + else if (isNumericSubType(t2, t1)) t1 + else IntTpe) + + private val _lubResults = new mutable.HashMap[(Depth, List[Type]), Type] + def lubResults = _lubResults + + private val _glbResults = new mutable.HashMap[(Depth, List[Type]), Type] + def glbResults = _glbResults + + def lub(ts: List[Type]): Type = ts match { + case Nil => NothingTpe + case t :: Nil => t + case _ => + if (Statistics.canEnable) Statistics.incCounter(lubCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null + try { + val res = lub(ts, lubDepth(ts)) + // If the number of unapplied type parameters in all incoming + // types is consistent, and the lub does not match that, return + // the type constructor of the calculated lub instead. This + // is because lubbing type constructors tends to result in types + // which have been applied to dummies or Nothing. + ts.map(_.typeParams.size).distinct match { + case x :: Nil if res.typeParams.size != x => + logResult(s"Stripping type args from lub because $res is not consistent with $ts")(res.typeConstructor) + case _ => + res + } + } + finally { + lubResults.clear() + glbResults.clear() + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + } + } + + /** The least upper bound wrt <:< of a list of types */ + protected[internal] def lub(ts: List[Type], depth: Depth): Type = { + def lub0(ts0: List[Type]): Type = elimSub(ts0, depth) match { + case List() => NothingTpe + 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 @ (mt @ MethodType(params, _)) :: rest => + MethodType(params, lub0(matchingRestypes(ts, mt.paramTypes))) + 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 @ AnnotatedType(annots, tpe) :: rest => + annotationsLub(lub0(ts map (_.withoutAnnotations)), ts) + case ts => + lubResults get ((depth, ts)) match { + case Some(lubType) => + lubType + case None => + lubResults((depth, ts)) = AnyTpe + val res = if (depth.isNegative) AnyTpe 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.isZero ) 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) + || sym.isFinal + || 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, depth.decr)) + 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), depth.decr), lub(bnds map (_.hi), depth.decr)) + 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, depth))) + } + // 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 lubsym(sym) andAlso (addMember(lubThisType, lubRefined, _, depth)) + 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 => + isSubType(t, lubRefined, depth) || { + if (settings.debug || 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 + } + } + // dropIllegalStarTypes is a localized fix for SI-6897. We should probably + // integrate that transformation at a lower level in master, but lubs are + // the likely and maybe only spot they escape, so fixing here for 2.10.1. + existentialAbstraction(tparams, dropIllegalStarTypes(lubType)) + } + if (printLubs) { + println(indent + "lub of " + ts + " at depth "+depth)//debug + indent = indent + " " + assert(indent.length <= 100) + } + if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) + val res = lub0(ts) + if (printLubs) { + indent = indent stripSuffix " " + println(indent + "lub of " + ts + " is " + res)//debug + } + 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 = Depth.Zero + private final val globalGlbLimit = Depth(2) + + /** The greatest lower bound of a list of types (as determined by `<:<`). */ + def glb(ts: List[Type]): Type = elimSuper(ts) match { + case List() => AnyTpe + case List(t) => t + case ts0 => + if (Statistics.canEnable) Statistics.incCounter(lubCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null + try { + glbNorm(ts0, lubDepth(ts0)) + } finally { + lubResults.clear() + glbResults.clear() + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + } + } + + protected[internal] def glb(ts: List[Type], depth: Depth): Type = elimSuper(ts) match { + case List() => AnyTpe + case List(t) => t + case ts0 => glbNorm(ts0, depth) + } + + /** The greatest lower bound of a list of types (as determined by `<:<`), which have been normalized + * with regard to `elimSuper`. */ + protected def glbNorm(ts: List[Type], depth: Depth): Type = { + def glb0(ts0: List[Type]): Type = ts0 match { + case List() => AnyTpe + 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 @ (mt @ MethodType(params, _)) :: rest => + MethodType(params, glbNorm(matchingRestypes(ts, mt.paramTypes), 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)) = NothingTpe + val res = if (depth.isNegative) NothingTpe 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.isZero) 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, depth.decr) + 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), depth.decr) + val hi = glb(bnds map (_.bounds.hi), depth.decr) + 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 = globalGlbDepth.incr + val dss = ts flatMap refinedToDecls + for (ds <- dss; sym <- ds.iterator) + if (globalGlbDepth < globalGlbLimit && !specializesSym(glbThisType, sym, depth)) + try { + addMember(glbThisType, glbRefined, glbsym(sym), depth) + } catch { + case ex: NoCommonType => + } + } finally { + globalGlbDepth = globalGlbDepth.decr + } + if (glbRefined.decls.isEmpty) glbBase else glbRefined + } + existentialAbstraction(tparams, glbType) + } catch { + case GlbFailure => + if (ts forall (t => NullTpe <:< t)) NullTpe + else NothingTpe + } + } + // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG + if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) + glb0(ts) + // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG + } + + /** 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 mt @ MethodType(params1, res) if isSameTypes(mt.paramTypes, pts) => + res + case NullaryMethodType(res) if pts.isEmpty => + res + case _ => + throw new NoCommonType(tps) + } +} diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala new file mode 100644 index 0000000000..f9b10c90be --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -0,0 +1,592 @@ +package scala +package reflect +package internal +package tpe + +import scala.collection.{ mutable } +import util.{ Statistics, TriState } +import scala.annotation.tailrec + +trait TypeComparers { + self: SymbolTable => + import definitions._ + import TypesStats._ + + private final val LogPendingSubTypesThreshold = TypeConstants.DefaultLogThreshhold + + private val _pendingSubTypes = new mutable.HashSet[SubTypePair] + def pendingSubTypes = _pendingSubTypes + + final case class SubTypePair(tp1: Type, tp2: Type) { + // SI-8146 we used to implement equality here in terms of pairwise =:=. + // But, this was inconsistent with hashCode, which was based on the + // Type#hashCode, based on the structure of types, not the meaning. + // Now, we use `Type#{equals,hashCode}` as the (consistent) basis for + // detecting cycles (aka keeping subtyping decidable.) + // + // I added a tests to show that we detect the cycle: neg/t8146-no-finitary* + + override def toString = tp1+" <:<? "+tp2 + } + + private var _subsametypeRecursions: Int = 0 + def subsametypeRecursions = _subsametypeRecursions + def subsametypeRecursions_=(value: Int) = _subsametypeRecursions = value + + private def isUnifiable(pre1: Type, pre2: Type) = ( + (isEligibleForPrefixUnification(pre1) || isEligibleForPrefixUnification(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) 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 || sym1.owner.hasPackageFlag || phase.erasedTypes || pre1 =:= pre2 + else + (sym1.name == sym2.name) && isUnifiable(pre1, pre2) + ) + + 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) = !isSameTypeConstructor(tp1, tp2) + + private def isSameTypeConstructor(tr1: TypeRef, tr2: TypeRef): Boolean = ( + (tr1.sym == tr2.sym) + && !isDifferentType(tr1.pre, tr2.pre) + ) + private def isSameTypeConstructor(tp1: Type, tp2: Type): Boolean = ( + tp1.isInstanceOf[TypeRef] + && tp2.isInstanceOf[TypeRef] + && isSameTypeConstructor(tp1.asInstanceOf[TypeRef], tp2.asInstanceOf[TypeRef]) + ) + + /** Do `tp1` and `tp2` denote equivalent types? */ + def isSameType(tp1: Type, tp2: Type): Boolean = try { + if (Statistics.canEnable) Statistics.incCounter(sametypeCount) + subsametypeRecursions += 1 + //OPT cutdown on Function0 allocation + //was: + // undoLog undoUnless { + // isSameType1(tp1, tp2) + // } + + val before = undoLog.log + var result = false + try { + result = isSameType1(tp1, tp2) + } + finally if (!result) undoLog.undoTo(before) + result + } + 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() + } + + // @pre: at least one argument has annotations + private def sameAnnotatedTypes(tp1: Type, tp2: Type) = ( + annotationsConform(tp1, tp2) + && annotationsConform(tp2, tp1) + && (tp1.withoutAnnotations =:= tp2.withoutAnnotations) + ) + // We flush out any AnnotatedTypes before calling isSameType2 because + // unlike most other subclasses of Type, we have to allow for equivalence of any + // combination of { tp1, tp2 } { is, is not } an AnnotatedType - this because the + // logic of "annotationsConform" is arbitrary and unknown. + private def isSameType1(tp1: Type, tp2: Type): Boolean = typeRelationPreCheck(tp1, tp2) match { + case state if state.isKnown => state.booleanValue + case _ if typeHasAnnotations(tp1) || typeHasAnnotations(tp2) => sameAnnotatedTypes(tp1, tp2) + case _ => isSameType2(tp1, tp2) + } + + private def isSameHKTypes(tp1: Type, tp2: Type) = ( + tp1.isHigherKinded + && tp2.isHigherKinded + && (tp1.normalize =:= tp2.normalize) + ) + private def isSameTypeRef(tr1: TypeRef, tr2: TypeRef) = ( + equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre) + && (isSameHKTypes(tr1, tr2) || isSameTypes(tr1.args, tr2.args)) + ) + + private def isSameSingletonType(tp1: SingletonType, tp2: SingletonType): Boolean = { + // We don't use dealiasWiden here because we are looking for the SAME type, + // and widening leads to a less specific type. The logic is along the lines of + // dealiasAndFollowUnderlyingAsLongAsTheTypeIsEquivalent. This method is only + // called after a surface comparison has failed, so if chaseDealiasedUnderlying + // does not produce a type other than tp1 and tp2, return false. + @tailrec def chaseDealiasedUnderlying(tp: Type): Type = tp.underlying.dealias match { + case next: SingletonType if tp ne next => chaseDealiasedUnderlying(next) + case _ => tp + } + val origin1 = chaseDealiasedUnderlying(tp1) + val origin2 = chaseDealiasedUnderlying(tp2) + ((origin1 ne tp1) || (origin2 ne tp2)) && (origin1 =:= origin2) + } + + private def isSameMethodType(mt1: MethodType, mt2: MethodType) = ( + isSameTypes(mt1.paramTypes, mt2.paramTypes) + && (mt1.resultType =:= mt2.resultType.substSym(mt2.params, mt1.params)) + && (mt1.isImplicit == mt2.isImplicit) + ) + + private def equalTypeParamsAndResult(tparams1: List[Symbol], res1: Type, tparams2: List[Symbol], res2: Type) = { + def subst(info: Type) = info.substSym(tparams2, tparams1) + // corresponds does not check length of two sequences before checking the predicate, + // but SubstMap assumes it has been checked (SI-2956) + ( sameLength(tparams1, tparams2) + && (tparams1 corresponds tparams2)((p1, p2) => methodHigherOrderTypeParamsSameVariance(p1, p2) && p1.info =:= subst(p2.info)) + && (res1 =:= subst(res2)) + ) + } + + // SI-2066 This prevents overrides with incompatible variance in higher order type parameters. + private def methodHigherOrderTypeParamsSameVariance(sym1: Symbol, sym2: Symbol) = { + def ignoreVariance(sym: Symbol) = !(sym.isHigherOrderTypeParameter && sym.logicallyEnclosingMember.isMethod) + !settings.isScala211 || ignoreVariance(sym1) || ignoreVariance(sym2) || sym1.variance == sym2.variance + } + + private def methodHigherOrderTypeParamsSubVariance(low: Symbol, high: Symbol) = + !settings.isScala211 || methodHigherOrderTypeParamsSameVariance(low, high) || low.variance.isInvariant + + def isSameType2(tp1: Type, tp2: Type): Boolean = { + def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSameType(lhs, rhs) + + /* Here we highlight those unfortunate type-like constructs which + * are hidden bundles of mutable state, cruising the type system picking + * up any type constraints naive enough to get into their hot rods. + */ + def mutateNonTypeConstructs(lhs: Type, rhs: Type) = lhs match { + case BoundedWildcardType(bounds) => bounds containsType rhs + case tv @ TypeVar(_, _) => tv.registerTypeEquality(rhs, typeVarLHS = lhs eq tp1) + case TypeRef(tv @ TypeVar(_, _), sym, _) => tv.registerTypeSelection(sym, rhs) + case _ => false + } + /* SingletonType receives this additional scrutiny because there are + * a variety of Types which must be treated as equivalent even if they + * arrive in different guises. For instance, object Foo in the following + * might appear in (at least) the four given below. + * + * package pkg { object Foo ; type Bar = Foo.type } + * + * ModuleClassTypeRef(pkg.type, Foo: ModuleClassSymbol, Nil) + * ThisType(Foo: ModuleClassSymbol) + * SingleType(pkg.type, Foo: ModuleSymbol) + * AliasTypeRef(NoPrefix, sym: AliasSymbol, Nil) where sym.info is one of the above + */ + def sameSingletonType = tp1 match { + case tp1: SingletonType => tp2 match { + case tp2: SingletonType => isSameSingletonType(tp1, tp2) + case _ => false + } + case _ => false + } + + /* Those false cases certainly are ugly. There's a proposed SIP to deuglify it. + * https://docs.google.com/a/improving.org/document/d/1onPrzSqyDpHScc9PS_hpxJwa3FlPtthxw-bAuuEe8uA + */ + def sameTypeAndSameCaseClass = tp1 match { + case tp1: TypeRef => tp2 match { case tp2: TypeRef => isSameTypeRef(tp1, tp2) ; case _ => false } + case tp1: MethodType => tp2 match { case tp2: MethodType => isSameMethodType(tp1, tp2) ; case _ => false } + case RefinedType(ps1, decls1) => tp2 match { case RefinedType(ps2, decls2) => isSameTypes(ps1, ps2) && (decls1 isSameScope decls2) ; case _ => false } + case SingleType(pre1, sym1) => tp2 match { case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1, pre1, sym2, pre2) ; case _ => false } + case PolyType(ps1, res1) => tp2 match { case PolyType(ps2, res2) => equalTypeParamsAndResult(ps1, res1, ps2, res2) ; case _ => false } + case ExistentialType(qs1, res1) => tp2 match { case ExistentialType(qs2, res2) => equalTypeParamsAndResult(qs1, res1, qs2, res2) ; case _ => false } + case ThisType(sym1) => tp2 match { case ThisType(sym2) => sym1 == sym2 ; case _ => false } + case ConstantType(c1) => tp2 match { case ConstantType(c2) => c1 == c2 ; case _ => false } + case NullaryMethodType(res1) => tp2 match { case NullaryMethodType(res2) => res1 =:= res2 ; case _ => false } + case TypeBounds(lo1, hi1) => tp2 match { case TypeBounds(lo2, hi2) => lo1 =:= lo2 && hi1 =:= hi2 ; case _ => false } + case _ => false + } + + ( sameTypeAndSameCaseClass + || sameSingletonType + || mutateNonTypeConstructs(tp1, tp2) + || mutateNonTypeConstructs(tp2, tp1) + || retry(normalizePlus(tp1), normalizePlus(tp2)) + ) + } + + def isSubType(tp1: Type, tp2: Type, depth: Depth = Depth.AnyDepth): Boolean = try { + subsametypeRecursions += 1 + + //OPT cutdown on Function0 allocation + //was: + // 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) + // } + // } + + val before = undoLog.log + var result = false + + try result = { // if subtype test fails, it should not affect constraints on typevars + if (subsametypeRecursions >= LogPendingSubTypesThreshold) { + val p = new SubTypePair(tp1, tp2) + if (pendingSubTypes(p)) + false // see neg/t8146-no-finitary* + else + try { + pendingSubTypes += p + isSubType1(tp1, tp2, depth) + } finally { + pendingSubTypes -= p + } + } else { + isSubType1(tp1, tp2, depth) + } + } finally if (!result) undoLog.undoTo(before) + + result + } 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() + } + + /** Check whether the subtype or type equivalence relationship + * between the argument is predetermined. Returns a tri-state + * value: True means the arguments are always sub/same types, + * False means they never are, and Unknown means the caller + * will have to figure things out. + */ + private def typeRelationPreCheck(tp1: Type, tp2: Type): TriState = { + def isTrue = ( + (tp1 eq tp2) + || isErrorOrWildcard(tp1) + || isErrorOrWildcard(tp2) + || (tp1 eq NoPrefix) && tp2.typeSymbol.isPackageClass // !! I do not see how this would be warranted by the spec + || (tp2 eq NoPrefix) && tp1.typeSymbol.isPackageClass // !! I do not see how this would be warranted by the spec + ) + // isFalse, assuming !isTrue + def isFalse = ( + (tp1 eq NoType) + || (tp2 eq NoType) + || (tp1 eq NoPrefix) + || (tp2 eq NoPrefix) + ) + + if (isTrue) TriState.True + else if (isFalse) TriState.False + else TriState.Unknown + } + + private def isSubType1(tp1: Type, tp2: Type, depth: Depth): Boolean = typeRelationPreCheck(tp1, tp2) match { + case state if state.isKnown => state.booleanValue + case _ if typeHasAnnotations(tp1) || typeHasAnnotations(tp2) => annotationsConform(tp1, tp2) && (tp1.withoutAnnotations <:< tp2.withoutAnnotations) + case _ => isSubType2(tp1, tp2, depth) + } + + private def isPolySubType(tp1: PolyType, tp2: PolyType): Boolean = { + val PolyType(tparams1, res1) = tp1 + val PolyType(tparams2, res2) = tp2 + + sameLength(tparams1, tparams2) && { + // fast-path: polymorphic method type -- type params cannot be captured + val isMethod = tparams1.head.owner.isMethod + //@M for an example of why we need to generate fresh symbols otherwise, see neg/tcpoly_ticket2101.scala + val substitutes = if (isMethod) tparams1 else cloneSymbols(tparams1) + def sub1(tp: Type) = if (isMethod) tp else tp.substSym(tparams1, substitutes) + def sub2(tp: Type) = tp.substSym(tparams2, substitutes) + def cmp(p1: Symbol, p2: Symbol) = ( + methodHigherOrderTypeParamsSubVariance(p2, p1) + && sub2(p2.info) <:< sub1(p1.info) + ) + + (tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2)) + } + } + // This is looking for situations such as B.this.x.type <:< B.super.x.type. + // If it's a ThisType on the lhs and a SuperType on the right, and they originate + // in the same class, and the 'x' in the ThisType has in its override chain + // the 'x' in the SuperType, then the types conform. + private def isThisAndSuperSubtype(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (SingleType(ThisType(lpre), v1), SingleType(SuperType(ThisType(rpre), _), v2)) => (lpre == rpre) && (v1.overrideChain contains v2) + case _ => false + } + + // @assume tp1.isHigherKinded || tp2.isHigherKinded + def isHKSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = { + def isSub(ntp1: Type, ntp2: Type) = (ntp1.withoutAnnotations, ntp2.withoutAnnotations) match { + case (TypeRef(_, AnyClass, _), _) => false // avoid some warnings when Nothing/Any are on the other side + case (_, TypeRef(_, NothingClass, _)) => false + case (pt1: PolyType, pt2: PolyType) => isPolySubType(pt1, pt2) // @assume both .isHigherKinded (both normalized to PolyType) + case (_: PolyType, MethodType(ps, _)) if ps exists (_.tpe.isWildcard) => false // don't warn on HasMethodMatching on right hand side + case _ => // @assume !(both .isHigherKinded) thus cannot be subtypes + def tp_s(tp: Type): String = f"$tp%-20s ${util.shortClassOfInstance(tp)}%s" + devWarning(s"HK subtype check on $tp1 and $tp2, but both don't normalize to polytypes:\n tp1=${tp_s(ntp1)}\n tp2=${tp_s(ntp2)}") + false + } + + ( tp1.typeSymbol == NothingClass // @M Nothing is subtype of every well-kinded type + || tp2.typeSymbol == AnyClass // @M Any is supertype of every well-kinded type (@PP: is it? What about continuations plugin?) + || isSub(tp1.normalize, tp2.normalize) && annotationsConform(tp1, tp2) // @M! normalize reduces higher-kinded case to PolyType's + ) + } + + /** Does type `tp1` conform to `tp2`? */ + private def isSubType2(tp1: Type, tp2: Type, depth: Depth): Boolean = { + def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSubType(lhs, rhs, depth) + + if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2)) + return (tp1 =:= tp2) || isThisAndSuperSubtype(tp1, tp2) || retry(tp1.underlying, tp2) + + if (tp1.isHigherKinded || tp2.isHigherKinded) + return isHKSubType(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 => + // TODO - dedicate a method to TypeRef/TypeRef subtyping. + // These typerefs are pattern matched up and down far more + // than is necessary. + val sym1 = tr1.sym + val sym2 = tr2.sym + val pre1 = tr1.pre + val pre2 = tr2.pre + (((if (sym1 == sym2) phase.erasedTypes || sym1.owner.hasPackageFlag || isSubType(pre1, pre2, depth) + 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, depth)) + || + sym2.isClass && { + val base = tr1 baseType sym2 + (base ne tr1) && isSubType(base, tr2, depth) + } + || + thirdTryRef(tr1, tr2)) + case _ => + secondTry + } + case AnnotatedType(_, _) => + isSubType(tp1.withoutAnnotations, tp2.withoutAnnotations, depth) && + annotationsConform(tp1, tp2) + case BoundedWildcardType(bounds) => + isSubType(tp1, bounds.hi, depth) + case tv2 @ TypeVar(_, constr2) => + tp1 match { + case AnnotatedType(_, _) | BoundedWildcardType(_) => + secondTry + case _ => + tv2.registerBound(tp1, isLowerBound = true) + } + case _ => + secondTry + } + + /* Second try, on the left: + * - unwrap AnnotatedTypes, BoundedWildcardTypes, + * - bind typevars, + * - handle existential types by skolemization. + */ + def secondTry = tp1 match { + case AnnotatedType(_, _) => + isSubType(tp1.withoutAnnotations, tp2.withoutAnnotations, depth) && + annotationsConform(tp1, tp2) + case BoundedWildcardType(bounds) => + isSubType(tp1.bounds.lo, tp2, depth) + case tv @ TypeVar(_,_) => + tv.registerBound(tp2, isLowerBound = false) + case ExistentialType(_, _) => + try { + skolemizationLevel += 1 + isSubType(tp1.skolemizeExistential, tp2, depth) + } finally { + skolemizationLevel -= 1 + } + case _ => + thirdTry + } + + def thirdTryRef(tp1: Type, tp2: TypeRef): Boolean = { + val sym2 = tp2.sym + def retry(lhs: Type, rhs: Type) = isSubType(lhs, rhs, depth) + def abstractTypeOnRight(lo: Type) = isDifferentTypeConstructor(tp2, lo) && retry(tp1, lo) + def classOnRight = ( + if (isRawType(tp2)) retry(tp1, rawToExistential(tp2)) + else if (sym2.isRefinementClass) retry(tp1, sym2.info) + else fourthTry + ) + sym2 match { + case SingletonClass => tp1.isStable || fourthTry + case _: ClassSymbol => classOnRight + case _: TypeSymbol if sym2.isDeferred => abstractTypeOnRight(tp2.bounds.lo) || fourthTry + case _: TypeSymbol => retry(tp1.normalize, tp2.normalize) + case _ => fourthTry + } + } + + /* Third try, on the right: + * - decompose refined types. + * - handle typerefs and existentials. + * - handle left+right method types, polytypes, typebounds + */ + def thirdTry = tp2 match { + case tr2: TypeRef => + thirdTryRef(tp1, tr2) + case rt2: RefinedType => + (rt2.parents forall (isSubType(tp1, _, depth))) && + (rt2.decls forall (specializesSym(tp1, _, depth))) + case et2: ExistentialType => + et2.withTypeVars(isSubType(tp1, _, depth), depth) || fourthTry + 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) && + isSubType(res1.substSym(params1, params2), res2, depth)) + // 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(_) => + isSubType(pt1.resultType, pt2.resultType, depth) + case _ => + false + } + case TypeBounds(lo2, hi2) => + tp1 match { + case TypeBounds(lo1, hi1) => + isSubType(lo2, lo1, depth) && isSubType(hi1, hi2, depth) + case _ => + false + } + case _ => + fourthTry + } + + /* Fourth try, on the left: + * - handle typerefs, refined types, and singleton types. + */ + def fourthTry = { + def retry(lhs: Type, rhs: Type) = isSubType(lhs, rhs, depth) + def abstractTypeOnLeft(hi: Type) = isDifferentTypeConstructor(tp1, hi) && retry(hi, tp2) + + tp1 match { + case tr1 @ TypeRef(pre1, sym1, _) => + def nullOnLeft = tp2 match { + case TypeRef(_, sym2, _) => sym1 isBottomSubClass sym2 + case _ => isSingleType(tp2) && retry(tp1, tp2.widen) + } + def moduleOnLeft = tp2 match { + case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1.sourceModule, pre1, sym2, pre2) + case _ => false + } + def classOnLeft = ( + if (isRawType(tp1)) retry(rawToExistential(tp1), tp2) + else if (sym1.isModuleClass) moduleOnLeft + else sym1.isRefinementClass && retry(sym1.info, tp2) + ) + sym1 match { + case NothingClass => true + case NullClass => nullOnLeft + case _: ClassSymbol => classOnLeft + case _: TypeSymbol if sym1.isDeferred => abstractTypeOnLeft(tp1.bounds.hi) + case _: TypeSymbol => retry(tp1.normalize, tp2.normalize) + case _ => false + } + case RefinedType(parents, _) => parents exists (retry(_, tp2)) + case _: SingletonType => retry(tp1.underlying, tp2) + case _ => false + } + } + + firstTry + } + + + def isWeakSubType(tp1: Type, tp2: Type) = + tp1.dealiasWiden match { + case TypeRef(_, sym1, _) if isNumericValueClass(sym1) => + tp2.deconst.dealias 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.dealias match { + case TypeRef(_, sym2, _) if isNumericValueClass(sym2) => + tv1.registerBound(tp2, isLowerBound = false, isNumericBound = true) + case _ => + isSubType(tp1, tp2) + } + case _ => + isSubType(tp1, tp2) + } + + def isNumericSubType(tp1: Type, tp2: Type) = ( + isNumericSubClass(primitiveBaseClass(tp1.dealiasWiden), primitiveBaseClass(tp2.dealias)) + ) + + /** If the given type has a primitive class among its base classes, + * the symbol of that class. Otherwise, NoSymbol. + */ + private def primitiveBaseClass(tp: Type): Symbol = { + @tailrec def loop(bases: List[Symbol]): Symbol = bases match { + case Nil => NoSymbol + case x :: xs => if (isPrimitiveValueClass(x)) x else loop(xs) + } + loop(tp.baseClasses) + } +} diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala new file mode 100644 index 0000000000..c1c43178e5 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala @@ -0,0 +1,275 @@ +package scala +package reflect +package internal +package tpe + +import scala.collection.{ generic } +import generic.Clearable + +private[internal] trait TypeConstraints { + self: SymbolTable => + import definitions._ + + /** A log of type variable with their original constraints. Used in order + * to undo constraints in the case of isSubType/isSameType failure. + */ + private lazy val _undoLog = new UndoLog + def undoLog = _undoLog + + import TypeConstraints.UndoPair + class UndoLog extends Clearable { + type UndoPairs = List[UndoPair[TypeVar, TypeConstraint]] + //OPT this method is public so we can do `manual inlining` + var log: UndoPairs = List() + + // register with the auto-clearing cache manager + perRunCaches.recordCache(this) + + /** Undo all changes to constraints to type variables upto `limit`. */ + //OPT this method is public so we can do `manual inlining` + def undoTo(limit: UndoPairs) { + assertCorrectThread() + while ((log ne limit) && log.nonEmpty) { + val UndoPair(tv, constr) = log.head + tv.constr = constr + log = log.tail + } + } + + /** No sync necessary, because record should only + * be called from within an undo or undoUnless block, + * which is already synchronized. + */ + private[reflect] def record(tv: TypeVar) = { + log ::= UndoPair(tv, tv.constr.cloneInternal) + } + + def clear() { + if (settings.debug) + self.log("Clearing " + log.size + " entries from the undoLog.") + log = Nil + } + + // `block` should not affect constraints on typevars + def undo[T](block: => T): T = { + val before = log + try block + finally undoTo(before) + } + } + + /** @PP: Unable to see why these apparently constant types should need vals + * in every TypeConstraint, I lifted them out. + */ + private lazy val numericLoBound = IntTpe + private lazy val numericHiBound = intersectionType(List(ByteTpe, CharTpe), 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 typeIsNothing + private var hibounds = hi0 filterNot typeIsAny + 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) { + // For some reason which is still a bit fuzzy, we must let Nothing through as + // a lower bound despite the fact that Nothing is always a lower bound. My current + // supposition is that the side-effecting type constraint accumulation mechanism + // depends on these subtype tests being performed to make forward progress when + // there are mutally recursive type vars. + // See pos/t6367 and pos/t6499 for the competing test cases. + val mustConsider = tp.typeSymbol match { + case NothingClass => true + case _ => !(lobounds contains tp) + } + if (mustConsider) { + 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) { + // My current test case only demonstrates the need to let Nothing through as + // a lower bound, but I suspect the situation is symmetrical. + val mustConsider = tp.typeSymbol match { + case AnyClass => true + case _ => !(hibounds contains tp) + } + if (mustConsider) { + 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 instWithinBounds = instValid && isWithinBounds(inst) + + 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 typeIsNothing match { + case Nil => "" + case tp :: Nil => " >: " + tp + case tps => tps.mkString(" >: (", ", ", ")") + } + val hi = hiBounds filterNot typeIsAny match { + case Nil => "" + case tp :: Nil => " <: " + tp + case tps => tps.mkString(" <: (", ", ", ")") + } + lo + hi + } + if (inst eq NoType) boundsStr + else boundsStr + " _= " + inst.safeToString + } + } + + /** 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[Variance], upper: Boolean, depth: Depth): Boolean = { + + def solveOne(tvar: TypeVar, tparam: Symbol, variance: Variance) { + if (tvar.constr.inst == NoType) { + val up = if (variance.isContravariant) !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) { + debuglog(s"$tvar addHiBound $bound.instantiateTypeParams($tparams, $tvars)") + tvar addHiBound bound.instantiateTypeParams(tparams, tvars) + } + for (tparam2 <- tparams) + tparam2.info.bounds.lo.dealias match { + case TypeRef(_, `tparam`, _) => + debuglog(s"$tvar addHiBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)") + tvar addHiBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars) + case _ => + } + } else { + if (bound.typeSymbol != NothingClass && bound.typeSymbol != tparam) { + debuglog(s"$tvar addLoBound $bound.instantiateTypeParams($tparams, $tvars)") + tvar addLoBound bound.instantiateTypeParams(tparams, tvars) + } + for (tparam2 <- tparams) + tparam2.info.bounds.hi.dealias match { + case TypeRef(_, `tparam`, _) => + debuglog(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)") + 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))) + val newInst = ( + if (up) { + if (depth.isAnyDepth) glb(tvar.constr.hiBounds) + else glb(tvar.constr.hiBounds, depth) + } + else { + if (depth.isAnyDepth) lub(tvar.constr.loBounds) + else lub(tvar.constr.loBounds, depth) + } + ) + + debuglog(s"$tvar setInst $newInst") + tvar setInst newInst + //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) + + def logBounds(tv: TypeVar) = log { + val what = if (!tv.instValid) "is invalid" else s"does not conform to bounds: ${tv.constr}" + s"Inferred type for ${tv.originString} (${tv.inst}) $what" + } + + tvars forall (tv => tv.instWithinBounds || util.andFalse(logBounds(tv))) + } +} + +private[internal] object TypeConstraints { + // UndoPair is declared in companion object to not hold an outer pointer reference + final case class UndoPair[TypeVar <: SymbolTable#TypeVar, + TypeConstraint <: TypeConstraints#TypeConstraint](tv: TypeVar, tConstraint: TypeConstraint) +} diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala new file mode 100644 index 0000000000..f06420de96 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -0,0 +1,1173 @@ +package scala +package reflect +package internal +package tpe + +import scala.collection.{ mutable, immutable } +import Flags._ +import scala.annotation.tailrec +import Variance._ + +private[internal] trait TypeMaps { + self: SymbolTable => + import definitions._ + + /** Normalize any type aliases within this type (@see Type#normalize). + * Note that this depends very much on the call to "normalize", not "dealias", + * so it is no longer carries the too-stealthy name "deAlias". + */ + object normalizeAliases extends TypeMap { + def apply(tp: Type): Type = mapOver(tp match { + case TypeRef(_, sym, _) if sym.isAliasType && tp.isHigherKinded => logResult(s"Normalized type alias function $tp")(tp.normalize) + case TypeRef(_, sym, _) if sym.isAliasType => tp.normalize + case tp => 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, _) => + AnyTpe + case tp1 @ RefinedType(parents, decls) => + parents filter (_.typeSymbol != SingletonClass) match { + case Nil => AnyTpe + case p :: Nil if decls.isEmpty => mapOver(p) + case ps => mapOver(copyRefinedType(tp1, ps, decls)) + } + case tp1 => + mapOver(tp1) + } + } + } + + /** Type with all top-level occurrences of abstract types replaced by their bounds */ + object abstractTypesToBounds extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(_, sym, _) if sym.isAliasType => apply(tp.dealias) + case TypeRef(_, sym, _) if sym.isAbstractType => apply(tp.bounds.hi) + case rtp @ RefinedType(parents, decls) => copyRefinedType(rtp, parents mapConserve this, decls) + case AnnotatedType(_, _) => mapOver(tp) + case _ => tp // no recursion - top level only + } + } + + // 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 + + /** Turn any T* types into Seq[T] except when + * in method parameter position. + */ + object dropIllegalStarTypes extends TypeMap { + def apply(tp: Type): Type = tp match { + case MethodType(params, restpe) => + // Not mapping over params + val restpe1 = apply(restpe) + if (restpe eq restpe1) tp + else MethodType(params, restpe1) + case TypeRef(_, RepeatedParamClass, arg :: Nil) => + seqType(arg) + case _ => + if (etaExpandKeepsStar) tp else mapOver(tp) + } + } + + 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 + } + + // todo. move these into scala.reflect.api + + /** A prototype for mapping a function over all possible types + */ + abstract class TypeMap(trackVariance: Boolean) extends (Type => Type) { + def this() = this(trackVariance = false) + def apply(tp: Type): Type + + private[this] var _variance: Variance = if (trackVariance) Covariant else Invariant + + def variance_=(x: Variance) = { assert(trackVariance, this) ; _variance = x } + def variance = _variance + + /** 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 = ( + if (trackVariance && args.nonEmpty && !variance.isInvariant && sym.typeParams.nonEmpty) + mapOverArgs(args, sym.typeParams) + else + 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 = flipped(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 = flipped(mapOver(tparams)) + val 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 = flipped(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) + copyRefinedType(rtp, parents1, decls1) + case ExistentialType(tparams, result) => + val tparams1 = mapOver(tparams) + val 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 AnnotatedType(annots, atp) => + 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) + /* + 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); + } + + def withVariance[T](v: Variance)(body: => T): T = { + val saved = variance + variance = v + try body finally variance = saved + } + @inline final def flipped[T](body: => T): T = { + if (trackVariance) variance = variance.flip + try body + finally if (trackVariance) variance = variance.flip + } + protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = ( + if (trackVariance) + map2Conserve(args, tparams)((arg, tparam) => withVariance(variance * tparam.variance)(this(arg))) + else + args mapConserve this + ) + /** Applies this map to the symbol's info, setting variance = Invariant + * if necessary when the symbol is an alias. + */ + private def applyToSymbolInfo(sym: Symbol): Type = { + if (trackVariance && !variance.isInvariant && sym.isAliasType) + withVariance(Invariant)(this(sym.info)) + else + this(sym.info) + } + + /** Called by mapOver to determine whether the original symbols can + * be returned, or whether they must be cloned. + */ + protected def noChangeToSymbols(origSyms: List[Symbol]): Boolean = { + @tailrec def loop(syms: List[Symbol]): Boolean = syms match { + case Nil => true + case x :: xs => (x.info eq applyToSymbolInfo(x)) && loop(xs) + } + loop(origSyms) + } + + /** 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 + } + } + + /** 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) AnyRefTpe + else try { + expanded += sym + val eparams = mapOver(typeParamsToExistentials(sym)) + existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) + } finally { + expanded -= sym + } + case _ => + mapOver(tp) + } + } + /*** + *@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) + } + } + */ + + /** Used by existentialAbstraction. + */ + class ExistentialExtrapolation(tparams: List[Symbol]) extends TypeMap(trackVariance = true) { + 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) + } + + /** If these conditions all hold: + * 1) we are in covariant (or contravariant) position + * 2) this type occurs exactly once in the existential scope + * 3) the widened upper (or lower) bound of this type contains no references to tparams + * Then we replace this lone occurrence of the type with the widened upper (or lower) bound. + * All other types pass through unchanged. + */ + def apply(tp: Type): Type = { + val tp1 = mapOver(tp) + if (variance.isInvariant) tp1 + else tp1 match { + case TypeRef(pre, sym, args) if tparams contains sym => + val repl = if (variance.isPositive) dropSingletonType(tp1.bounds.hi) else tp1.bounds.lo + val count = occurCount(sym) + val containsTypeParam = tparams exists (repl contains _) + def msg = { + val word = if (variance.isPositive) "upper" else "lower" + s"Widened lone occurrence of $tp1 inside existential to $word bound" + } + if (!repl.typeSymbol.isBottomClass && count == 1 && !containsTypeParam) + debuglogResult(msg)(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) + } + } + + /** Might the given symbol be important when calculating the prefix + * of a type? When tp.asSeenFrom(pre, clazz) is called on `tp`, + * the result will be `tp` unchanged if `pre` is trivial and `clazz` + * is a symbol such that isPossiblePrefix(clazz) == false. + */ + def isPossiblePrefix(clazz: Symbol) = clazz.isClass && !clazz.isPackageClass + + protected[internal] def skipPrefixOf(pre: Type, clazz: Symbol) = ( + (pre eq NoType) || (pre eq NoPrefix) || !isPossiblePrefix(clazz) + ) + + def newAsSeenFromMap(pre: Type, clazz: Symbol): AsSeenFromMap = + new AsSeenFromMap(pre, clazz) + + /** A map to compute the asSeenFrom method. + */ + class AsSeenFromMap(seenFromPrefix: Type, seenFromClass: Symbol) extends TypeMap with KeepOnlyTypeConstraints { + // Some example source constructs relevant in asSeenFrom: + // + // object CaptureThis { + // trait X[A] { def f: this.type = this } + // class Y[A] { def f: this.type = this } + // // Created new existential to represent This(CaptureThis.X) seen from CaptureThis.X[B]: type _1.type <: CaptureThis.X[B] with Singleton + // def f1[B] = new X[B] { } + // // TODO - why is the behavior different when it's a class? + // def f2[B] = new Y[B] { } + // } + // class CaptureVal[T] { + // val f: java.util.List[_ <: T] = null + // // Captured existential skolem for type _$1 seen from CaptureVal.this.f.type: type _$1 + // def g = f get 0 + // } + // class ClassParam[T] { + // // AsSeenFromMap(Inner.this.type, class Inner)/classParameterAsSeen(T)#loop(ClassParam.this.type, class ClassParam) + // class Inner(lhs: T) { def f = lhs } + // } + def capturedParams: List[Symbol] = _capturedParams + def capturedSkolems: List[Symbol] = _capturedSkolems + + def apply(tp: Type): Type = tp match { + case tp @ ThisType(_) => thisTypeAsSeen(tp) + case tp @ SingleType(_, sym) => if (sym.isPackageClass) tp else singleTypeAsSeen(tp) + case tp @ TypeRef(_, sym, _) if isTypeParamOfEnclosingClass(sym) => classParameterAsSeen(tp) + case _ => mapOver(tp) + } + + private var _capturedSkolems: List[Symbol] = Nil + private var _capturedParams: List[Symbol] = Nil + private val isStablePrefix = seenFromPrefix.isStable + + // isBaseClassOfEnclosingClassOrInfoIsNotYetComplete would be a more accurate + // but less succinct name. + private def isBaseClassOfEnclosingClass(base: Symbol) = { + def loop(encl: Symbol): Boolean = ( + isPossiblePrefix(encl) + && ((encl isSubClass base) || loop(encl.owner.enclClass)) + ) + // The hasCompleteInfo guard is necessary to avoid cycles during the typing + // of certain classes, notably ones defined inside package objects. + !base.hasCompleteInfo || loop(seenFromClass) + } + + /** Is the symbol a class type parameter from one of the enclosing + * classes, or a base class of one of them? + */ + private def isTypeParamOfEnclosingClass(sym: Symbol): Boolean = ( + sym.isTypeParameter + && sym.owner.isClass + && isBaseClassOfEnclosingClass(sym.owner) + ) + + /** Creates an existential representing a type parameter which appears + * in the prefix of a ThisType. + */ + protected def captureThis(pre: Type, clazz: Symbol): Type = { + capturedParams find (_.owner == clazz) match { + case Some(p) => p.tpe + case _ => + val qvar = clazz freshExistential nme.SINGLETON_SUFFIX setInfo singletonBounds(pre) + _capturedParams ::= qvar + debuglog(s"Captured This(${clazz.fullNameString}) seen from $seenFromPrefix: ${qvar.defString}") + qvar.tpe + } + } + protected def captureSkolems(skolems: List[Symbol]) { + for (p <- skolems; if !(capturedSkolems contains p)) { + debuglog(s"Captured $p seen from $seenFromPrefix") + _capturedSkolems ::= p + } + } + + /** Find the type argument in an applied type which corresponds to a type parameter. + * The arguments are required to be related as follows, through intermediary `clazz`. + * An exception will be thrown if this is violated. + * + * @param lhs its symbol is a type parameter of `clazz` + * @param rhs a type application constructed from `clazz` + */ + private def correspondingTypeArgument(lhs: Type, rhs: Type): Type = { + val TypeRef(_, lhsSym, lhsArgs) = lhs + val TypeRef(_, rhsSym, rhsArgs) = rhs + require(lhsSym.owner == rhsSym, s"$lhsSym is not a type parameter of $rhsSym") + + // Find the type parameter position; we'll use the corresponding argument. + // Why are we checking by name rather than by equality? Because for + // reasons which aren't yet fully clear, we can arrive here holding a type + // parameter whose owner is rhsSym, and which shares the name of an actual + // type parameter of rhsSym, but which is not among the type parameters of + // rhsSym. One can see examples of it at SI-4365. + val argIndex = rhsSym.typeParams indexWhere (lhsSym.name == _.name) + // don't be too zealous with the exceptions, see #2641 + if (argIndex < 0 && rhs.parents.exists(typeIsErroneous)) + ErrorType + else { + // It's easy to get here when working on hardcore type machinery (not to + // mention when not doing so, see above) so let's provide a standout error. + def own_s(s: Symbol) = s.nameString + " in " + s.owner.nameString + def explain = + sm"""| sought ${own_s(lhsSym)} + | classSym ${own_s(rhsSym)} + | tparams ${rhsSym.typeParams map own_s mkString ", "} + |""" + + if (argIndex < 0) + abort(s"Something is wrong: cannot find $lhs in applied type $rhs\n" + explain) + else { + val targ = rhsArgs(argIndex) + // @M! don't just replace the whole thing, might be followed by type application + val result = appliedType(targ, lhsArgs mapConserve this) + def msg = s"Created $result, though could not find ${own_s(lhsSym)} among tparams of ${own_s(rhsSym)}" + if (!rhsSym.typeParams.contains(lhsSym)) + devWarning(s"Inconsistent tparam/owner views: had to fall back on names\n$msg\n$explain") + + result + } + } + } + + // 0) @pre: `classParam` is a class type parameter + // 1) Walk the owner chain of `seenFromClass` until we find the class which owns `classParam` + // 2) Take the base type of the prefix at that point with respect to the owning class + // 3) Solve for the type parameters through correspondence with the type args of the base type + // + // Only class type parameters (and not skolems) are considered, because other type parameters + // are not influenced by the prefix through which they are seen. Note that 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. + private def classParameterAsSeen(classParam: Type): Type = { + val TypeRef(_, tparam, _) = classParam + + def loop(pre: Type, clazz: Symbol): Type = { + // have to deconst because it may be a Class[T] + def nextBase = (pre baseType clazz).deconst + //@M! see test pos/tcpoly_return_overriding.scala why mapOver is necessary + if (skipPrefixOf(pre, clazz)) + mapOver(classParam) + else if (!matchesPrefixAndClass(pre, clazz)(tparam.owner)) + loop(nextBase.prefix, clazz.owner) + else nextBase match { + case NoType => loop(NoType, clazz.owner) // backstop for SI-2797, must remove `SingletonType#isHigherKinded` and run pos/t2797.scala to get here. + case applied @ TypeRef(_, _, _) => correspondingTypeArgument(classParam, applied) + case ExistentialType(eparams, qtpe) => captureSkolems(eparams) ; loop(qtpe, clazz) + case t => abort(s"$tparam in ${tparam.owner} cannot be instantiated from ${seenFromPrefix.widen}") + } + } + loop(seenFromPrefix, seenFromClass) + } + + // Does the candidate symbol match the given prefix and class? + // Since pre may be something like ThisType(A) where trait A { self: B => }, + // we have to test the typeSymbol of the widened type, not pre.typeSymbol, or + // B will not be considered. + private def matchesPrefixAndClass(pre: Type, clazz: Symbol)(candidate: Symbol) = + (clazz == candidate) && (pre.widen.typeSymbol isSubClass clazz) + + // Whether the annotation tree currently being mapped over has had a This(_) node rewritten. + private[this] var wroteAnnotation = false + private object annotationArgRewriter extends TypeMapTransformer { + private def matchesThis(thiz: Symbol) = matchesPrefixAndClass(seenFromPrefix, seenFromClass)(thiz) + + // what symbol should really be used? + private def newThis(): Tree = { + wroteAnnotation = true + val presym = seenFromPrefix.widen.typeSymbol + val thisSym = presym.owner.newValue(presym.name.toTermName, presym.pos) setInfo seenFromPrefix + gen.mkAttributedQualifier(seenFromPrefix, thisSym) + } + + /** Rewrite `This` trees in annotation argument trees */ + override def transform(tree: Tree): Tree = super.transform(tree) match { + case This(_) if matchesThis(tree.symbol) => newThis() + case tree => tree + } + } + + // This becomes considerably cheaper if we optimize for the common cases: + // where the prefix is stable and where no This nodes are rewritten. If + // either is true, then we don't need to worry about calling giveup. So if + // the prefix is unstable, use a stack variable to indicate whether the tree + // was touched. This takes us to one allocation per AsSeenFromMap rather + // than an allocation on every call to mapOver, and no extra work when the + // tree only has its types remapped. + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + if (isStablePrefix) + annotationArgRewriter transform tree + else { + val saved = wroteAnnotation + wroteAnnotation = false + try annotationArgRewriter transform tree + finally if (wroteAnnotation) giveup() else wroteAnnotation = saved + } + } + + private def thisTypeAsSeen(tp: ThisType): Type = { + def loop(pre: Type, clazz: Symbol): Type = { + val pre1 = pre match { + case SuperType(thistpe, _) => thistpe + case _ => pre + } + if (skipPrefixOf(pre, clazz)) + mapOver(tp) // TODO - is mapOver necessary here? + else if (!matchesPrefixAndClass(pre, clazz)(tp.sym)) + loop((pre baseType clazz).prefix, clazz.owner) + else if (pre1.isStable) + pre1 + else + captureThis(pre1, clazz) + } + loop(seenFromPrefix, seenFromClass) + } + + private def singleTypeAsSeen(tp: SingleType): Type = { + val SingleType(pre, sym) = tp + + 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 + } + + override def toString = s"AsSeenFromMap($seenFromPrefix, $seenFromClass)" + } + + /** A base class to compute all substitutions */ + abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap { + // OPT this check was 2-3% of some profiles, demoted to -Xdev + if (isDeveloper) 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 + } + + @tailrec private 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) + ) + + def apply(tp0: Type): Type = if (from.isEmpty) tp0 else { + val boundSyms = tp0.boundSyms + val tp1 = if (boundSyms.nonEmpty && (boundSyms exists from.contains)) renameBoundSyms(tp0) else tp0 + val tp = mapOver(tp1) + def substFor(sym: Symbol) = subst(tp, sym, from, to) + + 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) => + val tcon = substFor(sym) + if ((tp eq tcon) || args.isEmpty) tcon + else appliedType(tcon.typeConstructor, args) + case SingleType(NoPrefix, sym) => + substFor(sym) + case ClassInfoType(parents, decls, sym) => + val parents1 = parents mapConserve this + // We don't touch decls here; they will be touched when an enclosing TreeSubstitutor + // transforms the tree that defines them. + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, sym) + case _ => + tp + } + } + } + + /** A map to implement the `substSym` method. */ + class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends SubstMap(from, to) { + def this(pairs: (Symbol, Symbol)*) = this(pairs.toList.map(_._1), pairs.toList.map(_._2)) + + protected def toType(fromtp: Type, sym: Symbol) = fromtp match { + case TypeRef(pre, _, args) => copyTypeRef(fromtp, pre, sym, args) + case SingleType(pre, _) => singleType(pre, sym) + } + @tailrec private 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) + ) + private def substFor(sym: Symbol) = subst(sym, from, to) + + override def apply(tp: Type): Type = ( + if (from.isEmpty) tp + else tp match { + case TypeRef(pre, sym, args) if pre ne NoPrefix => + val newSym = substFor(sym) + // mapOver takes care of subst'ing in args + mapOver ( if (sym eq newSym) tp else copyTypeRef(tp, pre, newSym, args) ) + // assert(newSym.typeParams.length == sym.typeParams.length, "typars mismatch in SubstSymMap: "+(sym, sym.typeParams, newSym, newSym.typeParams)) + case SingleType(pre, sym) if pre ne NoPrefix => + val newSym = substFor(sym) + mapOver( if (sym eq newSym) tp else singleType(pre, newSym) ) + case _ => + super.apply(tp) + } + ) + + object mapTreeSymbols extends TypeMapTransformer { + val strictCopy = newStrictTreeCopier + + def termMapsTo(sym: Symbol) = from indexOf sym match { + case -1 => None + case idx => Some(to(idx)) + } + + // if tree.symbol is mapped to another symbol, passes the new symbol into the + // constructor `trans` and sets the symbol and the type on the resulting tree. + def transformIfMapped(tree: Tree)(trans: Symbol => Tree) = termMapsTo(tree.symbol) match { + case Some(toSym) => trans(toSym) setSymbol toSym setType tree.tpe + case None => tree + } + + // changes trees which refer to one of the mapped symbols. trees are copied before attributes are modified. + override def transform(tree: Tree) = { + // super.transform maps symbol references in the types of `tree`. it also copies trees where necessary. + super.transform(tree) match { + case id @ Ident(_) => + transformIfMapped(id)(toSym => + strictCopy.Ident(id, toSym.name)) + + case sel @ Select(qual, name) => + transformIfMapped(sel)(toSym => + strictCopy.Select(sel, qual, toSym.name)) + + case tree => tree + } + } + } + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + mapTreeSymbols.transform(tree) + } + } + + /** A map to implement the `subst` method. */ + class SubstTypeMap(val from: List[Symbol], val 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.dealias) + } + } + + object ApproximateDependentMap extends TypeMap { + def apply(tp: Type): Type = + if (tp.isImmediatelyDependent) WildcardType + else mapOver(tp) + } + + /** Note: This map is needed even for non-dependent method types, despite what the name might imply. + */ + 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.iterator.filter(_ ne null).toList + + private object StableArgTp { + // type of actual arg corresponding to param -- if the type is stable + def unapply(param: Symbol): Option[Type] = (params indexOf param) match { + case -1 => None + case pid => + val tp = actuals(pid) + if (tp.isStable && (tp.typeSymbol != NothingClass)) Some(tp) + else None + } + } + + /** Return the type symbol for referencing a parameter that's instantiated to an unstable actual argument. + * + * To soundly abstract over an unstable value (x: T) while retaining the most type information, + * use `x.type forSome { type x.type <: T with Singleton}` + * `typeOf[T].narrowExistentially(symbolOf[x])`. + * + * See also: captureThis in AsSeenFromMap. + */ + private def existentialFor(pid: Int) = { + if (existentials(pid) eq null) { + val param = params(pid) + existentials(pid) = ( + param.owner.newExistential(param.name.toTypeName append nme.SINGLETON_SUFFIX, param.pos, param.flags) + setInfo singletonBounds(actuals(pid)) + ) + } + existentials(pid) + } + + private object UnstableArgTp { + // existential quantifier and type of corresponding actual arg with unstable type + def unapply(param: Symbol): Option[(Symbol, Type)] = (params indexOf param) match { + case -1 => None + case pid => + val sym = existentialFor(pid) + Some((sym, sym.tpe_*)) // refers to an actual value, must be kind-* + } + } + + private object StabilizedArgTp { + def unapply(param: Symbol): Option[Type] = + param match { + case StableArgTp(tp) => Some(tp) // (1) + case UnstableArgTp(_, tp) => Some(tp) // (2) + case _ => None + } + } + + /** instantiate `param.type` to the (sound approximation of the) type `T` + * of the actual argument `arg` that was passed in for `param` + * + * (1) If `T` is stable, we can just use that. + * + * (2) SI-3873: it'd be unsound to instantiate `param.type` to an unstable `T`, + * so we approximate to `X forSome {type X <: T with Singleton}` -- we can't soundly say more. + */ + def apply(tp: Type): Type = tp match { + case SingleType(NoPrefix, StabilizedArgTp(tp)) => tp + case _ => mapOver(tp) + } + + //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 StableArgTp(tp) => gen.mkAttributedQualifier(tp, tree.symbol) + case UnstableArgTp(quant, tp) => Ident(quant) copyAttrs tree setType tp + case _ => super.transform(tree) + } + } + treeTrans transform arg + } + } + + /** 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 each occurrence of a type variable to its origin. */ + 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 `filter` method. */ + class FilterTypeCollector(p: Type => Boolean) extends TypeCollector[List[Type]](Nil) { + 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) + } + } + } + + 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, stableOnly = true) orElse { + if (sym.isAliasType) throw missingAliasException + devWarning(s"$pre.$sym no longer exist at 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.hasPackageFlag) 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) { + devWarning(s"adapt to new run failed: pre=$pre pre1=$pre1 sym=$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 ExistentialType(_, _) => mapOver(tp) + case _ => tp + } + } + +} diff --git a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala new file mode 100644 index 0000000000..a062fc8209 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala @@ -0,0 +1,48 @@ +package scala +package reflect +package internal +package tpe + +import scala.collection.mutable.HashSet + +private[internal] trait TypeToStrings { + self: SymbolTable => + + /** The maximum number of recursions allowed in toString + */ + final val maxToStringRecursions = 50 + + private var _toStringRecursions = 0 + def toStringRecursions = _toStringRecursions + def toStringRecursions_=(value: Int) = _toStringRecursions = value + + private var _toStringSubjects = HashSet[Type]() + def toStringSubjects = _toStringSubjects + + protected def typeToString(tpe: Type): String = + // if (toStringSubjects contains tpe) { + // // handles self-referential anonymous classes and who knows what else + // "..." + // } + // else + if (toStringRecursions >= maxToStringRecursions) { + devWarning("Exceeded recursion depth attempting to print " + util.shortClassOfInstance(tpe)) + if (settings.debug) + (new Throwable).printStackTrace + + "..." + } + else + try { + toStringRecursions += 1 + // TODO: study performance impact of this cache + // to quote Jason: + // I'm a little uneasy with the performance impact of the fail-safe. We end up calling Type#toString + // when we generate error messages, including, importantly, errors issued during silent mode that are never issued. + // toStringSubjects += tpe + tpe.safeToString + } finally { + // toStringSubjects -= tpe + toStringRecursions -= 1 + } +} diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 52d1657dc3..d5b5967145 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package transform @@ -16,7 +17,7 @@ trait Erasure { /** 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 { + private def genericCore(tp: Type): Type = tp.dealiasWiden 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. @@ -36,7 +37,7 @@ trait Erasure { * 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 { + def unapply(tp: Type): Option[(Int, Type)] = tp.dealiasWiden match { case TypeRef(_, ArrayClass, List(arg)) => genericCore(arg) match { case NoType => @@ -54,9 +55,13 @@ trait Erasure { } } + /** Arrays despite their finality may turn up as refined type parents, + * e.g. with "tagged types" like Array[Int] with T. + */ protected def unboundedGenericArrayLevel(tp: Type): Int = tp match { - case GenericArray(level, core) if !(core <:< AnyRefClass.tpe) => level - case _ => 0 + case GenericArray(level, core) if !(core <:< AnyRefTpe) => level + case RefinedType(ps, _) if ps.nonEmpty => logResult(s"Unbounded generic level for $tp is")((ps map unboundedGenericArrayLevel).max) + case _ => 0 } // @M #2585 when generating a java generic signature that includes @@ -68,17 +73,8 @@ trait Erasure { // 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 unboxDerivedValueClassMethod(clazz: Symbol): Symbol = - (clazz.info.decl(nme.unbox)) orElse - (clazz.info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse - NoSymbol) - - def underlyingOfValueClass(clazz: Symbol): Type = - clazz.derivedValueClassUnbox.tpe.resultType + protected def rebindInnerClass(pre: Type, cls: Symbol): Type = + if (cls.isTopLevel || cls.isLocalToBlock) pre else cls.owner.tpe_* /** The type of the argument of a value class reference after erasure * This method needs to be called at a phase no later than erasurephase @@ -101,36 +97,40 @@ trait Erasure { def valueClassIsParametric(clazz: Symbol): Boolean = { assert(!phase.erasedTypes) clazz.typeParams contains - clazz.derivedValueClassUnbox.tpe.resultType.normalize.typeSymbol + clazz.derivedValueClassUnbox.tpe.resultType.typeSymbol } 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 + def eraseNormalClassRef(tref: TypeRef): Type = { + val TypeRef(pre, clazz, args) = tref + val pre1 = apply(rebindInnerClass(pre, clazz)) + val args1 = Nil + if ((pre eq pre1) && (args eq args1)) tref // OPT + else typeRef(pre1, clazz, args1) // #2585 + } protected def eraseDerivedValueClassRef(tref: TypeRef): Type = erasedValueClassArg(tref) def apply(tp: Type): Type = tp match { case ConstantType(_) => tp + case st: ThisType if st.sym.isPackageClass => + tp case st: SubType => apply(st.supertype) case tref @ TypeRef(pre, sym, args) => if (sym == ArrayClass) - if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe - else if (args.head.typeSymbol.isBottomClass) ObjectArray + if (unboundedGenericArrayLevel(tp) == 1) ObjectTpe + else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe) 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 == AnyClass || sym == AnyValClass || sym == SingletonClass) ObjectTpe + else if (sym == UnitClass) BoxedUnitTpe else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) - else if (sym.isClass) eraseNormalClassRef(pre, sym) - else apply(sym.info) // alias type or abstract type + else if (sym.isClass) eraseNormalClassRef(tref) + else apply(sym.info asSeenFrom (pre, sym.owner)) // alias type or abstract type case PolyType(tparams, restpe) => apply(restpe) case ExistentialType(tparams, restpe) => @@ -138,18 +138,18 @@ trait Erasure { case mt @ MethodType(params, restpe) => MethodType( cloneSymbolsAndModify(params, ErasureMap.this), - if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass) + if (restpe.typeSymbol == UnitClass) UnitTpe // 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(mt.paramTypes))) case RefinedType(parents, decls) => apply(mergeParents(parents)) - case AnnotatedType(_, atp, _) => + case AnnotatedType(_, atp) => apply(atp) case ClassInfoType(parents, decls, clazz) => ClassInfoType( if (clazz == ObjectClass || isPrimitiveValueClass(clazz)) Nil - else if (clazz == ArrayClass) List(ErasedObject) + else if (clazz == ArrayClass) ObjectTpe :: Nil else removeLaterObjects(parents map this), decls, clazz) case _ => @@ -157,7 +157,7 @@ trait Erasure { } def applyInArray(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) if (sym.isDerivedValueClass) => eraseNormalClassRef(pre, sym) + case tref @ TypeRef(_, sym, _) if sym.isDerivedValueClass => eraseNormalClassRef(tref) case _ => apply(tp) } } @@ -214,9 +214,6 @@ trait Erasure { specialConstructorErasure(clazz, restpe) case ExistentialType(tparams, restpe) => specialConstructorErasure(clazz, restpe) - case RefinedType(parents, decls) => - specialConstructorErasure( - clazz, specialScalaErasure.mergeParents(parents)) case mt @ MethodType(params, restpe) => MethodType( cloneSymbolsAndModify(params, specialScalaErasure), @@ -225,15 +222,7 @@ trait Erasure { typeRef(pre, clazz, List()) case tp => if (!(clazz == ArrayClass || tp.isError)) - // See SI-6556. It seems in some cases the result constructor - // type of an anonymous class is a different version of the class. - // This has nothing to do with value classes per se. - // We simply used a less discriminating transform before, that - // did not look at the cases in detail. - // It seems there is a deeper problem here, which needs - // following up to. But we will not risk regressions - // in 2.10 because of it. - log(s"!!! unexpected constructor erasure $tp for $clazz") + assert(clazz == ArrayClass || tp.isError, s"!!! unexpected constructor erasure $tp for $clazz") specialScalaErasure(tp) } } @@ -263,7 +252,7 @@ trait Erasure { * An intersection such as `Object with Trait` erases to Object. */ def mergeParents(parents: List[Type]): Type = - if (parents.isEmpty) ObjectClass.tpe + if (parents.isEmpty) ObjectTpe else parents.head } @@ -271,11 +260,11 @@ trait Erasure { /** 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. + * are then later unwrapped to the underlying parameter type in phase posterasure. */ object specialScalaErasure extends ScalaErasureMap { override def eraseDerivedValueClassRef(tref: TypeRef): Type = - ErasedValueType(tref) + ErasedValueType(tref.sym, erasedValueClassArg(tref)) } object javaErasure extends JavaErasureMap @@ -291,11 +280,11 @@ trait Erasure { } object boxingErasure extends ScalaErasureMap { - override def eraseNormalClassRef(pre: Type, clazz: Symbol) = - if (isPrimitiveValueClass(clazz)) boxedClass(clazz).tpe - else super.eraseNormalClassRef(pre, clazz) + override def eraseNormalClassRef(tref: TypeRef) = + if (isPrimitiveValueClass(tref.sym)) boxedClass(tref.sym).tpe + else super.eraseNormalClassRef(tref) override def eraseDerivedValueClassRef(tref: TypeRef) = - super.eraseNormalClassRef(tref.pre, tref.sym) + super.eraseNormalClassRef(tref) } /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. @@ -311,7 +300,7 @@ trait Erasure { * - Otherwise, the dominator is the first element of the span. */ def intersectionDominator(parents: List[Type]): Type = { - if (parents.isEmpty) ObjectClass.tpe + if (parents.isEmpty) ObjectTpe else { val psyms = parents map (_.typeSymbol) if (psyms contains ArrayClass) { @@ -333,10 +322,6 @@ trait Erasure { } } - /** 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 @@ -364,8 +349,7 @@ trait Erasure { 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)) + MethodType(List(index.cloneSymbol.setInfo(specialErasure(sym)(index.tpe)), tvar), UnitTpe) } else specialErasure(sym)(tp) } else if ( diff --git a/src/reflect/scala/reflect/internal/transform/PostErasure.scala b/src/reflect/scala/reflect/internal/transform/PostErasure.scala new file mode 100644 index 0000000000..f0c7d0f050 --- /dev/null +++ b/src/reflect/scala/reflect/internal/transform/PostErasure.scala @@ -0,0 +1,19 @@ +package scala.reflect +package internal +package transform + +trait PostErasure { + val global: SymbolTable + import global._ + import definitions._ + + object elimErasedValueType extends TypeMap { + def apply(tp: Type) = tp match { + case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp))) + case ErasedValueType(_, underlying) => underlying + case _ => mapOver(tp) + } + } + + def transformInfo(sym: Symbol, tp: Type) = elimErasedValueType(tp) +} diff --git a/src/reflect/scala/reflect/internal/transform/RefChecks.scala b/src/reflect/scala/reflect/internal/transform/RefChecks.scala index d6108ab665..4ca114e781 100644 --- a/src/reflect/scala/reflect/internal/transform/RefChecks.scala +++ b/src/reflect/scala/reflect/internal/transform/RefChecks.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package transform @@ -10,4 +11,4 @@ trait RefChecks { 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 index 71cc80895d..296ccde443 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package transform @@ -25,17 +26,20 @@ trait Transforms { self: SymbolTable => 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) + private val postErasureLazy = new Lazy(new { val global: Transforms.this.type = self } with PostErasure) def refChecks = refChecksLazy.force def uncurry = uncurryLazy.force def erasure = erasureLazy.force + def postErasure = postErasureLazy.force def transformedType(sym: Symbol) = - erasure.transformInfo(sym, - uncurry.transformInfo(sym, - refChecks.transformInfo(sym, sym.info))) + postErasure.transformInfo(sym, + erasure.transformInfo(sym, + uncurry.transformInfo(sym, + refChecks.transformInfo(sym, sym.info)))) def transformedType(tpe: Type) = - erasure.scalaErasure(uncurry.uncurry(tpe)) + postErasure.elimErasedValueType(erasure.scalaErasure(uncurry.uncurry(tpe))) } diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index 00c7c3de9a..abea8bed9f 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package transform @@ -10,6 +11,14 @@ trait UnCurry { import global._ import definitions._ + /** Note: changing tp.normalize to tp.dealias in this method leads to a single + * test failure: run/t5688.scala, where instead of the expected output + * Vector(ta, tb, tab) + * we instead get + * Vector(tab, tb, tab) + * I think that difference is not the product of sentience but of randomness. + * Let us figure out why it is and then change this method. + */ private def expandAlias(tp: Type): Type = if (!tp.isHigherKinded) tp.normalize else tp val uncurry: TypeMap = new TypeMap { @@ -37,7 +46,7 @@ trait UnCurry { apply(seqType(arg)) case TypeRef(pre, JavaRepeatedParamClass, arg :: Nil) => apply(arrayType( - if (isUnboundedGeneric(arg)) ObjectClass.tpe else arg)) + if (isUnboundedGeneric(arg)) ObjectTpe else arg)) case _ => expandAlias(mapOver(tp)) } diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala new file mode 100644 index 0000000000..10a8b4c812 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -0,0 +1,122 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala +package reflect.internal.util + +import scala.reflect.io.AbstractFile +import java.security.cert.Certificate +import java.security.{ ProtectionDomain, CodeSource } +import java.net.{ URL, URLConnection, URLStreamHandler } +import scala.collection.{ mutable, immutable } + +/** + * A class loader that loads files from a {@link scala.tools.nsc.io.AbstractFile}. + * + * @author Lex Spoon + */ +class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) + extends ClassLoader(parent) + with ScalaClassLoader +{ + protected def classNameToPath(name: String): String = + if (name endsWith ".class") name + else name.replace('.', '/') + ".class" + + protected def findAbstractFile(name: String): AbstractFile = { + var file: AbstractFile = root + val pathParts = name split '/' + + for (dirPart <- pathParts.init) { + file = file.lookupName(dirPart, directory = true) + if (file == null) + return null + } + + file.lookupName(pathParts.last, directory = false) match { + case null => null + case file => file + } + } + + protected def dirNameToPath(name: String): String = + name.replace('.', '/') + + protected def findAbstractDir(name: String): AbstractFile = { + var file: AbstractFile = root + val pathParts = dirNameToPath(name) split '/' + + for (dirPart <- pathParts) { + file = file.lookupName(dirPart, directory = true) + if (file == null) + return null + } + + file + } + + // parent delegation in JCL uses getResource; so either add parent.getResAsStream + // or implement findResource, which we do here as a study in scarlet (my complexion + // after looking at CLs and URLs) + override def findResource(name: String): URL = findAbstractFile(name) match { + case null => null + case file => new URL(null, "repldir:" + file.path, new URLStreamHandler { + override def openConnection(url: URL): URLConnection = new URLConnection(url) { + override def connect() { } + override def getInputStream = file.input + } + }) + } + + // this inverts delegation order: super.getResAsStr calls parent.getRes if we fail + override def getResourceAsStream(name: String) = findAbstractFile(name) match { + case null => super.getResourceAsStream(name) + case file => file.input + } + // ScalaClassLoader.classBytes uses getResAsStream, so we'll try again before delegating + override def classBytes(name: String): Array[Byte] = findAbstractFile(classNameToPath(name)) match { + case null => super.classBytes(name) + case file => file.toByteArray + } + override def findClass(name: String): Class[_] = { + val bytes = classBytes(name) + if (bytes.length == 0) + throw new ClassNotFoundException(name) + else + defineClass(name, bytes, 0, bytes.length, protectionDomain) + } + + lazy val protectionDomain = { + val cl = Thread.currentThread().getContextClassLoader() + val resource = cl.getResource("scala/runtime/package.class") + if (resource == null || resource.getProtocol != "jar") null else { + val s = resource.getPath + val n = s.lastIndexOf('!') + if (n < 0) null else { + val path = s.substring(0, n) + new ProtectionDomain(new CodeSource(new URL(path), null.asInstanceOf[Array[Certificate]]), null, this, null) + } + } + } + + private val packages = mutable.Map[String, Package]() + + override def definePackage(name: String, specTitle: String, specVersion: String, specVendor: String, implTitle: String, implVersion: String, implVendor: String, sealBase: URL): Package = { + throw new UnsupportedOperationException() + } + + override def getPackage(name: String): Package = { + findAbstractDir(name) match { + case null => super.getPackage(name) + case file => packages.getOrElseUpdate(name, { + val ctor = classOf[Package].getDeclaredConstructor(classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[URL], classOf[ClassLoader]) + ctor.setAccessible(true) + ctor.newInstance(name, null, null, null, null, null, null, null, this) + }) + } + } + + override def getPackages(): Array[Package] = + root.iterator.filter(_.isDirectory).map(dir => getPackage(dir.name)).toArray +} diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 2ba15e0776..d128521be8 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -3,7 +3,8 @@ * @author Paul Phillips */ -package scala.reflect.internal.util +package scala +package reflect.internal.util import scala.collection.{ mutable, immutable } import scala.annotation.tailrec @@ -33,15 +34,42 @@ trait Collections { 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 + + /** These are all written in terms of List because we're trying to wring all + * the performance we can and List is used almost exclusively in the compiler, + * but people are branching out in their collections so here's an overload. + */ + final def mforeach[A](xss: List[List[A]])(f: A => Unit) = xss foreach (_ foreach f) + final def mforeach[A](xss: Traversable[Traversable[A]])(f: A => Unit) = xss foreach (_ foreach f) + + /** A version of List#map, specialized for List, and optimized to avoid allocation if `as` is empty */ + final def mapList[A, B](as: List[A])(f: A => B): List[B] = if (as eq Nil) Nil else { + val head = new ::[B](f(as.head), Nil) + var tail: ::[B] = head + var rest = as.tail + while (rest ne Nil) { + val next = new ::(f(rest.head), Nil) + tail.tl = next + tail = next + rest = rest.tail + } + head + } + + final def collectFirst[A, B](as: List[A])(pf: PartialFunction[A, B]): Option[B] = { + @tailrec + def loop(rest: List[A]): Option[B] = rest match { + case Nil => None + case a :: as if pf.isDefinedAt(a) => Some(pf(a)) + case a :: as => loop(as) + } + loop(as) + } final def map2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => C): List[C] = { val lb = new ListBuffer[C] @@ -54,20 +82,60 @@ trait Collections { } lb.toList } + + /** like map2, but returns list `xs` itself - instead of a copy - if function + * `f` maps all elements to themselves. + */ + final def map2Conserve[A <: AnyRef, B](xs: List[A], ys: List[B])(f: (A, B) => A): List[A] = { + // Note to developers: there exists a duplication between this function and `List#mapConserve`. + // If any successful optimization attempts or other changes are made, please rehash them there too. + @tailrec + def loop(mapped: ListBuffer[A], unchanged: List[A], pending0: List[A], pending1: List[B]): List[A] = { + if (pending0.isEmpty || pending1.isEmpty) { + if (mapped eq null) unchanged + else mapped.prependToList(unchanged) + } else { + val head00 = pending0.head + val head01 = pending1.head + val head1 = f(head00, head01) + + if ((head1 eq head00.asInstanceOf[AnyRef])) { + loop(mapped, unchanged, pending0.tail, pending1.tail) + } else { + val b = if (mapped eq null) new ListBuffer[A] else mapped + var xc = unchanged + while ((xc ne pending0) && (xc ne pending1)) { + b += xc.head + xc = xc.tail + } + b += head1 + val tail0 = pending0.tail + val tail1 = pending1.tail + loop(b, tail0, tail0, tail1) + } + } + } + loop(null, xs, xs, ys) + } + 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 lb: ListBuffer[C] = null var ys1 = xs1 var ys2 = xs2 while (!ys1.isEmpty && !ys2.isEmpty) { - lb ++= f(ys1.head, ys2.head) + val cs = f(ys1.head, ys2.head) + if (cs ne Nil) { + if (lb eq null) lb = new ListBuffer[C] + lb ++= cs + } ys1 = ys1.tail ys2 = ys2.tail } - lb.toList + if (lb eq null) Nil else lb.result } final def flatCollect[A, B](elems: List[A])(pf: PartialFunction[A, Traversable[B]]): List[B] = { @@ -141,7 +209,7 @@ trait Collections { ys1 = ys1.tail ys2 = ys2.tail } - buf.result + buf.result() } final def foreach2[A, B](xs1: List[A], xs2: List[B])(f: (A, B) => Unit): Unit = { var ys1 = xs1 @@ -189,18 +257,6 @@ trait Collections { } 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 @@ -216,6 +272,11 @@ trait Collections { true } + final def sequence[A](as: List[Option[A]]): Option[List[A]] = { + if (as.exists (_.isEmpty)) None + else Some(as.flatten) + } + final def transposeSafe[A](ass: List[List[A]]): Option[List[List[A]]] = try { Some(ass.transpose) } catch { @@ -223,5 +284,4 @@ trait Collections { } } -object Collections extends Collections { } - +object Collections extends Collections diff --git a/src/reflect/scala/reflect/internal/util/FreshNameCreator.scala b/src/reflect/scala/reflect/internal/util/FreshNameCreator.scala new file mode 100644 index 0000000000..8442c1015f --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/FreshNameCreator.scala @@ -0,0 +1,28 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.reflect.internal +package util + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicLong +import scala.collection.mutable +import scala.reflect.NameTransformer + +class FreshNameCreator(creatorPrefix: String = "") { + protected val counters = new ConcurrentHashMap[String, AtomicLong]() + + /** + * Create a fresh name with the given prefix. It is guaranteed + * that the returned name has never been returned by a previous + * call to this function (provided the prefix does not end in a digit). + */ + def newName(prefix: String): String = { + val safePrefix = NameTransformer.encode(prefix) + counters.putIfAbsent(safePrefix, new AtomicLong(0)) + val idx = counters.get(safePrefix).incrementAndGet() + s"$creatorPrefix$safePrefix$idx" + } +} diff --git a/src/reflect/scala/reflect/internal/util/HashSet.scala b/src/reflect/scala/reflect/internal/util/HashSet.scala index 4135f3c469..b4178e055d 100644 --- a/src/reflect/scala/reflect/internal/util/HashSet.scala +++ b/src/reflect/scala/reflect/internal/util/HashSet.scala @@ -3,11 +3,11 @@ * @author Martin Odersky */ -package scala.reflect.internal.util +package scala +package reflect +package 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) diff --git a/src/reflect/scala/reflect/internal/util/Origins.scala b/src/reflect/scala/reflect/internal/util/Origins.scala index 3259a12163..2eb4fa29d5 100644 --- a/src/reflect/scala/reflect/internal/util/Origins.scala +++ b/src/reflect/scala/reflect/internal/util/Origins.scala @@ -3,12 +3,11 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package 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. diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala index 8f287a1640..0192d31806 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -1,248 +1,210 @@ /* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky - * */ -package scala.reflect.internal.util +package scala +package reflect +package internal +package util -import scala.reflect.ClassTag -import scala.reflect.internal.FatalError -import scala.reflect.macros.Attachments +/** @inheritdoc */ +class Position extends scala.reflect.api.Position with InternalPositionImpl with DeprecatedPosition { + type Pos = Position + def pos: Position = this + def withPos(newPos: Position): macros.Attachments { type Pos = Position.this.Pos } = newPos + + protected def fail(what: String) = throw new UnsupportedOperationException(s"Position.$what on $this") + + // If scala-refactoring extends Position directly it seems I have no + // choice but to offer all the concrete methods. + def isDefined = false + def isRange = false + def source: SourceFile = NoSourceFile + def start: Int = fail("start") + def point: Int = fail("point") + def end: Int = fail("end") +} object Position { val tabInc = 8 + private def validate[T <: Position](pos: T): T = { + if (pos.isRange) + assert(pos.start <= pos.end, s"bad position: ${pos.show}") + + pos + } + /** 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" + val pos = if (posIn eq null) NoPosition else posIn + val prefix = pos.source match { + case NoSourceFile => "" + case s if shortenFile => s.file.name + ":" + case s => s.file.path + ":" } + prefix + (pos showError msg) } -} - -/** 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. - */ -abstract class Position extends scala.reflect.api.Position { self => - - type Pos = Position - - def pos: Position = this - - def withPos(newPos: Position): Attachments { type Pos = self.Pos } = newPos - - /** An optional value containing the source file referred to by this position, or - * None if not defined. - */ - def source: SourceFile = throw new UnsupportedOperationException(s"Position.source on ${this.getClass}") - - /** 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(s"Position.start on ${this.getClass}") - - /** 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(s"Position.point on ${this.getClass}") - - /** 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(s"Position.end on ${this.getClass}") - - /** 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 + def offset(source: SourceFile, point: Int): Position = validate(new OffsetPosition(source, point)) + def range(source: SourceFile, start: Int, point: Int, end: Int): Position = validate(new RangePosition(source, start, point, end)) + def transparent(source: SourceFile, start: Int, point: Int, end: Int): Position = validate(new TransparentPosition(source, start, point, end)) +} - /** If this is a range position, the offset position of its start. - * Otherwise the position itself - */ - def focusStart: Position = this +class OffsetPosition(sourceIn: SourceFile, pointIn: Int) extends DefinedPosition { + override def isRange = false + override def source = sourceIn + override def point = pointIn + override def start = point + override def end = point +} +class RangePosition(sourceIn: SourceFile, startIn: Int, pointIn: Int, endIn: Int) extends OffsetPosition(sourceIn, pointIn) { + override def isRange = true + override def start = startIn + override def end = endIn +} +class TransparentPosition(sourceIn: SourceFile, startIn: Int, pointIn: Int, endIn: Int) extends RangePosition(sourceIn, startIn, pointIn, endIn) { + override def isTransparent = true +} +case object NoPosition extends UndefinedPosition +case class FakePos(msg: String) extends UndefinedPosition { + override def toString = msg +} - /** If this is a range position, the offset position of its point. - * Otherwise the position itself - */ - def focus: Position = this +sealed abstract class DefinedPosition extends Position { + final override def isDefined = true + override def equals(that: Any) = that match { + case that: DefinedPosition => source.file == that.source.file && start == that.start && point == that.point && end == that.end + case _ => false + } + override def hashCode = Seq[Any](source.file, start, point, end).## + override def toString = ( + if (isRange) s"RangePosition($canonicalPath, $start, $point, $end)" + else s"source-$canonicalPath,line-$line,$pointMessage$point" + ) + private def pointMessage = if (point > source.length) "out-of-bounds-" else "offset=" + private def canonicalPath = source.file.canonicalPath +} - /** If this is a range position, the offset position of its end. - * Otherwise the position itself - */ - def focusEnd: Position = this +sealed abstract class UndefinedPosition extends Position { + final override def isDefined = false + override def isRange = false + override def source = NoSourceFile + override def start = fail("start") + override def point = fail("point") + override def end = fail("end") +} - /** 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 +private[util] trait InternalPositionImpl { + self: Position => - /** 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) + // The methods which would be abstract in Position if it were + // possible to change Position. + def isDefined: Boolean + def isRange: Boolean + def source: SourceFile + def start: Int + def point: Int + def end: Int - /** 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. + /** Map this position to its position in the original source file + * (which may be this position unchanged.) */ - def precedes(pos: Position): Boolean = - isDefined && pos.isDefined && endOrPoint <= pos.startOrPoint + def finalPosition: Pos = source positionInUltimateSource this - /** 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 + def isTransparent = false + def isOffset = isDefined && !isRange + def isOpaqueRange = isRange && !isTransparent + def pointOrElse(alt: Int): Int = if (isDefined) point else alt + def makeTransparent: Position = if (isOpaqueRange) Position.transparent(source, start, point, end) else this - /** 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. + /** Copy a range position with a changed value. */ - def overlaps(pos: Position): Boolean = - isRange && pos.isRange && - ((pos.start < end && start < pos.end) || (start < pos.end && pos.start < end)) + def withStart(start: Int): Position = copyRange(start = start) + def withPoint(point: Int): Position = if (isRange) copyRange(point = point) else Position.offset(source, point) + def withEnd(end: Int): Position = copyRange(end = end) + def withSource(source: SourceFile): Position = copyRange(source = source) + def withShift(shift: Int): Position = Position.range(source, start + shift, point + shift, end + shift) - /** Does this position cover the same range as that position? - * Holds only if both position are ranges + /** Convert a range position to a simple offset. */ - 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 focusStart: Position = if (this.isRange) asOffset(start) else this + def focus: Position = if (this.isRange) asOffset(point) else this + def focusEnd: Position = if (this.isRange) asOffset(end) else this + + /** If you have it in for punctuation you might not like these methods. + * However I think they're aptly named. + * + * | means union + * ^ means "the point" (look, it's a caret) + * |^ means union, taking the point of the rhs + * ^| means union, taking the point of the lhs */ - 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 = { + def |(that: Position, poses: Position*): Position = poses.foldLeft(this | that)(_ | _) + def |(that: Position): Position = this union that + def ^(point: Int): Position = this withPoint point + def |^(that: Position): Position = (this | that) ^ that.point + def ^|(that: Position): Position = (this | that) ^ this.point + + def union(pos: Position): Position = ( + if (!pos.isRange) this + else if (this.isRange) copyRange(start = start min pos.start, end = end max pos.end) + else pos + ) + + def includes(pos: Position): Boolean = isRange && pos.isDefined && start <= pos.start && pos.end <= end + def properlyIncludes(pos: Position): Boolean = includes(pos) && (start < pos.start || pos.end < end) + def precedes(pos: Position): Boolean = bothDefined(pos) && end <= pos.start + def properlyPrecedes(pos: Position): Boolean = bothDefined(pos) && end < pos.start + def sameRange(pos: Position): Boolean = bothRanges(pos) && start == pos.start && end == pos.end + // This works because it's a range position invariant that S1 < E1 and S2 < E2. + // So if S1 < E2 and S2 < E1, then both starts precede both ends, which is the + // necessary condition to establish that there is overlap. + def overlaps(pos: Position): Boolean = bothRanges(pos) && start < pos.end && pos.start < end + + def line: Int = if (hasSource) source.offsetToLine(point) + 1 else 0 + def column: Int = if (hasSource) calculateColumn() else 0 + def lineContent: String = if (hasSource) source.lineToString(line - 1) else "" + def lineCaret: String = if (hasSource) " " * (column - 1) + "^" else "" + @deprecated("use `lineCaret`", since="2.11.0") + def lineCarat: String = lineCaret + + def showError(msg: String): String = { + def escaped(s: String) = { + def u(c: Int) = f"\\u$c%04x" + def uable(c: Int) = (c < 0x20 && c != '\t') || c == 0x7F + if (s exists (c => uable(c))) { + val sb = new StringBuilder + s foreach (c => sb append (if (uable(c)) u(c) else c)) + sb.toString + } else s + } + def errorAt(p: Pos) = { + def where = p.line + def content = escaped(p.lineContent) + def indicator = p.lineCaret + f"$where: $msg%n$content%n$indicator" + } + finalPosition match { + case FakePos(fmsg) => s"$fmsg $msg" + case NoPosition => msg + case pos => errorAt(pos) + } + } + def showDebug: String = toString + def show = ( + if (isOpaqueRange) s"[$start:$end]" + else if (isTransparent) s"<$start:$end>" + else if (isDefined) s"[$point]" + else "[NoPosition]" + ) + + private def asOffset(point: Int): Position = Position.offset(source, point) + private def copyRange(source: SourceFile = source, start: Int = start, point: Int = point, end: Int = end): Position = + Position.range(source, start, point, end) + + private def calculateColumn(): Int = { var idx = source.lineToOffset(source.offsetToLine(point)) var col = 0 while (idx != point) { @@ -251,61 +213,39 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e } col + 1 } + private def hasSource = source ne NoSourceFile + private def bothRanges(that: Position) = isRange && that.isRange + private def bothDefined(that: Position) = isDefined && that.isDefined +} - override def union(pos: Position) = if (pos.isRange) pos else this +/** Holding cell for methods unused and/or unnecessary. */ +private[util] trait DeprecatedPosition { + self: Position => - 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 + @deprecated("use `point`", "2.9.0") // Used in SBT 0.12.4 + def offset: Option[Int] = if (isDefined) Some(point) else None - 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+"]" -} + @deprecated("use `focus`", "2.11.0") + def toSingleLine: Position = this -/** 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) sys.error("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 - } + @deprecated("use `line`", "2.11.0") + def safeLine: Int = line - override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" - override def show = "["+start+":"+end+"]" - private var focusCache: Position = NoPosition -} + @deprecated("use `showDebug`", "2.11.0") + def dbgString: String = showDebug -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+">" + @deprecated("use `finalPosition`", "2.11.0") + def inUltimateSource(source: SourceFile): Position = source positionInUltimateSource this + + @deprecated("use `lineCaret`", since="2.11.0") + def lineWithCarat(maxWidth: Int): (String, String) = ("", "") + + @deprecated("Use `withSource(source)` and `withShift`", "2.11.0") + def withSource(source: SourceFile, shift: Int): Position = this withSource source withShift shift + + @deprecated("Use `start` instead", "2.11.0") + def startOrPoint: Int = if (isRange) start else point + + @deprecated("Use `end` instead", "2.11.0") + def endOrPoint: Int = if (isRange) end else point } diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala new file mode 100644 index 0000000000..63ea6e2c49 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala @@ -0,0 +1,124 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect.internal.util + +import java.lang.{ ClassLoader => JClassLoader } +import java.lang.reflect.{ Constructor, Modifier, Method } +import java.io.{ File => JFile } +import java.net.{ URLClassLoader => JURLClassLoader } +import java.net.URL +import scala.reflect.runtime.ReflectionUtils.unwrapHandler +import ScalaClassLoader._ +import scala.util.control.Exception.{ catching } +import scala.language.implicitConversions +import scala.reflect.{ ClassTag, classTag } + +trait HasClassPath { + def classPathURLs: Seq[URL] +} + +/** A wrapper around java.lang.ClassLoader to lower the annoyance + * of java reflection. + */ +trait ScalaClassLoader extends JClassLoader { + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = { + val saved = contextLoader + try { setContext(this) ; action } + finally setContext(saved) + } + def setAsContext() { setContext(this) } + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = false) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, initialize = true) + + private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = + catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt + Class.forName(path, initialize, this).asInstanceOf[Class[T]] + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = + tryToInitializeClass[AnyRef](path).map(_.newInstance()).orNull + + /** The actual bytes for a class file, or an empty array if it can't be found. */ + def classBytes(className: String): Array[Byte] = classAsStream(className) match { + case null => Array() + case stream => scala.reflect.io.Streamable.bytes(stream) + } + + /** An InputStream representing the given class name, or null if not found. */ + def classAsStream(className: String) = + getResourceAsStream(className.replaceAll("""\.""", "/") + ".class") + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]) { + val clsToRun = tryToInitializeClass(objectName) getOrElse ( + throw new ClassNotFoundException(objectName) + ) + val method = clsToRun.getMethod("main", classOf[Array[String]]) + if (!Modifier.isStatic(method.getModifiers)) + throw new NoSuchMethodException(objectName + ".main is not static") + + try asContext(method.invoke(null, Array(arguments.toArray: AnyRef): _*)) // !!! : AnyRef shouldn't be necessary + catch unwrapHandler({ case ex => throw ex }) + } +} + +/** Methods for obtaining various classloaders. + * appLoader: the application classloader. (Also called the java system classloader.) + * extLoader: the extension classloader. + * bootLoader: the boot classloader. + * contextLoader: the context classloader. + */ +object ScalaClassLoader { + /** Returns loaders which are already ScalaClassLoaders unaltered, + * and translates java.net.URLClassLoaders into scala URLClassLoaders. + * Otherwise creates a new wrapper. + */ + implicit def apply(cl: JClassLoader): ScalaClassLoader = cl match { + case cl: ScalaClassLoader => cl + case cl: JURLClassLoader => new URLClassLoader(cl.getURLs.toSeq, cl.getParent) + case _ => new JClassLoader(cl) with ScalaClassLoader + } + def contextLoader = apply(Thread.currentThread.getContextClassLoader) + def appLoader = apply(JClassLoader.getSystemClassLoader) + def setContext(cl: JClassLoader) = + Thread.currentThread.setContextClassLoader(cl) + def savingContextLoader[T](body: => T): T = { + val saved = contextLoader + try body + finally setContext(saved) + } + + class URLClassLoader(urls: Seq[URL], parent: JClassLoader) + extends JURLClassLoader(urls.toArray, parent) + with ScalaClassLoader + with HasClassPath { + + private var classloaderURLs: Seq[URL] = urls + def classPathURLs: Seq[URL] = classloaderURLs + + /** Override to widen to public */ + override def addURL(url: URL) = { + classloaderURLs :+= url + super.addURL(url) + } + } + + def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = + new URLClassLoader(urls, parent) + + /** True if supplied class exists in supplied path */ + def classExists(urls: Seq[URL], name: String): Boolean = + (fromURLs(urls) tryToLoadClass name).isDefined + + /** Finding what jar a clazz or instance came from */ + def originOfClass(x: Class[_]): Option[URL] = + Option(x.getProtectionDomain.getCodeSource) flatMap (x => Option(x.getLocation)) +} diff --git a/src/reflect/scala/reflect/internal/util/Set.scala b/src/reflect/scala/reflect/internal/util/Set.scala index 36bdb8174a..635bfb05e4 100644 --- a/src/reflect/scala/reflect/internal/util/Set.scala +++ b/src/reflect/scala/reflect/internal/util/Set.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky */ -package scala.reflect.internal.util +package scala +package reflect.internal.util /** A common class for lightweight sets. */ @@ -18,11 +19,8 @@ abstract class Set[T <: AnyRef] { 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 index bc2d0ee4db..a2642628a4 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -4,7 +4,8 @@ */ -package scala.reflect.internal.util +package scala +package reflect.internal.util import scala.reflect.io.{ AbstractFile, VirtualFile } import scala.collection.mutable.ArrayBuffer @@ -15,16 +16,16 @@ 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 content: Array[Char] // normalized, must end in SU + def file : AbstractFile + def isLineBreak(idx: Int): Boolean + def isEndOfLine(idx: Int): Boolean def isSelfContained: Boolean def length : Int - def position(offset: Int) : Position = { + def position(offset: Int): Position = { assert(offset < length, file + ": " + offset + " >= " + length) - new OffsetPosition(this, offset) + Position.offset(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 @@ -34,14 +35,14 @@ abstract class SourceFile { */ 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 "" + def lineToString(index: Int): String = { + val start = lineToOffset(index) + var end = start + while (end < length && !isEndOfLine(end)) end += 1 + new String(content, start, end - start) + } @tailrec final def skipWhitespace(offset: Int): Int = @@ -56,6 +57,7 @@ object NoSourceFile extends SourceFile { def content = Array() def file = NoFile def isLineBreak(idx: Int) = false + def isEndOfLine(idx: Int) = false def isSelfContained = true def length = -1 def offsetToLine(offset: Int) = -1 @@ -81,7 +83,6 @@ object ScriptSourceFile { } 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) @@ -90,19 +91,23 @@ object ScriptSourceFile { stripped } + + def apply(underlying: BatchSourceFile) = { + val headerLen = headerLength(underlying.content) + new ScriptSourceFile(underlying, underlying.content drop headerLen, headerLen) + } } -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 pos.withSource(underlying, start) + else pos withSource underlying withShift start } /** a file whose contents do not change over time */ -class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extends SourceFile { +class BatchSourceFile(val file : AbstractFile, content0: 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) @@ -129,18 +134,30 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend 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) - } + private def charAtIsEOL(idx: Int)(p: Char => Boolean) = { + // don't identify the CR in CR LF as a line break, since LF will do. + def notCRLF0 = content(idx) != CR || !content.isDefinedAt(idx + 1) || content(idx + 1) != LF + + idx < length && notCRLF0 && p(content(idx)) + } + + def isLineBreak(idx: Int) = charAtIsEOL(idx)(isLineBreakChar) + + /** True if the index is included by an EOL sequence. */ + def isEndOfLine(idx: Int) = (content isDefinedAt idx) && PartialFunction.cond(content(idx)) { + case CR | LF => true + } + + /** True if the index is end of an EOL sequence. */ + def isAtEndOfLine(idx: Int) = charAtIsEOL(idx) { + case CR | LF => true + case _ => false + } def calculateLineIndices(cs: Array[Char]) = { val buf = new ArrayBuffer[Int] buf += 0 - for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1 + for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1 buf += cs.length // sentinel, so that findLine below works smoother buf.toArray } @@ -150,15 +167,17 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend private var lastLine = 0 - /** Convert offset to line in this source file - * Lines are numbered from 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) + def findLine(lo: Int, hi: Int, mid: Int): Int = ( + if (mid < lo || hi < mid) mid // minimal sanity check - as written this easily went into infinite loopyland + else 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/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index cbd27b0d65..905f1bf26e 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -1,4 +1,5 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util import scala.collection.mutable @@ -102,8 +103,8 @@ quant) for ((_, q) <- qs if q.underlying == q; r <- q :: q.children.toList if r.prefix.nonEmpty) yield r - private def showPercent(x: Double, base: Double) = - if (base == 0) "" else f" (${x / base * 100}%2.1f%%)" + private def showPercent(x: Long, base: Long) = + if (base == 0) "" else f" (${x.toDouble / base.toDouble * 100}%2.1f%%)" /** The base trait for quantities. * Quantities with non-empty prefix are printed in the statistics info. @@ -132,6 +133,12 @@ quant) if (this.value < that.value) -1 else if (this.value > that.value) 1 else 0 + override def equals(that: Any): Boolean = + that match { + case that: Counter => (this compare that) == 0 + case _ => false + } + override def hashCode = value override def toString = value.toString } @@ -155,7 +162,7 @@ quant) value = value0 + underlying.value - uvalue0 } override def toString = - value + showPercent(value, underlying.value) + value + showPercent(value.toLong, underlying.value.toLong) } class Timer(val prefix: String, val phases: Seq[String]) extends Quantity { @@ -183,6 +190,12 @@ quant) if (this.specificNanos < that.specificNanos) -1 else if (this.specificNanos > that.specificNanos) 1 else 0 + override def equals(that: Any): Boolean = + that match { + case that: StackableTimer => (this compare that) == 0 + case _ => false + } + override def hashCode = specificNanos.## override def toString = s"${super.toString} aggregate, ${show(specificNanos)} specific" } @@ -257,7 +270,6 @@ quant) def enabled = _enabled def enabled_=(cond: Boolean) = { if (cond && !_enabled) { - val test = new Timer("", Nil) val start = System.nanoTime() var total = 0L for (i <- 1 to 10000) { diff --git a/src/reflect/scala/reflect/internal/util/StringOps.scala b/src/reflect/scala/reflect/internal/util/StringOps.scala index 8f6c409e0b..efb8126ff0 100644 --- a/src/reflect/scala/reflect/internal/util/StringOps.scala +++ b/src/reflect/scala/reflect/internal/util/StringOps.scala @@ -6,8 +6,12 @@ ** |/ ** \* */ +package scala +package reflect +package internal +package util -package scala.reflect.internal.util +import scala.compat.Platform.EOL /** This object provides utility methods to extract elements * from Strings. @@ -16,24 +20,32 @@ package scala.reflect.internal.util * @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 oempty(xs: String*) = xs filterNot (x => x == null || x == "") + def ojoin(xs: String*): String = oempty(xs: _*) mkString " " + def longestCommonPrefix(xs: List[String]): String = xs match { + case Nil => "" + case w :: Nil => w + case _ => + def lcp(ss: List[String]): String = { + val w :: ws = ss + if (w == "") "" + else if (ws exists (s => s == "" || (s charAt 0) != (w charAt 0))) "" + else w.substring(0, 1) + lcp(ss map (_ substring 1)) + } + lcp(xs) } - 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 + /** Like String#trim, but trailing whitespace only. + */ + def trimTrailingSpace(s: String): String = { + var end = s.length + while (end > 0 && s.charAt(end - 1).isWhitespace) + end -= 1 - s.substring(0, idx + 1) - } + if (end == s.length) s + else s.substring(0, end) } + /** Breaks the string into lines and strips each line before reassembling. */ + def trimAllTrailingSpace(s: String): String = s.lines map trimTrailingSpace mkString EOL def decompose(str: String, sep: Char): List[String] = { def ws(start: Int): List[String] = @@ -49,14 +61,6 @@ trait StringOps { 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) @@ -65,10 +69,6 @@ trait StringOps { 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 { @@ -81,9 +81,6 @@ trait StringOps { } /** Turns a count into a friendly English description if n<=4. - * - * @param n ... - * @return ... */ def countAsString(n: Int): String = n match { diff --git a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala index e7579229b2..e622e78d57 100644 --- a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala +++ b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package util @@ -6,7 +7,7 @@ trait StripMarginInterpolator { def stringContext: StringContext /** - * A safe combination of `[[scala.collection.immutable.StringLike#stripMargin]] + * A safe combination of [[scala.collection.immutable.StringLike#stripMargin]] * and [[scala.StringContext#raw]]. * * The margin of each line is defined by whitespace leading up to a '|' character. diff --git a/src/reflect/scala/reflect/internal/util/TableDef.scala b/src/reflect/scala/reflect/internal/util/TableDef.scala index 8e2bcc2ff7..8708442c85 100644 --- a/src/reflect/scala/reflect/internal/util/TableDef.scala +++ b/src/reflect/scala/reflect/internal/util/TableDef.scala @@ -1,38 +1,27 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util import TableDef._ import scala.language.postfixOps /** A class for representing tabular data in a way that preserves - * its inner beauty. See Exceptional for an example usage. + * its inner beauty. * 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. - */ + // These operators are about all there is to it. + /** Appends a column to the table. */ 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. - */ + // 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 defaultSep(index: Int) = if (index > (cols.size - 2)) "" else " " + def sepAfter(i: Int): String = 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)) @@ -59,20 +48,12 @@ class TableDef[T](_cols: Column[T]*) { 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(", ", ", ")") diff --git a/src/reflect/scala/reflect/internal/util/ThreeValues.scala b/src/reflect/scala/reflect/internal/util/ThreeValues.scala index f89bd9e199..18410510cb 100644 --- a/src/reflect/scala/reflect/internal/util/ThreeValues.scala +++ b/src/reflect/scala/reflect/internal/util/ThreeValues.scala @@ -1,4 +1,5 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util /** A simple three value type for booleans with an unknown value */ object ThreeValues { diff --git a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala index fa83f70f3a..e4a6503184 100644 --- a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala +++ b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -1,4 +1,5 @@ -package scala.reflect.internal +package scala +package reflect.internal package util import scala.collection.{ mutable, immutable } @@ -12,13 +13,9 @@ trait TraceSymbolActivity { if (enabled && 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) { @@ -44,39 +41,8 @@ trait TraceSymbolActivity { } } - /** 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 lazy val erasurePhase = findPhaseWithName("erasure") + private def signature(id: Int) = enteringPhase(erasurePhase)(allSymbols(id).defString) private def dashes(s: Any): String = ("" + s) map (_ => '-') private def show(s1: Any, ss: Any*) { @@ -119,17 +85,9 @@ trait TraceSymbolActivity { } println("\n") } - private def showFreq[T, U](xs: Traversable[T])(groupFn: T => U, showFn: U => String = (x: U) => "" + x) = { + private def showFreq[T, U](xs: Traversable[T])(groupFn: T => U, showFn: U => String) = { 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 (!enabled) return diff --git a/src/reflect/scala/reflect/internal/util/TriState.scala b/src/reflect/scala/reflect/internal/util/TriState.scala new file mode 100644 index 0000000000..4074d974d2 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/TriState.scala @@ -0,0 +1,28 @@ +package scala +package reflect +package internal +package util + +import scala.language.implicitConversions + +import TriState._ + +/** A simple true/false/unknown value, for those days when + * true and false don't quite partition the space. + */ +final class TriState private (val value: Int) extends AnyVal { + def isKnown = this != Unknown + def booleanValue = this match { + case True => true + case False => false + case _ => sys.error("Not a Boolean value") + } +} + +object TriState { + implicit def booleanToTriState(b: Boolean): TriState = if (b) True else False + + val Unknown = new TriState(-1) + val False = new TriState(0) + val True = new TriState(1) +} diff --git a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala index fc12e31360..a9a7c7780d 100644 --- a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -4,7 +4,7 @@ package reflect.internal.util import java.lang.ref.{WeakReference, ReferenceQueue} import scala.annotation.tailrec import scala.collection.generic.Clearable -import scala.collection.mutable.{Set => mSet} +import scala.collection.mutable.{Set => MSet} /** * A HashSet where the elements are stored weakly. Elements in this set are elligible for GC if no other @@ -16,8 +16,8 @@ import scala.collection.mutable.{Set => mSet} * This set implmeentation is not in general thread safe without external concurrency control. However it behaves * properly when GC concurrently collects elements in this set. */ -final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: Double) extends Set[A] with Function1[A, Boolean] with mSet[A] { - +final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: Double) extends Set[A] with Function1[A, Boolean] with MSet[A] { + import WeakHashSet._ def this() = this(initialCapacity = WeakHashSet.defaultInitialCapacity, loadFactor = WeakHashSet.defaultLoadFactor) @@ -47,7 +47,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D candidate *= 2 } candidate - } + } /** * the underlying table of entries which is an array of Entry linked lists @@ -65,7 +65,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D * find the bucket associated with an elements's hash code */ private[this] def bucketFor(hash: Int): Int = { - // spread the bits around to try to avoid accidental collisions using the + // spread the bits around to try to avoid accidental collisions using the // same algorithm as java.util.HashMap var h = hash h ^= h >>> 20 ^ h >>> 12 @@ -98,7 +98,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D def poll(): Entry[A] = queue.poll().asInstanceOf[Entry[A]] @tailrec - def queueLoop { + def queueLoop(): Unit = { val stale = poll() if (stale != null) { val bucket = bucketFor(stale.hash) @@ -109,11 +109,11 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D linkedListLoop(null, table(bucket)) - queueLoop + queueLoop() } } - - queueLoop + + queueLoop() } /** @@ -123,7 +123,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D val oldTable = table table = new Array[Entry[A]](oldTable.size * 2) threshhold = computeThreshHold - + @tailrec def tableLoop(oldBucket: Int): Unit = if (oldBucket < oldTable.size) { @tailrec @@ -225,7 +225,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D def +=(elem: A) = this + elem // from scala.reflect.interanl.Set - override def addEntry(x: A) { this += x } + override def addEntry(x: A) { this += x } // remove an element from this set and return this set override def -(elem: A): this.type = elem match { @@ -274,6 +274,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D override def foreach[U](f: A => U): Unit = iterator foreach f + // It has the `()` because iterator runs `removeStaleEntries()` override def toList(): List[A] = iterator.toList // Iterator over all the elements in this set in no particular order @@ -292,7 +293,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D */ private[this] var entry: Entry[A] = null - /** + /** * the element that will be the result of the next call to next() */ private[this] var lookaheadelement: A = null.asInstanceOf[A] @@ -339,7 +340,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D * the entries must be stable. If any are garbage collected during validation * then an assertion may inappropriately fire. */ - def fullyValidate { + def fullyValidate: Unit = { var computedCount = 0 var bucket = 0 while (bucket < table.size) { @@ -383,34 +384,9 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D * Number of buckets in the table */ def bucketsCount: Int = table.size - - /** - * Number of buckets that don't hold anything - */ - def emptyBucketsCount = bucketsCount - fullBucketsCount - - /** - * Number of elements that are in collision. Useful for diagnosing performance issues. - */ - def collisionsCount = size - (fullBucketsCount - collisionBucketsCount) - - /** - * A map from a count of elements to the number of buckets with that count - */ - def elementCountDistribution = table map linkedListSize groupBy identity map {case (size, list) => (size, list.size)} - - private def linkedListSize(entry: Entry[A]) = { - var e = entry - var count = 0 - while (e != null) { - count += 1 - e = e.tail - } - count - } } - private[util] def diagnostics = new Diagnostics + private[util] def diagnostics = new Diagnostics } /** @@ -427,4 +403,4 @@ object WeakHashSet { val defaultLoadFactor = .75 def apply[A <: AnyRef](initialCapacity: Int = WeakHashSet.defaultInitialCapacity, loadFactor: Double = WeakHashSet.defaultLoadFactor) = new WeakHashSet[A](initialCapacity, defaultLoadFactor) -} +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/util/package.scala b/src/reflect/scala/reflect/internal/util/package.scala index 6d77235db6..3618c150ca 100644 --- a/src/reflect/scala/reflect/internal/util/package.scala +++ b/src/reflect/scala/reflect/internal/util/package.scala @@ -1,7 +1,42 @@ -package scala.reflect +package scala +package reflect package internal +import scala.language.existentials // SI-6541 + package object util { + import StringOps.longestCommonPrefix + + // An allocation-avoiding reusable instance of the so-common List(Nil). + val ListOfNil: List[List[Nothing]] = Nil :: Nil + + def andFalse(body: Unit): Boolean = false + + // Shorten a name like Symbols$FooSymbol to FooSymbol. + private def shortenName(name: String): String = { + if (name == "") return "" + val segments = (name split '$').toList + val last = segments.last + + if (last.length == 0) + segments takeRight 2 mkString "$" + else + last + } + + def shortClassOfInstance(x: AnyRef): String = shortClass(x.getClass) + def shortClass(clazz: Class[_]): String = { + val name: String = (clazz.getName split '.').last + def isModule = name endsWith "$" // object + def isAnon = (name split '$').last forall (_.isDigit) // anonymous class + + if (isModule) + (name split '$' filterNot (_ == "")).last + "$" + else if (isAnon) + clazz.getSuperclass :: clazz.getInterfaces.toList map (c => shortClass(c)) mkString " with " + else + shortenName(name) + } /** * Adds the `sm` String interpolator to a [[scala.StringContext]]. */ diff --git a/src/reflect/scala/reflect/io/AbstractFile.scala b/src/reflect/scala/reflect/io/AbstractFile.scala index 15befb67f1..ac1159b2ac 100644 --- a/src/reflect/scala/reflect/io/AbstractFile.scala +++ b/src/reflect/scala/reflect/io/AbstractFile.scala @@ -4,19 +4,21 @@ */ -package scala.reflect +package scala +package reflect package io -import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream } +import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream, ByteArrayOutputStream } import java.io.{ File => JFile } import java.net.URL import scala.collection.mutable.ArrayBuffer +import scala.reflect.internal.util.Statistics /** * An abstraction over files for use in the reflection/compiler libraries. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' - * + * * @author Philippe Altherr * @version 1.0, 23/03/2004 */ @@ -27,7 +29,7 @@ object AbstractFile { /** * If the specified File exists and is a regular file, returns an - * abstract regular file backed by it. Otherwise, returns <code>null</code>. + * abstract regular file backed by it. Otherwise, returns `null`. */ def getFile(file: File): AbstractFile = if (file.isFile) new PlainFile(file) else null @@ -38,10 +40,7 @@ object AbstractFile { /** * 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 ... + * backed by it. Otherwise, returns `null`. */ def getDirectory(file: File): AbstractFile = if (file.isDirectory) new PlainFile(file) @@ -51,15 +50,14 @@ object AbstractFile { /** * 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 ... + * `null`. */ def getURL(url: URL): AbstractFile = { if (url == null || !Path.isExtensionJarOrZip(url.getPath)) null else ZipArchive fromURL url } + + def getResources(url: URL): AbstractFile = ZipArchive fromManifestURL url } /** @@ -80,12 +78,12 @@ object AbstractFile { * </p> * <p> * The interface does <b>not</b> allow to access the content. - * The class <code>symtab.classfile.AbstractFileReader</code> accesses + * The class `symtab.classfile.AbstractFileReader` 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>. + * all other cases, the class `SourceFile` is used, which honors + * `global.settings.encoding.value`. * </p> - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ abstract class AbstractFile extends Iterable[AbstractFile] { @@ -116,7 +114,10 @@ abstract class AbstractFile extends Iterable[AbstractFile] { def underlyingSource: Option[AbstractFile] = None /** Does this abstract file denote an existing file? */ - def exists: Boolean = (file eq null) || file.exists + def exists: Boolean = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileExistsCount) + (file eq null) || file.exists + } /** Does this abstract file represent something which can contain classfiles? */ def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip")) @@ -130,6 +131,9 @@ abstract class AbstractFile extends Iterable[AbstractFile] { /** Is this abstract file a directory? */ def isDirectory: Boolean + /** Does this abstract file correspond to something on-disk? */ + def isVirtual: Boolean = false + /** Returns the time that this abstract file was last modified. */ def lastModified: Long @@ -148,7 +152,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { 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 + * warning: use `Global.getSourceFile()` to use the proper * encoding when converting to the char array. */ @throws(classOf[IOException]) @@ -159,24 +163,36 @@ abstract class AbstractFile extends Iterable[AbstractFile] { @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 + sizeOption match { + case Some(size) => + var rest = size + 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 + case None => + val out = new ByteArrayOutputStream() + var c = in.read() + while(c != -1) { + out.write(c) + c = in.read() + } + in.close() + out.toByteArray() } - 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 + * name. If there is no such file, returns `null`. The argument + * `directory` tells whether to look for a directory or * a regular file. */ def lookupName(name: String, directory: Boolean): AbstractFile @@ -186,19 +202,6 @@ abstract class AbstractFile extends Iterable[AbstractFile] { */ 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. */ @@ -243,7 +246,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { */ 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) + fileOrSubdirectoryNamed(name, isDir = false) } /** @@ -252,7 +255,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { */ 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) + fileOrSubdirectoryNamed(name, isDir = true) } protected def unsupported(): Nothing = unsupported(null) diff --git a/src/reflect/scala/reflect/io/Directory.scala b/src/reflect/scala/reflect/io/Directory.scala index c040d1eac5..2b965e6d69 100644 --- a/src/reflect/scala/reflect/io/Directory.scala +++ b/src/reflect/scala/reflect/io/Directory.scala @@ -6,7 +6,8 @@ ** |/ ** \* */ -package scala.reflect +package scala +package reflect package io import java.io.{ File => JFile } @@ -14,12 +15,10 @@ import java.io.{ File => JFile } * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object Directory { - import scala.util.Properties.{ tmpDir, userHome, userDir } + import scala.util.Properties.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 @@ -30,20 +29,18 @@ object Directory { path.createDirectory() } } -import Path._ /** An abstraction for directories. * * @author Paul Phillips * @since 2.8 - * + * * ''Note: This is library is considered experimental and should not be used unless you know what you are doing.'' */ 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. @@ -60,7 +57,6 @@ class Directory(jfile: JFile) extends Path(jfile) { 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 @@ -70,10 +66,4 @@ class Directory(jfile: JFile) extends Path(jfile) { 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/reflect/io/File.scala b/src/reflect/scala/reflect/io/File.scala index 736ba5d51e..a9c6807e88 100644 --- a/src/reflect/scala/reflect/io/File.scala +++ b/src/reflect/scala/reflect/io/File.scala @@ -7,13 +7,16 @@ \* */ -package scala.reflect +package scala +package reflect package io import java.io.{ FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter, - BufferedInputStream, BufferedOutputStream, IOException, PrintStream, PrintWriter, Closeable => JCloseable } -import java.io.{ File => JFile } + BufferedInputStream, BufferedOutputStream, IOException, PrintStream, PrintWriter, Closeable => JCloseable, + File => JFile +} + import java.nio.channels.{ Channel, FileChannel } import scala.io.Codec import scala.language.{reflectiveCalls, implicitConversions} @@ -22,8 +25,7 @@ import scala.language.{reflectiveCalls, implicitConversions} */ object File { def pathSeparator = java.io.File.pathSeparator - def separator = java.io.File.separator - + 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. @@ -32,41 +34,7 @@ object File { 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 @@ -76,19 +44,17 @@ import Path._ * * @author Paul Phillips * @since 2.8 - * + * * ''Note: This is library is considered experimental and should not be used unless you know what you are doing.'' */ 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 @@ -99,26 +65,22 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w /** 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(): BufferedWriter = bufferedWriter(append = 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 = { @@ -127,12 +89,6 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w 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 _) @@ -150,39 +106,6 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w 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 = { diff --git a/src/reflect/scala/reflect/io/FileOperationException.scala b/src/reflect/scala/reflect/io/FileOperationException.scala index 13a1322798..fdfe0234e0 100644 --- a/src/reflect/scala/reflect/io/FileOperationException.scala +++ b/src/reflect/scala/reflect/io/FileOperationException.scala @@ -7,7 +7,8 @@ \* */ -package scala.reflect +package scala +package reflect package io /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ case class FileOperationException(msg: String) extends RuntimeException(msg) diff --git a/src/reflect/scala/reflect/io/IOStats.scala b/src/reflect/scala/reflect/io/IOStats.scala new file mode 100644 index 0000000000..71f8be330d --- /dev/null +++ b/src/reflect/scala/reflect/io/IOStats.scala @@ -0,0 +1,32 @@ +package scala +package reflect.io + +import scala.reflect.internal.util.Statistics + +// Due to limitations in the Statistics machinery, these are only +// reported if this patch is applied. +// +// --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +// +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala +// @@ -109,7 +109,7 @@ quant) +// * Quantities with non-empty prefix are printed in the statistics info. +// */ +// trait Quantity { +// - if (enabled && prefix.nonEmpty) { +// + if (prefix.nonEmpty) { +// val key = s"${if (underlying != this) underlying.prefix else ""}/$prefix" +// qs(key) = this +// } +// @@ -243,7 +243,7 @@ quant) +// * +// * to remove all Statistics code from build +// */ +// - final val canEnable = _enabled +// + final val canEnable = true // _enabled +// +// We can commit this change as the first diff reverts a fix for an IDE memory leak. +private[io] object IOStats { + val fileExistsCount = Statistics.newCounter("# File.exists calls") + val fileIsDirectoryCount = Statistics.newCounter("# File.isDirectory calls") + val fileIsFileCount = Statistics.newCounter("# File.isFile calls") +} diff --git a/src/reflect/scala/reflect/io/NoAbstractFile.scala b/src/reflect/scala/reflect/io/NoAbstractFile.scala index 8c88d3abf6..18eca7698d 100644 --- a/src/reflect/scala/reflect/io/NoAbstractFile.scala +++ b/src/reflect/scala/reflect/io/NoAbstractFile.scala @@ -3,15 +3,15 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package io import java.io.InputStream -import java.io.{ File => JFile } /** A distinguished object so you can avoid both null * and Option. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object NoAbstractFile extends AbstractFile { @@ -19,9 +19,10 @@ object NoAbstractFile extends AbstractFile { def container: AbstractFile = this def create(): Unit = ??? def delete(): Unit = ??? - def file: JFile = null + def file: java.io.File = null def input: InputStream = null def isDirectory: Boolean = false + override def isVirtual: Boolean = true def iterator: Iterator[AbstractFile] = Iterator.empty def lastModified: Long = 0L def lookupName(name: String, directory: Boolean): AbstractFile = null @@ -30,4 +31,5 @@ object NoAbstractFile extends AbstractFile { def output: java.io.OutputStream = null def path: String = "" override def toByteArray = Array[Byte]() + override def toString = "<no file>" } diff --git a/src/reflect/scala/reflect/io/Path.scala b/src/reflect/scala/reflect/io/Path.scala index 36fdc04db4..15fce953f2 100644 --- a/src/reflect/scala/reflect/io/Path.scala +++ b/src/reflect/scala/reflect/io/Path.scala @@ -3,16 +3,17 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package io import java.io.{ FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter, - BufferedInputStream, BufferedOutputStream, RandomAccessFile } -import java.io.{ File => JFile } + BufferedInputStream, BufferedOutputStream, RandomAccessFile, File => JFile } import java.net.{ URI, URL } import scala.util.Random.alphanumeric import scala.language.implicitConversions +import scala.reflect.internal.util.Statistics /** An abstraction for filesystem paths. The differences between * Path, File, and Directory are primarily to communicate intent. @@ -27,7 +28,7 @@ import scala.language.implicitConversions * * @author Paul Phillips * @since 2.8 - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object Path { @@ -49,32 +50,28 @@ object Path { 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) + def apply(jfile: JFile): Path = try { + def isFile = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileIsFileCount) + jfile.isFile + } + + def isDirectory = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileIsDirectoryCount) + jfile.isDirectory + } + + if (isFile) new File(jfile) + else if (isDirectory) new Directory(jfile) else new Path(jfile) + } catch { case ex: SecurityException => new Path(jfile) } /** Avoiding any shell/path issues by only using alphanumerics. */ private[io] def randomPrefix = alphanumeric take 6 mkString "" @@ -84,19 +81,13 @@ import Path._ /** The Path constructor is private so we can enforce some * semantics regarding how a Path might relate to the world. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ 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) @@ -104,6 +95,7 @@ class Path private[io] (val jfile: JFile) { 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. */ @@ -136,7 +128,6 @@ class Path private[io] (val jfile: JFile) { 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) = { @@ -152,9 +143,8 @@ class Path private[io] (val jfile: JFile) { 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 */ @@ -185,10 +175,6 @@ class Path private[io] (val jfile: JFile) { 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 @@ -212,23 +198,28 @@ class Path private[io] (val jfile: JFile) { // 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 exists = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileExistsCount) + try jfile.exists() catch { case ex: SecurityException => false } + } - def isFile = jfile.isFile() - def isDirectory = jfile.isDirectory() + def isFile = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileIsFileCount) + try jfile.isFile() catch { case ex: SecurityException => false } + } + def isDirectory = { + if (Statistics.canEnable) Statistics.incCounter(IOStats.fileIsDirectoryCount) + try jfile.isDirectory() catch { case ex: SecurityException => jfile.getPath == "." } + } 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 @@ -248,7 +239,6 @@ class Path private[io] (val jfile: 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! @@ -270,16 +260,6 @@ class Path private[io] (val jfile: JFile) { 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 diff --git a/src/reflect/scala/reflect/io/PlainFile.scala b/src/reflect/scala/reflect/io/PlainFile.scala index 82b0568657..8f24d84488 100644 --- a/src/reflect/scala/reflect/io/PlainFile.scala +++ b/src/reflect/scala/reflect/io/PlainFile.scala @@ -3,23 +3,12 @@ * @author Martin Odersky */ - -package scala.reflect +package scala +package reflect package io import java.io.{ FileInputStream, FileOutputStream, IOException } -import PartialFunction._ -/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ -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 -} + /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { override def isDirectory = true @@ -28,7 +17,7 @@ class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) { } /** This class implements an abstract file backed by a File. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class PlainFile(val givenPath: Path) extends AbstractFile { @@ -54,7 +43,7 @@ class PlainFile(val givenPath: Path) extends AbstractFile { override def sizeOption = Some(givenPath.length.toInt) override def toString = path - override def hashCode(): Int = fpath.hashCode + override def hashCode(): Int = fpath.hashCode() override def equals(that: Any): Boolean = that match { case x: PlainFile => fpath == x.fpath case _ => false @@ -68,8 +57,14 @@ class PlainFile(val givenPath: Path) extends AbstractFile { /** Returns all abstract subfiles of this abstract directory. */ def iterator: Iterator[AbstractFile] = { + // Optimization: Assume that the file was not deleted and did not have permissions changed + // between the call to `list` and the iteration. This saves a call to `exists`. + def existsFast(path: Path) = path match { + case (_: Directory | _: io.File) => true + case _ => path.exists + } if (!isDirectory) Iterator.empty - else givenPath.toDirectory.list filter (_.exists) map (new PlainFile(_)) + else givenPath.toDirectory.list filter existsFast map (new PlainFile(_)) } /** @@ -77,10 +72,6 @@ class PlainFile(val givenPath: Path) extends AbstractFile { * 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 diff --git a/src/reflect/scala/reflect/io/Streamable.scala b/src/reflect/scala/reflect/io/Streamable.scala index 61ec8a4c23..aa47947672 100644 --- a/src/reflect/scala/reflect/io/Streamable.scala +++ b/src/reflect/scala/reflect/io/Streamable.scala @@ -3,7 +3,8 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package io import java.net.{ URI, URL } @@ -17,14 +18,14 @@ import Path.fail * * @author Paul Phillips * @since 2.8 - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ 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. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ trait Bytes { @@ -69,7 +70,7 @@ object Streamable { } /** For objects which can be viewed as Chars. - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ trait Chars extends Bytes { @@ -81,7 +82,6 @@ object Streamable { */ 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) @@ -89,8 +89,7 @@ object Streamable { /** Obtains an InputStreamReader wrapped around a FileInputStream. */ - def reader(): InputStreamReader = reader(creationCodec) - def reader(codec: Codec): InputStreamReader = new InputStreamReader(inputStream, codec.charSet) + def reader(codec: Codec): InputStreamReader = new InputStreamReader(inputStream(), codec.charSet) /** Wraps a BufferedReader around the result of reader(). */ @@ -108,7 +107,10 @@ object Streamable { /** Convenience function to import entire file into a String. */ def slurp(): String = slurp(creationCodec) - def slurp(codec: Codec) = chars(codec).mkString + def slurp(codec: Codec) = { + val src = chars(codec) + try src.mkString finally src.close() // Always Be Closing + } } /** Call a function on something Closeable, finally closing it. */ @@ -117,7 +119,9 @@ object Streamable { finally stream.close() def bytes(is: => InputStream): Array[Byte] = - (new Bytes { def inputStream() = is }).toByteArray + (new Bytes { + def inputStream() = is + }).toByteArray() def slurp(is: => InputStream)(implicit codec: Codec): String = new Chars { def inputStream() = is } slurp codec diff --git a/src/reflect/scala/reflect/io/VirtualDirectory.scala b/src/reflect/scala/reflect/io/VirtualDirectory.scala index 78713c2ae0..aa6ceaa09f 100644 --- a/src/reflect/scala/reflect/io/VirtualDirectory.scala +++ b/src/reflect/scala/reflect/io/VirtualDirectory.scala @@ -2,7 +2,8 @@ * Copyright 2005-2013 LAMP/EPFL */ -package scala.reflect +package scala +package reflect package io import scala.collection.mutable @@ -11,7 +12,7 @@ import scala.collection.mutable * An in-memory directory. * * @author Lex Spoon - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory]) @@ -26,22 +27,23 @@ extends AbstractFile { def container = maybeContainer.get def isDirectory = true - var lastModified: Long = System.currentTimeMillis + override def isVirtual = true + val 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 } + def create() { unsupported() } /** Delete the underlying file or directory (recursively). */ - def delete() { unsupported } + 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 + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = unsupported() private val files = mutable.Map.empty[String, AbstractFile] @@ -53,20 +55,20 @@ extends AbstractFile { (files get name filter (_.isDirectory == directory)).orNull override def fileNamed(name: String): AbstractFile = - Option(lookupName(name, false)) getOrElse { + Option(lookupName(name, directory = false)) getOrElse { val newFile = new VirtualFile(name, path+'/'+name) files(name) = newFile newFile } override def subdirectoryNamed(name: String): AbstractFile = - Option(lookupName(name, true)) getOrElse { + Option(lookupName(name, directory = true)) getOrElse { val dir = new VirtualDirectory(name, Some(this)) files(name) = dir dir } def clear() { - files.clear(); + files.clear() } } diff --git a/src/reflect/scala/reflect/io/VirtualFile.scala b/src/reflect/scala/reflect/io/VirtualFile.scala index 95f4429fad..45f38db745 100644 --- a/src/reflect/scala/reflect/io/VirtualFile.scala +++ b/src/reflect/scala/reflect/io/VirtualFile.scala @@ -3,18 +3,17 @@ * @author Martin Odersky */ - -package scala.reflect +package scala +package reflect package io -import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, InputStream, OutputStream } -import java.io.{ File => JFile } +import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, InputStream, OutputStream, File => JFile } /** This class implements an in-memory file. * * @author Philippe Altherr * @version 1.0, 23/03/2004 - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ class VirtualFile(val name: String, override val path: String) extends AbstractFile { @@ -33,20 +32,16 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF case _ => false } - //######################################################################## - // Private data private var content = Array.emptyByteArray - //######################################################################## - // Public Methods def absolute = this /** Returns null. */ - final def file: JFile = null + def file: JFile = null - override def sizeOption: Option[Int] = Some(content.size) + override def sizeOption: Option[Int] = Some(content.length) - def input : InputStream = new ByteArrayInputStream(content); + def input : InputStream = new ByteArrayInputStream(content) override def output: OutputStream = { new ByteArrayOutputStream() { @@ -62,10 +57,16 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF /** Is this abstract file a directory? */ def isDirectory: Boolean = false + /** @inheritdoc */ + override def isVirtual: Boolean = true + + // private var _lastModified: Long = 0 + // _lastModified + /** 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 + // !!! Except it doesn't - it's private and never set - so I replaced it + // with constant 0 to save the field. + def lastModified: Long = 0 /** Returns all abstract subfiles of this abstract directory. */ def iterator: Iterator[AbstractFile] = { @@ -74,20 +75,16 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF } /** Does this abstract file denote an existing file? */ - def create() { unsupported } + def create() { unsupported() } /** Delete the underlying file or directory (recursively). */ - def delete() { unsupported } + 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 + "'") @@ -97,7 +94,5 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF /** Returns an abstract file with the given name. It does not * check that it exists. */ - def lookupNameUnchecked(name: String, directory: Boolean) = unsupported - - //######################################################################## + def lookupNameUnchecked(name: String, directory: Boolean) = unsupported() } diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 3b57721e89..8260189459 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -3,14 +3,17 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package io import java.net.URL -import java.io.{ IOException, InputStream, ByteArrayInputStream } +import java.io.{ IOException, InputStream, ByteArrayInputStream, FilterInputStream } import java.io.{ File => JFile } import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream } +import java.util.jar.Manifest import scala.collection.{ immutable, mutable } +import scala.collection.convert.WrapAsScala.asScalaIterator import scala.annotation.tailrec /** An abstraction for zip files and streams. Everything is written the way @@ -20,13 +23,10 @@ import scala.annotation.tailrec * @author Philippe Altherr (original version) * @author Paul Phillips (this one) * @version 2.0, - * + * * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ 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. @@ -41,10 +41,11 @@ object ZipArchive { * @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) + def fromManifestURL(url: URL): AbstractFile = new ManifestResources(url) + + private def dirName(path: String) = splitPath(path, front = true) + private def baseName(path: String) = splitPath(path, front = 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 @@ -65,13 +66,13 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq 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 + 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 => @@ -79,7 +80,6 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq else Iterator(f) } } - def deepIterator = walkIterator(iterator) /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) { // have to keep this name for compat with sbt's compiler-interface @@ -126,7 +126,11 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ final class FileZipArchive(file: JFile) extends ZipArchive(file) { def iterator: Iterator[Entry] = { - val zipFile = new ZipFile(file) + val zipFile = try { + new ZipFile(file) + } catch { + case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe) + } val root = new DirEntry("/") val dirs = mutable.HashMap[String, DirEntry]("/" -> root) val enum = zipFile.entries() @@ -232,3 +236,63 @@ final class URLZipArchive(val url: URL) extends ZipArchive(null) { case _ => false } } + +final class ManifestResources(val url: URL) extends ZipArchive(null) { + def iterator = { + val root = new DirEntry("/") + val dirs = mutable.HashMap[String, DirEntry]("/" -> root) + val manifest = new Manifest(input) + val iter = manifest.getEntries().keySet().iterator().filter(_.endsWith(".class")).map(new ZipEntry(_)) + + while (iter.hasNext) { + val zipEntry = iter.next() + val dir = getDir(dirs, zipEntry) + if (zipEntry.isDirectory) dir + else { + class FileEntry() extends Entry(zipEntry.getName) { + override def lastModified = zipEntry.getTime() + override def input = resourceInputStream(path) + override def sizeOption = None + } + val f = new FileEntry() + dir.entries(f.name) = f + } + } + + try root.iterator + finally dirs.clear() + } + + def name = path + def path: String = { + val s = url.getPath + val n = s.lastIndexOf('!') + s.substring(0, n) + } + def input = url.openStream() + def lastModified = + try url.openConnection().getLastModified() + catch { case _: IOException => 0 } + + override def canEqual(other: Any) = other.isInstanceOf[ManifestResources] + override def hashCode() = url.hashCode + override def equals(that: Any) = that match { + case x: ManifestResources => url == x.url + case _ => false + } + + private def resourceInputStream(path: String): InputStream = { + new FilterInputStream(null) { + override def read(): Int = { + if(in == null) in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); + if(in == null) throw new RuntimeException(path + " not found") + super.read(); + } + + override def close(): Unit = { + super.close(); + in = null; + } + } + } +} diff --git a/src/reflect/scala/reflect/macros/Aliases.scala b/src/reflect/scala/reflect/macros/Aliases.scala index 92d76f4370..64819a8601 100644 --- a/src/reflect/scala/reflect/macros/Aliases.scala +++ b/src/reflect/scala/reflect/macros/Aliases.scala @@ -1,14 +1,15 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that defines shorthands for the + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that defines shorthands for the * most frequently used types and functions of the underlying compiler universe. */ trait Aliases { - self: Context => + self: blackbox.Context => /** The type of symbols representing declarations. */ type Symbol = universe.Symbol @@ -39,10 +40,16 @@ trait Aliases { /** The type of tree modifiers. */ type Modifiers = universe.Modifiers - /** The type of compilation runs. */ + /** The type of compilation runs. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type Run = universe.Run - /** The type of compilation units. */ + /** The type of compilation units. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type CompilationUnit = universe.CompilationUnit /** Expr wraps an abstract syntax tree and tags it with its type. */ @@ -109,4 +116,9 @@ trait Aliases { * Shortcut for `implicitly[TypeTag[T]].tpe` */ def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe + + /** + * Type symbol of `x` as derived from a type tag. + */ + def symbolOf[T: WeakTypeTag]: universe.TypeSymbol = universe.symbolOf[T] } diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index 007df3b6e2..5ccdc15a03 100644 --- a/src/reflect/scala/reflect/macros/Attachments.scala +++ b/src/reflect/scala/reflect/macros/Attachments.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -40,6 +41,10 @@ abstract class Attachments { self => def get[T: ClassTag]: Option[T] = (all filter matchesTag[T]).headOption.asInstanceOf[Option[T]] + /** Check underlying payload contains an instance of type `T`. */ + def contains[T: ClassTag]: Boolean = + !isEmpty && (all exists matchesTag[T]) + /** Creates a copy of this attachment with the payload slot of T added/updated with the provided value. * Replaces an existing payload of the same type, if exists. */ @@ -52,13 +57,14 @@ abstract class Attachments { self => if (newAll.isEmpty) pos.asInstanceOf[Attachments { type Pos = self.Pos }] else new NonemptyAttachments[Pos](this.pos, newAll) } + + def isEmpty: Boolean = true } // SI-7018: This used to be an inner class of `Attachments`, but that led to a memory leak in the // IDE via $outer pointers. -// Forward compatibility note: This class used to be Attachments$NonemptyAttachments. -// However it's private, therefore it transcends the compatibility policy for 2.10.x. private final class NonemptyAttachments[P >: Null](override val pos: P, override val all: Set[Any]) extends Attachments { type Pos = P def withPos(newPos: Pos) = new NonemptyAttachments(newPos, all) + override def isEmpty: Boolean = false } diff --git a/src/reflect/scala/reflect/macros/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala index a4ad71c348..69ede42cc7 100644 --- a/src/reflect/scala/reflect/macros/Enclosures.scala +++ b/src/reflect/scala/reflect/macros/Enclosures.scala @@ -1,66 +1,114 @@ -package scala.reflect +package scala +package reflect package macros +import scala.language.existentials // SI-6541 + /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that exposes - * enclosing trees (method, class, compilation unit and currently compiled application), + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that exposes + * enclosing trees (method, class, compilation unit and currently compiled macro application), * the enclosing position of the macro expansion, as well as macros and implicits * that are currently in-flight. + * + * Starting from Scala 2.11.0, the APIs to get the trees enclosing by the current macro application are deprecated, + * and the reasons for that are two-fold. Firstly, we would like to move towards the philosophy of locally-expanded macros, + * as it has proven to be important for understanding of code. Secondly, within the current architecture of scalac, + * we are unable to have c.enclosingTree-style APIs working robustly. Required changes to the typechecker would greatly + * exceed the effort that we would like to expend on this feature given the existence of more pressing concerns at the moment. + * This is somewhat aligned with the overall evolution of macros during the 2.11 development cycle, where we played with + * `c.introduceTopLevel` and `c.introduceMember`, but at the end of the day decided to reject them. + * + * If you're relying on the now deprecated APIs, consider using the new [[c.internal.enclosingOwner]] method that can be used to obtain + * the names of enclosing definitions. Alternatively try reformulating your macros in terms of completely local expansion + * and/or joining a discussion of a somewhat related potential language feature at [[https://groups.google.com/forum/#!topic/scala-debate/f4CLmYShX6Q]]. + * We also welcome questions and suggestions on our mailing lists, where we would be happy to further discuss this matter. */ trait Enclosures { - self: Context => + self: blackbox.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 + def 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. + * 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] - - /** Information about one of the currently considered implicit candidates. - * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, - * hence implicit searches can recursively trigger other implicit searches. - * - * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. - * If we're in an implicit macro being expanded, it's included in this list. - * - * 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)] + def enclosingMacros: List[blackbox.Context] /** Tries to guess a position for the enclosing application. - * But that is simple, right? Just dereference ``pos'' of ``macroApplication''? Not really. + * 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. + * Surprisingly, quite often we can do this by navigation the `enclosingMacros` stack. */ - val enclosingPosition: Position + def enclosingPosition: Position /** Tree that corresponds to the enclosing method, or EmptyTree if not applicable. + * @see [[scala.reflect.macros.Enclosures]] */ - val enclosingMethod: Tree + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingMethod: Tree /** Tree that corresponds to the enclosing class, or EmptyTree if not applicable. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingClass: Tree + + /** Tree that corresponds to the enclosing DefDef tree. + * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingDef: universe.DefDef + + /** Tree that corresponds to the enclosing Template tree. + * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingTemplate: universe.Template + + /** Tree that corresponds to the enclosing ImplDef tree (i.e. either ClassDef or ModuleDef). + * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] */ - val enclosingClass: Tree + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingImpl: universe.ImplDef + + /** Tree that corresponds to the enclosing PackageDef tree. + * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingPackage: universe.PackageDef /** Compilation unit that contains this macro application. + * @see [[scala.reflect.macros.Enclosures]] */ - val enclosingUnit: CompilationUnit + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingUnit: CompilationUnit /** Compilation run that contains this macro application. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + def enclosingRun: Run + + /** Indicates than one of the enclosure methods failed to find a tree + * of required type among enclosing trees. + * @see [[scala.reflect.macros.Enclosures]] */ - val enclosingRun: Run -}
\ No newline at end of file + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") + case class EnclosureException(expected: Class[_], enclosingTrees: List[Tree]) + extends Exception(s"Couldn't find a tree of type $expected among enclosing trees $enclosingTrees") +} diff --git a/src/reflect/scala/reflect/macros/Evals.scala b/src/reflect/scala/reflect/macros/Evals.scala index 37680c219b..68e07dd319 100644 --- a/src/reflect/scala/reflect/macros/Evals.scala +++ b/src/reflect/scala/reflect/macros/Evals.scala @@ -1,14 +1,15 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that provides + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that provides * a facility to evaluate trees. */ trait Evals { - self: Context => + self: blackbox.Context => /** Takes a typed wrapper for a tree of type `T` and evaluates it to a value of type `T`. * @@ -16,13 +17,13 @@ trait Evals { * permitted by the shape of the arguments. * * Known issues: because of [[https://issues.scala-lang.org/browse/SI-5748 https://issues.scala-lang.org/browse/SI-5748]] - * trees being evaluated first need to undergo `resetAllAttrs`. Resetting symbols and types + * trees being evaluated first need to undergo `untypecheck`. Resetting symbols and types * mutates the tree in place, therefore the conventional approach is to `duplicate` the tree first. * * {{{ * scala> def impl(c: Context)(x: c.Expr[String]) = { - * | val x1 = c.Expr[String](c.resetAllAttrs(x.tree.duplicate)) - * | println(s"compile-time value is: \${c.eval(x1)}") + * | val x1 = c.Expr[String](c.untypecheck(x.tree.duplicate)) + * | println(s"compile-time value is: ${c.eval(x1)}") * | x * | } * impl: (c: Context)(x: c.Expr[String])c.Expr[String] @@ -54,4 +55,4 @@ trait Evals { * refers to a runtime value `x`, which is unknown at compile time. */ def eval[T](expr: Expr[T]): T -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/macros/ExprUtils.scala b/src/reflect/scala/reflect/macros/ExprUtils.scala index 458cde9692..c438653c92 100644 --- a/src/reflect/scala/reflect/macros/ExprUtils.scala +++ b/src/reflect/scala/reflect/macros/ExprUtils.scala @@ -1,51 +1,65 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that defines shorthands for the + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that defines shorthands for the * most common `Expr`-creating functions. */ trait ExprUtils { - self: Context => + self: blackbox.Context => /** Shorthand for `Literal(Constant(null))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literalNull: Expr[Null] /** Shorthand for `Literal(Constant(()))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literalUnit: Expr[Unit] /** Shorthand for `Literal(Constant(true))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literalTrue: Expr[Boolean] /** Shorthand for `Literal(Constant(false))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literalFalse: Expr[Boolean] /** Shorthand for `Literal(Constant(x: Boolean))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Boolean): Expr[Boolean] /** Shorthand for `Literal(Constant(x: Byte))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Byte): Expr[Byte] /** Shorthand for `Literal(Constant(x: Short))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Short): Expr[Short] /** Shorthand for `Literal(Constant(x: Int))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Int): Expr[Int] /** Shorthand for `Literal(Constant(x: Long))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Long): Expr[Long] /** Shorthand for `Literal(Constant(x: Float))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Float): Expr[Float] /** Shorthand for `Literal(Constant(x: Double))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Double): Expr[Double] /** Shorthand for `Literal(Constant(x: String))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: String): Expr[String] /** Shorthand for `Literal(Constant(x: Char))` in the underlying `universe`. */ + @deprecated("Use quasiquotes instead", "2.11.0") def literal(x: Char): Expr[Char] } diff --git a/src/reflect/scala/reflect/macros/FrontEnds.scala b/src/reflect/scala/reflect/macros/FrontEnds.scala index 67b24088b5..a770f325b2 100644 --- a/src/reflect/scala/reflect/macros/FrontEnds.scala +++ b/src/reflect/scala/reflect/macros/FrontEnds.scala @@ -1,15 +1,16 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that * provides facilities to communicate with the compiler's front end * (emit warnings, errors and other sorts of messages). */ trait FrontEnds { - self: Context => + self: blackbox.Context => /** For sending a message which should not be labeled as a warning/error, * but also shouldn't require -verbose to be visible. @@ -44,4 +45,4 @@ trait FrontEnds { * Use `enclosingPosition` if you're in doubt what position to pass to `pos`. */ def abort(pos: Position, msg: String): Nothing -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/macros/Infrastructure.scala b/src/reflect/scala/reflect/macros/Infrastructure.scala index 99706e84fe..0f2d9ce4cf 100644 --- a/src/reflect/scala/reflect/macros/Infrastructure.scala +++ b/src/reflect/scala/reflect/macros/Infrastructure.scala @@ -1,14 +1,15 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that * provides facilities to communicate with the compiler's infrastructure. */ trait Infrastructure { - self: Context => + self: blackbox.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. @@ -22,4 +23,4 @@ trait Infrastructure { /** Exposes current classpath. */ def classPath: List[java.net.URL] -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/macros/Internals.scala b/src/reflect/scala/reflect/macros/Internals.scala new file mode 100644 index 0000000000..75164344da --- /dev/null +++ b/src/reflect/scala/reflect/macros/Internals.scala @@ -0,0 +1,79 @@ +package scala +package reflect +package macros + +/** + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * @see [[scala.reflect.api.Internals]] + */ +trait Internals { + self: blackbox.Context => + + /** @see [[scala.reflect.api.Internals]] */ + val internal: ContextInternalApi + + /** @see [[scala.reflect.api.Internals]] */ + trait ContextInternalApi extends universe.MacroInternalApi { + /** Symbol associated with the innermost enclosing lexical context. + * Walking the owner chain of this symbol will reveal information about more and more enclosing contexts. + */ + def enclosingOwner: Symbol + + /** Functions that are available during [[transform]]. + * @see [[transform]] + */ + trait TransformApi { + /** Calls the current transformer on the given tree. + * Current transformer = argument to the `transform` call. + */ + def recur(tree: Tree): Tree + + /** Calls the default transformer on the given tree. + * Default transformer = recur into tree's children and assemble the results. + */ + def default(tree: Tree): Tree + } + + /** Transforms a given tree using the provided function. + * @see [[TransformApi]] + */ + // TODO: explore a more concise notation that Denys and I discussed today + // when transformer is PartialFunction[Tree, Tree]] and TransformApi is passed magically + // also cf. https://github.com/dsl-paradise/dsl-paradise + def transform(tree: Tree)(transformer: (Tree, TransformApi) => Tree): Tree + + /** Functions that are available during [[typingTransform]]. + * @see [[typingTransform]] + */ + trait TypingTransformApi extends TransformApi { + /** Temporarily pushes the given symbol onto the owner stack, creating a new local typer, + * invoke the given operation and then rollback the changes to the owner stack. + */ + def atOwner[T](owner: Symbol)(op: => T): T + + /** Temporarily pushes the given tree onto the recursion stack, and then calls `atOwner(symbol)(trans)`. + */ + def atOwner[T](tree: Tree, owner: Symbol)(op: => T): T + + /** Returns the symbol currently on the top of the owner stack. + * If we're not inside any `atOwner` call, then macro application's context owner will be used. + */ + def currentOwner: Symbol + + /** Typechecks the given tree using the local typer currently on the top of the owner stack. + * If we're not inside any `atOwner` call, then macro application's callsite typer will be used. + */ + def typecheck(tree: Tree): Tree + } + + /** Transforms a given tree using the provided function. + * @see [[TypingTransformApi]] + */ + def typingTransform(tree: Tree)(transformer: (Tree, TypingTransformApi) => Tree): Tree + + /** Transforms a given tree at a given owner using the provided function. + * @see [[TypingTransformApi]] + */ + def typingTransform(tree: Tree, owner: Symbol)(transformer: (Tree, TypingTransformApi) => Tree): Tree + } +} diff --git a/src/reflect/scala/reflect/macros/Names.scala b/src/reflect/scala/reflect/macros/Names.scala index 8bbaa5f848..4f3448e1ed 100644 --- a/src/reflect/scala/reflect/macros/Names.scala +++ b/src/reflect/scala/reflect/macros/Names.scala @@ -1,23 +1,56 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that - * provides functions that generate unique names. + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that + * provides functions that generate fresh names. + * + * In the current implementation, fresh names are more or less unique in the sense that + * within the same compilation run they are guaranteed not to clash with: + * 1) Results of past and future invocations of functions of `freshName` family + * 2) User-defined or macro-generated names that don't contain dollar symbols + * 3) Macro-generated names that are created by concatenating names from the first, second and third categories + * + * Uniqueness of fresh names across compilation runs is not guaranteed, but that's something + * that we would like to improve upon in future releases. See [[https://issues.scala-lang.org/browse/SI-6879]] for more information. + * + * @define freshNameNoParams + * Creates a string that represents a more or less unique name. + * Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names. + * + * @define freshNameStringParam + * Creates a string that represents a more or less unique name having a given prefix. + * Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names. + * + * @define freshNameNameParam + * Creates a more or less unique name having a given name as a prefix and + * having the same flavor (term name or type name) as the given name. + * Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names. */ trait Names { - self: Context => + self: blackbox.Context => - /** Creates a unique string. */ + /** $freshNameNoParams */ + @deprecated("Use freshName instead", "2.11.0") def fresh(): String - /** Creates a unique string having a given prefix. */ + /** $freshNameStringParam */ + @deprecated("Use freshName instead", "2.11.0") def fresh(name: String): String - /** Creates a unique name having a given name as a prefix and - * having the same flavor (term name or type name) as the given name. - */ + /** $freshNameNameParam */ + @deprecated("Use freshName instead", "2.11.0") def fresh[NameType <: Name](name: NameType): NameType + + /** $freshNameNoParams */ + def freshName(): String + + /** $freshNameStringParam */ + def freshName(name: String): String + + /** $freshNameNameParam */ + def freshName[NameType <: Name](name: NameType): NameType } diff --git a/src/reflect/scala/reflect/macros/Parsers.scala b/src/reflect/scala/reflect/macros/Parsers.scala index 93a763792c..720b754649 100644 --- a/src/reflect/scala/reflect/macros/Parsers.scala +++ b/src/reflect/scala/reflect/macros/Parsers.scala @@ -1,14 +1,15 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that * exposes functions to parse strings with Scala code into trees. */ trait Parsers { - self: Context => + self: blackbox.Context => /** Parses a string with a Scala expression into an abstract syntax tree. * Only works for expressions, i.e. parsing a package declaration will fail. @@ -19,4 +20,4 @@ trait Parsers { /** Indicates an error during [[scala.reflect.macros.Parsers#parse]]. */ -case class ParseException(val pos: scala.reflect.api.Position, val msg: String) extends Exception(msg) +case class ParseException(pos: scala.reflect.api.Position, msg: String) extends Exception(msg) diff --git a/src/reflect/scala/reflect/macros/Reifiers.scala b/src/reflect/scala/reflect/macros/Reifiers.scala index 3db7b9af02..e35a5c8622 100644 --- a/src/reflect/scala/reflect/macros/Reifiers.scala +++ b/src/reflect/scala/reflect/macros/Reifiers.scala @@ -1,26 +1,27 @@ -package scala.reflect +package scala +package reflect package macros /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that * exposes functions to save reflection artifacts for runtime. */ trait Reifiers { - self: Context => + self: blackbox.Context => /** 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''. + * 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 ``universe.treeBuild.mkRuntimeUniverseRef''. - * Possible values for ``mirror'' include ``EmptyTree'' (in that case the reifier will automatically pick an appropriate mirror). + * The produced tree will be bound to the specified `universe` and `mirror`. + * Possible values for `universe` include `universe.internal.gen.mkRuntimeUniverseRef`. + * 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). + * 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: + * 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. @@ -39,7 +40,7 @@ trait Reifiers { * * 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. + * * 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 @@ -48,13 +49,13 @@ trait Reifiers { 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''. + * 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). + * 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 @@ -86,10 +87,10 @@ trait Reifiers { * Such errors represent one of the standard ways for reification to go wrong, e.g. * an attempt to create a `TypeTag` from a weak type. */ -case class ReificationException(val pos: scala.reflect.api.Position, val msg: String) extends Exception(msg) +case class ReificationException(pos: scala.reflect.api.Position, msg: String) extends Exception(msg) /** Indicates an unexpected expected error during one of the `reifyXXX` methods in [[scala.reflect.macros.Reifiers]]. * Such errors wrap random crashes in reification logic and are distinguished from expected [[scala.reflect.macros.ReificationException]]s * so that the latter can be reported as compilation errors, while the former manifest themselves as compiler crashes. */ -case class UnexpectedReificationException(val pos: scala.reflect.api.Position, val msg: String, val cause: Throwable = null) extends Exception(msg, cause) +case class UnexpectedReificationException(pos: scala.reflect.api.Position, msg: String, cause: Throwable = null) extends Exception(msg, cause) diff --git a/src/reflect/scala/reflect/macros/TreeBuilder.scala b/src/reflect/scala/reflect/macros/TreeBuilder.scala deleted file mode 100644 index 204dc40858..0000000000 --- a/src/reflect/scala/reflect/macros/TreeBuilder.scala +++ /dev/null @@ -1,72 +0,0 @@ -package scala.reflect -package macros - -/** - * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> - * - * A helper available in [[scala.reflect.macros.Universe]] that defines shorthands for the - * most common tree-creating functions. - */ -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 - - /** A tree that refers to the runtime reflexive universe, ``scala.reflect.runtime.universe''. */ - def mkRuntimeUniverseRef: Tree -} diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala index d36636a6d2..d0dccb469d 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -1,16 +1,17 @@ -package scala.reflect +package scala +package reflect package macros +import scala.reflect.internal.{Mode => InternalMode} + /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * A slice of [[scala.reflect.macros.Context the Scala macros context]] that + * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that * partially exposes the type checker to macro writers. */ trait Typers { - self: Context => - - import universe._ + self: blackbox.Context => /** 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. @@ -22,21 +23,45 @@ trait Typers { * 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] + def openMacros: List[blackbox.Context] - /** Information about one of the currently considered implicit candidates. - * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, - * hence implicit searches can recursively trigger other implicit searches. - * - * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. - * If we're in an implicit macro being expanded, it's included in this list. - * - * 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. + /** Represents mode of operations of the typechecker underlying `c.typecheck` calls. + * Is necessary since the shape of the typechecked tree alone is not enough to guess how it should be typechecked. + * Can be EXPRmode (typecheck as a term), TYPEmode (typecheck as a type) or PATTERNmode (typecheck as a pattern). + */ + // I'd very much like to make use of https://github.com/dsl-paradise/dsl-paradise here! + type TypecheckMode + + /** Indicates that an argument to `c.typecheck` should be typechecked as a term. + * This is the default typechecking mode in Scala 2.11 and the only one supported in Scala 2.10. + */ + val TERMmode: TypecheckMode + + /** Indicates that an argument to `c.typecheck` should be typechecked as a type. + */ + val TYPEmode: TypecheckMode + + /** Indicates that an argument to `c.typecheck` should be typechecked as a pattern. + */ + val PATTERNmode: TypecheckMode + + /** @see `scala.reflect.macros.TypecheckException` + */ + type TypecheckException = scala.reflect.macros.TypecheckException + + /** @see `scala.reflect.macros.TypecheckException` */ - def openImplicits: List[(Type, Tree)] + val TypecheckException = scala.reflect.macros.TypecheckException - /** Typechecks the provided tree against the expected type `pt` in the macro callsite context. + /** @see `Typers.typecheck` + */ + @deprecated("Use `c.typecheck` instead", "2.11.0") + def typeCheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = + typecheck(tree, TERMmode, pt, silent, withImplicitViewsDisabled, withMacrosDisabled) + + /** Typechecks the provided tree against the expected type `pt` in the macro callsite context + * under typechecking mode specified in `mode` with [[EXPRmode]] being default. + * This populates symbols and types of the tree and possibly transforms it to reflect certain desugarings. * * If `silent` is false, `TypecheckException` 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. @@ -49,7 +74,7 @@ trait Typers { * * @throws [[scala.reflect.macros.TypecheckException]] */ - def typeCheck(tree: Tree, pt: Type = WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree + def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = universe.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. @@ -57,7 +82,7 @@ trait Typers { * If `silent` is false, `TypecheckException` 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. + * Unlike in `typecheck`, `silent` is true by default. * * @throws [[scala.reflect.macros.TypecheckException]] */ @@ -69,27 +94,35 @@ trait Typers { * If `silent` is false, `TypecheckException` 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. + * Unlike in `typecheck`, `silent` is true by default. * * @throws [[scala.reflect.macros.TypecheckException]] */ def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, 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. + /** Recursively resets locally defined symbols and types in a given tree. + * WARNING: Don't use this API, go for [[untypecheck]] instead. */ - def resetAllAttrs(tree: Tree): Tree + @deprecated("Use `c.untypecheck` instead", "2.11.0") + def resetLocalAttrs(tree: Tree): Tree - /** Recursively resets locally defined symbols and types in a given tree. + /** In the current implementation of Scala's reflection API, untyped trees (also known as parser trees or unattributed trees) + * are observationally different from typed trees (also known as typer trees, typechecked trees or attributed trees), * - * 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. + * Usually, if some compiler API takes a tree, then both untyped and typed trees will do. However in some cases, + * only untyped or only typed trees are appropriate. For example, [[eval]] only accepts untyped trees and one can only splice + * typed trees inside typed trees. Therefore in the current reflection API, there is a need in functions + * that go back and forth between untyped and typed trees. For this we have [[typecheck]] and `untypecheck`. + * + * Note that `untypecheck` is currently afflicted by https://issues.scala-lang.org/browse/SI-5464, + * which makes it sometimes corrupt trees so that they don't make sense anymore. Unfortunately, there's no workaround for that. + * We plan to fix this issue soon, but for now please keep it in mind. + * + * @see [[http://stackoverflow.com/questions/20936509/scala-macros-what-is-the-difference-between-typed-aka-typechecked-an-untyped]] */ - def resetLocalAttrs(tree: Tree): Tree + def untypecheck(tree: Tree): Tree } /** Indicates an error during one of the methods in [[scala.reflect.macros.Typers]]. */ -case class TypecheckException(val pos: scala.reflect.api.Position, val msg: String) extends Exception(msg) +case class TypecheckException(pos: scala.reflect.api.Position, msg: String) extends Exception(msg) diff --git a/src/reflect/scala/reflect/macros/Universe.scala b/src/reflect/scala/reflect/macros/Universe.scala index 4e76f7c408..1eb67215bb 100644 --- a/src/reflect/scala/reflect/macros/Universe.scala +++ b/src/reflect/scala/reflect/macros/Universe.scala @@ -1,6 +1,10 @@ -package scala.reflect +package scala +package reflect package macros +import scala.language.implicitConversions +import scala.language.higherKinds + /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * @@ -16,111 +20,96 @@ package macros */ abstract class Universe extends scala.reflect.api.Universe { - /** A factory that encapsulates common tree-building functions. - * @group Macros - */ - val treeBuild: TreeBuilder { val global: Universe.this.type } + /** @inheritdoc */ + override type Internal <: MacroInternalApi - /** The API of reflection artifacts that support [[scala.reflect.macros.Attachments]]. - * These artifacts are trees and symbols. - * @group Macros - */ - trait AttachableApi { - /** The attachment of the reflection artifact. */ - def attachments: Attachments { type Pos = Position } + /** @inheritdoc */ + trait MacroInternalApi extends InternalApi { internal => - /** Updates the attachment with the payload slot of T added/updated with the provided value. - * Replaces an existing payload of the same type, if exists. - * Returns the reflection artifact itself. + /** Adds a given symbol to the given scope. */ - def updateAttachment[T: ClassTag](attachment: T): AttachableApi.this.type + def enter(scope: Scope, sym: Symbol): scope.type - /** Update the attachment with the payload of the given class type `T` removed. - * Returns the reflection artifact itself. + /** Removes a given symbol to the given scope. */ - def removeAttachment[T: ClassTag]: AttachableApi.this.type - } + def unlink(scope: Scope, sym: Symbol): scope.type - // Symbol extensions --------------------------------------------------------------- + /** Collects all the symbols defined by subtrees of `tree` that are owned by `prev`, + * and then changes their owner to point to `next`. + * + * This is an essential tool to battle owner chain corruption when moving trees + * from one lexical context to another. Whenever you take an attributed tree that + * has been typechecked under the Context owned by some symbol (let's call it `x`) + * and splice it elsewhere, into the Context owned by another symbol (let's call it `y`), + * it is imperative that you either call `untypecheck` or do `changeOwner(tree, x, y)`. + * + * Since at the moment `untypecheck` has fundamental problem that can sometimes lead to tree corruption, + * `changeOwner` becomes an indispensible tool in building 100% robust macros. + * Future versions of the reflection API might obviate the need in taking care of + * these low-level details, but at the moment this is what we've got. + */ + def changeOwner(tree: Tree, prev: Symbol, next: Symbol): tree.type - /** The `Symbol` API is extended for macros: See [[SymbolContextApi]] for details. - * - * @group Macros - */ - override type Symbol >: Null <: SymbolContextApi + /** Advanced tree factories */ + val gen: TreeGen - /** The extended API of symbols that's supported in macro context universes - * @group API - */ - trait SymbolContextApi extends SymbolApi with AttachableApi { self: Symbol => + /** The attachment of the symbol. */ + def attachments(symbol: Symbol): Attachments { type Pos = Position } - /** If this symbol is a skolem, its corresponding type parameter, otherwise the symbol itself. - * - * [[https://groups.google.com/forum/#!msg/scala-internals/0j8laVNTQsI/kRXMF_c8bGsJ To quote Martin Odersky]], - * skolems are synthetic type "constants" that are copies of existentially bound or universally - * bound type variables. E.g. if one is inside the right-hand side of a method: - * - * {{{ - * def foo[T](x: T) = ... foo[List[T]].... - * }}} - * - * the skolem named `T` refers to the unknown type instance of `T` when `foo` is called. It needs to be different - * from the type parameter because in a recursive call as in the `foo[List[T]]` above the type parameter gets - * substituted with `List[T]`, but the ''type skolem'' stays what it is. - * - * The other form of skolem is an ''existential skolem''. Say one has a function - * - * {{{ - * def bar(xs: List[T] forSome { type T }) = xs.head - * }}} - * - * then each occurrence of `xs` on the right will have type `List[T']` where `T'` is a fresh copy of `T`. + /** Updates the attachment with the payload slot of T added/updated with the provided value. + * Replaces an existing payload of the same type, if exists. + * Returns the symbol itself. */ - def deSkolemize: Symbol + def updateAttachment[T: ClassTag](symbol: Symbol, attachment: T): symbol.type - /** The position of this symbol. */ - def pos: Position + /** Update the attachment with the payload of the given class type `T` removed. + * Returns the symbol itself. + */ + def removeAttachment[T: ClassTag](symbol: Symbol): symbol.type + + /** Sets the `owner` of the symbol. */ + def setOwner(symbol: Symbol, newowner: Symbol): symbol.type - /** Sets the `typeSignature` of the symbol. */ - def setTypeSignature(tpe: Type): Symbol + /** Sets the `info` of the symbol. */ + def setInfo(symbol: Symbol, tpe: Type): symbol.type /** Sets the `annotations` of the symbol. */ - def setAnnotations(annots: Annotation*): Symbol + def setAnnotations(symbol: Symbol, annots: Annotation*): symbol.type /** Sets the `name` of the symbol. */ - def setName(name: Name): Symbol + def setName(symbol: Symbol, name: Name): symbol.type /** Sets the `privateWithin` of the symbol. */ - def setPrivateWithin(sym: Symbol): Symbol - } + def setPrivateWithin(symbol: Symbol, sym: Symbol): symbol.type - // Tree extensions --------------------------------------------------------------- + /** Enables `flags` on the symbol. */ + def setFlag(symbol: Symbol, flags: FlagSet): symbol.type - /** The `Tree` API is extended for macros: See [[TreeContextApi]] for details. - * - * @group Macros - */ - override type Tree >: Null <: TreeContextApi + /** Disables `flags` on the symbol. */ + def resetFlag(symbol: Symbol, flags: FlagSet): symbol.type - /** The extended API of trees that's supported in macro context universes - * @group API - */ - trait TreeContextApi extends TreeApi with AttachableApi { self: Tree => + /** The attachment of the tree. */ + def attachments(tree: Tree): Attachments { type Pos = Position } - /** Sets the `pos` of the tree. Returns `Unit`. */ - def pos_=(pos: Position): Unit + /** Updates the attachment with the payload slot of T added/updated with the provided value. + * Replaces an existing payload of the same type, if exists. + * Returns the tree itself. + */ + def updateAttachment[T: ClassTag](tree: Tree, attachment: T): tree.type - /** Sets the `pos` of the tree. Returns the tree itself. */ - def setPos(newpos: Position): Tree + /** Update the attachment with the payload of the given class type `T` removed. + * Returns the tree itself. + */ + def removeAttachment[T: ClassTag](tree: Tree): tree.type - /** Sets the `tpe` of the tree. Returns `Unit`. */ - def tpe_=(t: Type): Unit + /** Sets the `pos` of the tree. Returns the tree itself. */ + def setPos(tree: Tree, newpos: Position): tree.type /** Sets the `tpe` of the tree. Returns the tree itself. */ - def setType(tp: Type): Tree + def setType(tree: Tree, tp: Type): tree.type /** Like `setType`, but if this is a previously empty TypeTree that - * fact is remembered so that resetAllAttrs will snap back. + * fact is remembered so that `untypecheck` 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, @@ -128,7 +117,8 @@ abstract class Universe extends scala.reflect.api.Universe { * 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 + * `untypecheck` (or `resetAttrs` in compiler parlance) 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 @@ -136,93 +126,355 @@ abstract class Universe extends scala.reflect.api.Universe { * and therefore should be abandoned if the current line of type * inquiry doesn't work out. */ - def defineType(tp: Type): Tree - - /** Sets the `symbol` of the tree. Returns `Unit`. */ - def symbol_=(sym: Symbol): Unit + def defineType(tree: Tree, tp: Type): tree.type /** Sets the `symbol` of the tree. Returns the tree itself. */ - def setSymbol(sym: Symbol): Tree - } + def setSymbol(tree: Tree, sym: Symbol): tree.type - /** @inheritdoc */ - override type SymTree >: Null <: Tree with SymTreeContextApi + /** Sets the `original` field of the type tree. */ + def setOriginal(tt: TypeTree, original: Tree): TypeTree - /** The extended API of sym trees that's supported in macro context universes - * @group API - */ - trait SymTreeContextApi extends SymTreeApi { this: SymTree => - /** Sets the `symbol` field of the sym tree. */ - var symbol: Symbol - } + /** Mark a variable as captured; i.e. force boxing in a *Ref type. + * @group Macros + */ + def captureVariable(vble: Symbol): Unit - /** @inheritdoc */ - override type TypeTree >: Null <: TypTree with TypeTreeContextApi + /** Mark given identifier as a reference to a captured variable itself + * suppressing dereferencing with the `elem` field. + * @group Macros + */ + def referenceCapturedVariable(vble: Symbol): Tree - /** The extended API of sym trees that's supported in macro context universes - * @group API - */ - trait TypeTreeContextApi extends TypeTreeApi { this: TypeTree => - /** Sets the `original` field of the type tree. */ - def setOriginal(tree: Tree): this.type + /** Convert type of a captured variable to *Ref type. + * @group Macros + */ + def capturedVariableType(vble: Symbol): Type + + /** Retrieves the untyped list of subpatterns attached to selector dummy of an UnApply node. + * Useful in writing quasiquoting macros that do pattern matching. + */ + def subpatterns(tree: Tree): Option[List[Tree]] + + /** @inheritdoc */ + override type Decorators <: MacroDecoratorApi + + /** @inheritdoc */ + trait MacroDecoratorApi extends DecoratorApi { + /** Extension methods for scopes */ + type ScopeDecorator[T <: Scope] <: MacroScopeDecoratorApi[T] + + /** @see [[ScopeDecorator]] */ + implicit def scopeDecorator[T <: Scope](tree: T): ScopeDecorator[T] + + /** @see [[ScopeDecorator]] */ + class MacroScopeDecoratorApi[T <: Scope](val scope: T) { + /** @see [[internal.enter]] */ + def enter(sym: Symbol): T = internal.enter(scope, sym) + + /** @see [[internal.unlink]] */ + def unlink(sym: Symbol): T = internal.unlink(scope, sym) + } + + /** @inheritdoc */ + override type TreeDecorator[T <: Tree] <: MacroTreeDecoratorApi[T] + + /** @see [[TreeDecorator]] */ + class MacroTreeDecoratorApi[T <: Tree](override val tree: T) extends TreeDecoratorApi[T](tree) { + /** @see [[internal.changeOwner]] */ + def changeOwner(prev: Symbol, next: Symbol): tree.type = internal.changeOwner(tree, prev, next) + + /** @see [[internal.attachments]] */ + def attachments: Attachments { type Pos = Position } = internal.attachments(tree) + + /** @see [[internal.updateAttachment]] */ + def updateAttachment[A: ClassTag](attachment: A): tree.type = internal.updateAttachment(tree, attachment) + + /** @see [[internal.removeAttachment]] */ + def removeAttachment[A: ClassTag]: T = internal.removeAttachment[A](tree) + + /** @see [[internal.setPos]] */ + def setPos(newpos: Position): T = internal.setPos(tree, newpos) + + /** @see [[internal.setType]] */ + def setType(tp: Type): T = internal.setType(tree, tp) + + /** @see [[internal.defineType]] */ + def defineType(tp: Type): T = internal.defineType(tree, tp) + + /** @see [[internal.setSymbol]] */ + def setSymbol(sym: Symbol): T = internal.setSymbol(tree, sym) + } + + /** Extension methods for typetrees */ + type TypeTreeDecorator[T <: TypeTree] <: MacroTypeTreeDecoratorApi[T] + + /** @see [[TypeTreeDecorator]] */ + implicit def typeTreeDecorator[T <: TypeTree](tt: T): TypeTreeDecorator[T] + + /** @see [[TypeTreeDecorator]] */ + class MacroTypeTreeDecoratorApi[T <: TypeTree](val tt: T) { + /** @see [[internal.setOriginal]] */ + def setOriginal(tree: Tree): TypeTree = internal.setOriginal(tt, tree) + } + + /** @inheritdoc */ + override type SymbolDecorator[T <: Symbol] <: MacroSymbolDecoratorApi[T] + + /** @see [[TreeDecorator]] */ + class MacroSymbolDecoratorApi[T <: Symbol](override val symbol: T) extends SymbolDecoratorApi[T](symbol) { + /** @see [[internal.attachments]] */ + def attachments: Attachments { type Pos = Position } = internal.attachments(symbol) + + /** @see [[internal.updateAttachment]] */ + def updateAttachment[A: ClassTag](attachment: A): T = internal.updateAttachment(symbol, attachment) + + /** @see [[internal.removeAttachment]] */ + def removeAttachment[A: ClassTag]: T = internal.removeAttachment[A](symbol) + + /** @see [[internal.setOwner]] */ + def setOwner(newowner: Symbol): T = internal.setOwner(symbol, newowner) + + /** @see [[internal.setInfo]] */ + def setInfo(tpe: Type): T = internal.setInfo(symbol, tpe) + + /** @see [[internal.setAnnotations]] */ + def setAnnotations(annots: Annotation*): T = internal.setAnnotations(symbol, annots: _*) + + /** @see [[internal.setName]] */ + def setName(name: Name): T = internal.setName(symbol, name) + + /** @see [[internal.setPrivateWithin]] */ + def setPrivateWithin(sym: Symbol): T = internal.setPrivateWithin(symbol, sym) + + /** @see [[internal.setFlag]] */ + def setFlag(flags: FlagSet): T = internal.setFlag(symbol, flags) + + /** @see [[internal.setFlag]] */ + def resetFlag(flags: FlagSet): T = internal.resetFlag(symbol, flags) + } + } } - /** @inheritdoc */ - override type Ident >: Null <: RefTree with IdentContextApi + /** @group Internal */ + trait TreeGen { + /** 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 - /** The extended API of idents that's supported in macro context universes - * @group API - */ - trait IdentContextApi extends IdentApi { this: Ident => - /** Was this ident created from a backquoted identifier? */ - def isBackquoted: Boolean + /** 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): RefTree + + /** Builds a typed reference to given symbol. */ + def mkAttributedRef(sym: Symbol): RefTree + + def stabilize(tree: Tree): Tree + + def mkAttributedStableRef(pre: Type, sym: Symbol): Tree + + def mkAttributedStableRef(sym: Symbol): Tree + + /** Builds an untyped reference to given symbol. Requires the symbol to be static. */ + def mkUnattributedRef(sym: Symbol): RefTree + + /** Builds an untyped reference to symbol with given name. Requires the symbol to be static. */ + def mkUnattributedRef(fullName: Name): RefTree + + /** Builds a typed This reference to given symbol. */ + def mkAttributedThis(sym: Symbol): This + + /** Builds a typed Ident with an underlying symbol. */ + def mkAttributedIdent(sym: Symbol): RefTree + + /** Builds a typed Select with an underlying symbol. */ + def mkAttributedSelect(qual: Tree, sym: Symbol): RefTree + + /** 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 + + /** A tree that refers to the runtime reflexive universe, `scala.reflect.runtime.universe`. */ + def mkRuntimeUniverseRef: Tree + + def mkZero(tp: Type): Tree + + def mkCast(tree: Tree, pt: Type): Tree } - /** Mark a variable as captured; i.e. force boxing in a *Ref type. - * @group Macros - */ - def captureVariable(vble: Symbol): Unit + /** @see [[internal.gen]] */ + @deprecated("Use `internal.gen` instead", "2.11.0") + val treeBuild: TreeGen - /** Mark given identifier as a reference to a captured variable itself - * suppressing dereferencing with the `elem` field. - * @group Macros - */ - def referenceCapturedVariable(vble: Symbol): Tree + /** @inheritdoc */ + type Compat <: MacroCompatApi - /** Convert type of a captured variable to *Ref type. - * @group Macros + /** @see [[compat]] + * @group Internal */ - def capturedVariableType(vble: Symbol): Type + trait MacroCompatApi extends CompatApi { + /** Scala 2.10 compatibility enrichments for Symbol. */ + implicit class MacroCompatibleSymbol(symbol: Symbol) { + /** @see [[InternalMacroApi.attachments]] */ + @deprecated("Use `internal.attachments` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def attachments: Attachments { type Pos = Position } = internal.attachments(symbol) + + /** @see [[InternalMacroApi.updateAttachment]] */ + @deprecated("Use `internal.updateAttachment` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def updateAttachment[T: ClassTag](attachment: T): Symbol = internal.updateAttachment[T](symbol, attachment) + + /** @see [[InternalMacroApi.removeAttachment]] */ + @deprecated("Use `internal.removeAttachment` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def removeAttachment[T: ClassTag]: Symbol = internal.removeAttachment[T](symbol) + + /** @see [[InternalMacroApi.setInfo]] */ + @deprecated("Use `internal.setInfo` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setTypeSignature(tpe: Type): Symbol = internal.setInfo(symbol, tpe) + + /** @see [[InternalMacroApi.setAnnotations]] */ + @deprecated("Use `internal.setAnnotations` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setAnnotations(annots: Annotation*): Symbol = internal.setAnnotations(symbol, annots: _*) + + /** @see [[InternalMacroApi.setName]] */ + @deprecated("Use `internal.setName` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setName(name: Name): Symbol = internal.setName(symbol, name) + + /** @see [[InternalMacroApi.setPrivateWithin]] */ + @deprecated("Use `internal.setPrivateWithin` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setPrivateWithin(sym: Symbol): Symbol = internal.setPrivateWithin(symbol, sym) + } + + /** Scala 2.10 compatibility enrichments for TypeTree. */ + implicit class MacroCompatibleTree(tree: Tree) { + /** @see [[InternalMacroApi.attachments]] */ + @deprecated("Use `internal.attachments` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def attachments: Attachments { type Pos = Position } = internal.attachments(tree) + + /** @see [[InternalMacroApi.updateAttachment]] */ + @deprecated("Use `internal.updateAttachment` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def updateAttachment[T: ClassTag](attachment: T): Tree = internal.updateAttachment[T](tree, attachment) + + /** @see [[InternalMacroApi.removeAttachment]] */ + @deprecated("Use `internal.removeAttachment` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def removeAttachment[T: ClassTag]: Tree = internal.removeAttachment[T](tree) + + /** @see [[InternalMacroApi.setPos]] */ + @deprecated("Use `internal.setPos` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def pos_=(pos: Position): Unit = internal.setPos(tree, pos) + + /** @see [[InternalMacroApi.setPos]] */ + @deprecated("Use `internal.setPos` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setPos(newpos: Position): Tree = internal.setPos(tree, newpos) + + /** @see [[InternalMacroApi.setType]] */ + @deprecated("Use `internal.setType` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def tpe_=(t: Type): Unit = internal.setType(tree, t) + + /** @see [[InternalMacroApi.setType]] */ + @deprecated("Use `internal.setType` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setType(tp: Type): Tree = internal.setType(tree, tp) + + /** @see [[InternalMacroApi.defineType]] */ + @deprecated("Use `internal.defineType` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def defineType(tp: Type): Tree = internal.defineType(tree, tp) + + /** @see [[InternalMacroApi.setSymbol]] */ + @deprecated("Use `internal.setSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def symbol_=(sym: Symbol): Unit = internal.setSymbol(tree, sym) + + /** @see [[InternalMacroApi.setSymbol]] */ + @deprecated("Use `internal.setSymbol` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setSymbol(sym: Symbol): Tree = internal.setSymbol(tree, sym) + } + + /** Scala 2.10 compatibility enrichments for TypeTree. */ + implicit class CompatibleTypeTree(tt: TypeTree) { + /** @see [[InternalMacroApi.setOriginal]] */ + @deprecated("Use `internal.setOriginal` instead or import `internal.decorators._` for infix syntax", "2.11.0") + def setOriginal(tree: Tree): TypeTree = internal.setOriginal(tt, tree) + } + + /** @see [[InternalMacroApi.captureVariable]] */ + @deprecated("Use `internal.captureVariable` instead", "2.11.0") + def captureVariable(vble: Symbol): Unit = internal.captureVariable(vble) + + /** @see [[InternalMacroApi.captureVariable]] */ + @deprecated("Use `internal.referenceCapturedVariable` instead", "2.11.0") + def referenceCapturedVariable(vble: Symbol): Tree = internal.referenceCapturedVariable(vble) + + /** @see [[InternalMacroApi.captureVariable]] */ + @deprecated("Use `internal.capturedVariableType` instead", "2.11.0") + def capturedVariableType(vble: Symbol): Type = internal.capturedVariableType(vble) + } /** The type of compilation runs. + * @see [[scala.reflect.macros.Enclosures]] * @template * @group Macros */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type Run <: RunContextApi /** Compilation run uniquely identifies current invocation of the compiler * (e.g. can be used to implement per-run caches for macros) and provides access to units of work * of the invocation (currently processed unit of work and the list of all units). + * @see [[scala.reflect.macros.Enclosures]] * @group API */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") trait RunContextApi { /** Currently processed unit of work (a real or a virtual file). */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def currentUnit: CompilationUnit /** All units of work comprising this compilation run. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def units: Iterator[CompilationUnit] } /** The type of compilation units. + * @see [[scala.reflect.macros.Enclosures]] * @template * @group Macros */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type CompilationUnit <: CompilationUnitContextApi /** Compilation unit describes a unit of work of the compilation run. * It provides such information as file name, textual representation of the unit and the underlying AST. + * @see [[scala.reflect.macros.Enclosures]] * @group API */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") trait CompilationUnitContextApi { /** Source file corresponding to this compilation unit. * @@ -233,9 +485,11 @@ abstract class Universe extends scala.reflect.api.Universe { * It should not be used unless you know what you are doing. In subsequent releases, this API will be refined * and exposed as a part of scala.reflect.api. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def source: scala.reflect.internal.util.SourceFile /** The AST that corresponds to this compilation unit. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def body: Tree } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/macros/Context.scala b/src/reflect/scala/reflect/macros/blackbox/Context.scala index aa1c1db227..2f9c512efa 100644 --- a/src/reflect/scala/reflect/macros/Context.scala +++ b/src/reflect/scala/reflect/macros/blackbox/Context.scala @@ -1,14 +1,12 @@ -package scala.reflect +package scala +package reflect package 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.macros (and probably reside in scala-compiler.jar) +package blackbox /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * - * The Scala macros context. + * The blackbox Scala macros context. * * See [[scala.reflect.macros.package the overview page]] for a description of how macros work. This documentation * entry provides information on the API available to macro writers. @@ -26,6 +24,14 @@ package macros * Other than that, macro contexts provide facilities for typechecking, exploring the compiler's symbol table and * enclosing trees and compilation units, evaluating trees, logging warnings/errors and much more. * Refer to the documentation of top-level traits in this package to learn the details. + * + * If a macro def refers to a macro impl that uses `blackbox.Context`, then this macro def becomes a blackbox macro, + * which means that its expansion will be upcast to its return type, enforcing faithfullness of that macro to its + * type signature. Whitebox macros, i.e. the ones defined with `whitebox.Context`, aren't bound by this restriction, + * which enables a number of important use cases, but they are also going to enjoy less support than blackbox macros, + * so choose wisely. See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information. + * + * @see `scala.reflect.macros.whitebox.Context` */ trait Context extends Aliases with Enclosures @@ -36,7 +42,8 @@ trait Context extends Aliases with Typers with Parsers with Evals - with ExprUtils { + with ExprUtils + with Internals { /** The compile-time universe. */ val universe: Universe @@ -51,7 +58,7 @@ trait Context extends Aliases /** The prefix tree from which the macro is selected. * - * For a example, for a macro `filter` defined as an instance method on a collection `Coll`, + * For example, for a macro `filter` defined as an instance method on a collection `Coll`, * `prefix` represents an equivalent of `this` for normal instance methods: * * {{{ diff --git a/src/reflect/scala/reflect/macros/package.scala b/src/reflect/scala/reflect/macros/package.scala index 21d189bb25..cc7111d794 100644 --- a/src/reflect/scala/reflect/macros/package.scala +++ b/src/reflect/scala/reflect/macros/package.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> @@ -6,10 +7,22 @@ package scala.reflect * The base package for Scala macros. * * Macros are functions that are called by the compiler during compilation. - * Within these functions the programmer has access to compiler APIs exposed in [[scala.reflect.macros.Context]]. + * Within these functions the programmer has access to compiler APIs. * For example, it is possible to generate, analyze and typecheck code. * * See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] on how to get started with Scala macros. */ package object macros { + /** The Scala macros context. + * + * In Scala 2.11, macros that were once the one are split into blackbox and whitebox macros, + * with the former being better supported and the latter being more powerful. You can read about + * the details of the split and the associated trade-offs in the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]]. + * + * `scala.reflect.macros.Context` follows this tendency and turns into `scala.reflect.macros.blackbox.Context` + * and `scala.reflect.macros.whitebox.Context`. The original `Context` is left in place for compatibility reasons, + * but it is now deprecated, nudging the users to choose between blackbox and whitebox macros. + */ + @deprecated("Use blackbox.Context or whitebox.Context instead", "2.11.0") + type Context = whitebox.Context }
\ No newline at end of file diff --git a/src/reflect/scala/reflect/macros/whitebox/Context.scala b/src/reflect/scala/reflect/macros/whitebox/Context.scala new file mode 100644 index 0000000000..bd48df46cc --- /dev/null +++ b/src/reflect/scala/reflect/macros/whitebox/Context.scala @@ -0,0 +1,77 @@ +package scala +package reflect +package macros +package whitebox + +/** + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * + * The whitebox Scala macros context. + * + * See [[scala.reflect.macros.package the overview page]] for a description of how macros work. This documentation + * entry provides information on the API available to macro writers. + * + * A macro context wraps a compiler universe exposed in `universe` and having type [[scala.reflect.macros.Universe]]. + * This type is a refinement over the generic reflection API provided in [[scala.reflect.api.Universe]]. The + * extended Universe provides mutability for reflection artifacts (e.g. macros can change types of compiler trees, + * add annotation to symbols representing definitions, etc) and exposes some internal compiler functionality + * such as `Symbol.deSkolemize` or `Tree.attachments`. + * + * Another fundamental part of a macro context is `macroApplication`, which provides access to the tree undergoing + * macro expansion. Parts of this tree can be found in arguments of the corresponding macro implementations and + * in `prefix`, but `macroApplication` gives the full picture. + * + * Other than that, macro contexts provide facilities for typechecking, exploring the compiler's symbol table and + * enclosing trees and compilation units, evaluating trees, logging warnings/errors and much more. + * Refer to the documentation of top-level traits in this package to learn the details. + * + * If a macro def refers to a macro impl that uses `whitebox.Context`, then this macro def becomes a whitebox macro, + * gaining the ability to refine the type of its expansion beyond its official return type, which enables a number of important use cases. + * Blackbox macros, i.e. the ones defined with `blackbox.Context`, can't do that, so they are less powerful. + * However blackbox macros are also going to enjoy better support than whitebox macros, so choose wisely. + * See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information. + * + * @see `scala.reflect.macros.blackbox.Context` + */ +trait Context extends blackbox.Context { + /** @inheritdoc + */ + def openMacros: List[Context] + + /** @inheritdoc + */ + def enclosingMacros: List[Context] + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * `pre` and `sym` provide information about the candidate itself. + * `pt` and `tree` store the parameters of the implicit search the candidate is participating in. + */ + case class ImplicitCandidate(pre: Type, sym: Symbol, pt: Type, tree: Tree) + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * If we're in an implicit macro being expanded, it's included in this list. + * + * 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[ImplicitCandidate] + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * If we're in an implicit macro being expanded, it's included in this list. + * + * 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. + */ + def enclosingImplicits: List[ImplicitCandidate] +}
\ No newline at end of file diff --git a/src/reflect/scala/reflect/runtime/Gil.scala b/src/reflect/scala/reflect/runtime/Gil.scala new file mode 100644 index 0000000000..0edb1e5748 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/Gil.scala @@ -0,0 +1,25 @@ +package scala.reflect +package runtime + +private[reflect] trait Gil { + self: SymbolTable => + + // fixme... please... + // there are the following avenues of optimization we discussed with Roland: + // 1) replace PackageScope locks with ConcurrentHashMap, because PackageScope materializers seem to be idempotent + // 2) unlock unpickling completers by verifying that they are idempotent or moving non-idempotent parts + // 3) remove the necessity in global state for isSubType + private lazy val gil = new java.util.concurrent.locks.ReentrantLock + + @inline final def gilSynchronized[T](body: => T): T = { + if (isCompilerUniverse) body + else { + try { + gil.lock() + body + } finally { + gil.unlock() + } + } + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 9c0781ca06..6584d80de3 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.ref.WeakReference @@ -6,25 +7,22 @@ import scala.collection.mutable.WeakHashMap import java.lang.{Class => jClass, Package => jPackage} import java.lang.reflect.{ - Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, + Method => jMethod, Constructor => jConstructor, Field => jField, Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray, + AccessibleObject => jAccessibleObject, GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement } import java.lang.annotation.{Annotation => jAnnotation} import java.io.IOException -import internal.MissingRequirementError +import scala.reflect.internal.{ MissingRequirementError, JavaAccFlags, JMethodOrConstructor } import internal.pickling.ByteCodecs -import internal.ClassfileConstants._ import internal.pickling.UnPickler -import scala.collection.mutable.{ HashMap, ListBuffer } +import scala.collection.mutable.{ HashMap, ListBuffer, ArrayBuffer } import internal.Flags._ -//import scala.tools.nsc.util.ScalaClassLoader -//import scala.tools.nsc.util.ScalaClassLoader._ -import ReflectionUtils.{staticSingletonInstance, innerSingletonInstance, scalacShouldntLoadClass} +import ReflectionUtils._ import scala.language.existentials import scala.runtime.{ScalaRunTime, BoxesRunTime} -import scala.reflect.internal.util.Collections._ -private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUniverse: SymbolTable => +private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]() @@ -35,41 +33,39 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni jm } - override type RuntimeClass = java.lang.Class[_] - override type Mirror = JavaMirror + implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[JavaMirror]) override lazy val rootMirror: Mirror = createMirror(NoSymbol, rootClassLoader) // overriden by ReflectGlobal def rootClassLoader: ClassLoader = this.getClass.getClassLoader - trait JavaClassCompleter extends FlagAssigningCompleter - - def init() = { - definitions.AnyValClass // force it. - - // establish root association to avoid cyclic dependency errors later - rootMirror.classToScala(classOf[java.lang.Object]).initialize + trait JavaClassCompleter - // println("initializing definitions") - definitions.init() - } - - def runtimeMirror(cl: ClassLoader): Mirror = mirrors get cl match { - case Some(WeakReference(m)) => m - case _ => createMirror(rootMirror.RootClass, cl) + def runtimeMirror(cl: ClassLoader): Mirror = gilSynchronized { + 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 */ + /* Class loader that is a mastermind behind the reflexive mirror */ val classLoader: ClassLoader ) extends Roots(owner) with super.JavaMirror { thisMirror => val universe: thisUniverse.type = thisUniverse import definitions._ + private[reflect] lazy val runDefinitions = new definitions.RunDefinitions // only one "run" in the reflection universe + import runDefinitions._ + + override lazy val RootPackage = (new RootPackage with SynchronizedTermSymbol).markFlagsCompleted(mask = AllFlags) + override lazy val RootClass = (new RootClass with SynchronizedModuleClassSymbol).markFlagsCompleted(mask = AllFlags) + override lazy val EmptyPackage = (new EmptyPackage with SynchronizedTermSymbol).markFlagsCompleted(mask = AllFlags) + override lazy val EmptyPackageClass = (new EmptyPackageClass with SynchronizedModuleClassSymbol).markFlagsCompleted(mask = AllFlags) /** The lazy type for root. */ @@ -84,19 +80,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // the same thing is done by the `missingHook` below override def staticPackage(fullname: String): ModuleSymbol = try super.staticPackage(fullname) - catch { - case _: MissingRequirementError => - makeScalaPackage(fullname) - } + catch { case _: ScalaReflectionException => makeScalaPackage(fullname) } // ----------- Caching ------------------------------------------------------------------ - private val classCache = new TwoWayCache[jClass[_], ClassSymbol] - private val packageCache = new TwoWayCache[Package, ModuleSymbol] - private val methodCache = new TwoWayCache[jMethod, MethodSymbol] + 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] + private val fieldCache = new TwoWayCache[jField, TermSymbol] + private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] private[runtime] def toScala[J: HasJavaClass, S](cache: TwoWayCache[J, S], key: J)(body: (JavaMirror, J) => S): S = cache.toScala(key){ @@ -104,39 +97,36 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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 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 jclazz: jClass[_] => jclazz + case jmeth: jMethod => jmeth.getDeclaringClass case jconstr: jConstructor[_] => jconstr.getDeclaringClass } }) // ----------- Implementations of mirror operations and classes ------------------- - private def ErrorInnerClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") - private def ErrorInnerModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") - private def ErrorStaticClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") - private def ErrorStaticModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") - private def ErrorNotMember(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") - private def ErrorNotField(sym: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $sym") - private def ErrorNonExistentField(sym: Symbol) = throw new ScalaReflectionException( + private def abort(msg: String) = throw new ScalaReflectionException(msg) + + private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") + private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym") + private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym") + private def ErrorArrayConstructor(sym: Symbol, owner: Symbol) = abort(s"Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") + private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") + private def ErrorNonExistentField(sym: Symbol) = abort( sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method |note that private parameters of class constructors don't get mapped onto fields and/or accessors, |unless they are used outside of their declaring constructors.""") - @deprecated("corresponding check has been removed from FieldMirror.set, this method is also being phased out", "2.11.0") - private def ErrorSetImmutableField(sym: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${sym.name}") - private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $sym") - private def ErrorFree(member: Symbol, freeType: Symbol) = throw new ScalaReflectionException(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") /** Helper functions for extracting typed values from a (Class[_], Any) * representing an annotation argument. @@ -152,7 +142,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni object ConstantArg { def enumToSymbol(enum: Enum[_]): Symbol = { val staticPartOfEnum = classToScala(enum.getClass).companionSymbol - staticPartOfEnum.typeSignature.declaration(enum.name: TermName) + staticPartOfEnum.info.declaration(enum.name: TermName) } def unapply(schemaAndValue: (jClass[_], Any)): Option[Any] = schemaAndValue match { @@ -214,7 +204,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case _ => body } } - private def checkMemberOf(sym: Symbol, owner: ClassSymbol) { if (sym.owner == AnyClass || sym.owner == AnyRefClass || sym.owner == ObjectClass) { // do nothing @@ -229,6 +218,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def checkConstructorOf(sym: Symbol, owner: ClassSymbol) { if (!sym.isClassConstructor) ErrorNotConstructor(sym, owner) + if (owner == ArrayClass) ErrorArrayConstructor(sym, owner) ensuringNotFree(sym) { if (!owner.info.decls.toList.contains(sym)) ErrorNotConstructor(sym, owner) } @@ -240,16 +230,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (staticClazz.isPrimitive) staticClazz else dynamicClazz } - private class JavaInstanceMirror[T: ClassTag](val instance: T) - extends InstanceMirror { + private class JavaInstanceMirror[T: ClassTag](val instance: T) extends InstanceMirror { def symbol = thisMirror.classSymbol(preciseClass(instance)) def reflectField(field: TermSymbol): FieldMirror = { checkMemberOf(field, symbol) if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field) - val name = - if (field.isGetter) nme.getterToLocal(field.name) - else if (field.isSetter) nme.getterToLocal(nme.setterToGetter(field.name)) - else field.name + val name = if (field.isAccessor) field.localName else field.name val field1 = (field.owner.info decl name).asTerm try fieldToJava(field1) catch { @@ -259,7 +245,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } def reflectMethod(method: MethodSymbol): MethodMirror = { checkMemberOf(method, symbol) - mkJavaMethodMirror(instance, method) + mkMethodMirror(instance, method) } def reflectClass(cls: ClassSymbol): ClassMirror = { if (cls.isStatic) ErrorStaticClass(cls) @@ -274,105 +260,189 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def toString = s"instance mirror for $instance" } - private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol) + // caches value class metadata, so that we minimize the work that needs to be done during Mirror.apply + private class DerivedValueClassMetadata(info: Type) { + val symbol = info.typeSymbol + val isDerivedValueClass = symbol.isDerivedValueClass + lazy val boxer = runtimeClass(symbol.toType).getDeclaredConstructors().head + lazy val unboxer = { + val fields @ (field :: _) = symbol.toType.decls.collect{ case ts: TermSymbol if ts.isParamAccessor && ts.isMethod => ts }.toList + assert(fields.length == 1, s"$symbol: $fields") + runtimeClass(symbol.asClass).getDeclaredMethod(field.name.toString) + } + } + + private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol, metadata: DerivedValueClassMetadata) extends FieldMirror { - lazy val jfield = { - val jfield = fieldToJava(symbol) - if (!jfield.isAccessible) jfield.setAccessible(true) - jfield + def this(receiver: Any, symbol: TermSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.info)) + def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol, metadata) + import metadata._ + + lazy val jfield = ensureAccessible(fieldToJava(symbol)) + def get = { + val value = jfield get receiver + if (isDerivedValueClass) boxer.newInstance(value) else value } - def get = jfield.get(receiver) def set(value: Any) = { // it appears useful to be able to set values of vals, therefore I'm disabling this check // if (!symbol.isMutable) ErrorSetImmutableField(symbol) - jfield.set(receiver, value) - } - // this dummy method is necessary to prevent the optimizer from stripping off ErrorSetImmutableField - // which would break binary compatibility with 2.10.0 - private def dummy(symbol: Symbol) = ErrorSetImmutableField(symbol) - override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)" - } - - private def showMethodSig(symbol: MethodSymbol): String = { - var sig = s"${symbol.fullName}" - if (symbol.typeParams.nonEmpty) { - def showTparam(tparam: Symbol) = - tparam.typeSignature match { - case tpe @ TypeBounds(_, _) => s"${tparam.name}$tpe" - case _ => tparam.name - } - def showTparams(tparams: List[Symbol]) = "[" + (tparams map showTparam mkString ", ") + "]" - sig += showTparams(symbol.typeParams) - } - if (symbol.paramss.nonEmpty) { - def showParam(param: Symbol) = s"${param.name}: ${param.typeSignature}" - def showParams(params: List[Symbol]) = { - val s_mods = if (params.nonEmpty && params(0).hasFlag(IMPLICIT)) "implicit " else "" - val s_params = params map showParam mkString ", " - "(" + s_mods + s_params + ")" - } - def showParamss(paramss: List[List[Symbol]]) = paramss map showParams mkString "" - sig += showParamss(symbol.paramss) + jfield.set(receiver, if (isDerivedValueClass) unboxer.invoke(value) else value) } - sig += s": ${symbol.returnType}" - sig + + override def toString = s"field mirror for ${showDecl(symbol)} (bound to $receiver)" } // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it // because both AnyVal and its primitive descendants define their own getClass methods - private def isGetClass(meth: MethodSymbol) = meth.name.toString == "getClass" && meth.paramss.flatten.isEmpty + private def isGetClass(meth: MethodSymbol) = (meth.name string_== "getClass") && meth.paramss.flatten.isEmpty private def isStringConcat(meth: MethodSymbol) = meth == String_+ || (meth.owner.isPrimitiveValueClass && meth.returnType =:= StringClass.toType) lazy val bytecodelessMethodOwners = Set[Symbol](AnyClass, AnyValClass, AnyRefClass, ObjectClass, ArrayClass) ++ ScalaPrimitiveValueClasses lazy val bytecodefulObjectMethods = Set[Symbol](Object_clone, Object_equals, Object_finalize, Object_hashCode, Object_toString, Object_notify, Object_notifyAll) ++ ObjectClass.info.member(nme.wait_).asTerm.alternatives.map(_.asMethod) private def isBytecodelessMethod(meth: MethodSymbol): Boolean = { - if (isGetClass(meth) || isStringConcat(meth) || meth.owner.isPrimitiveValueClass || meth == Predef_classOf || meth.isTermMacro) return true + if (isGetClass(meth) || isStringConcat(meth) || meth.owner.isPrimitiveValueClass || meth == runDefinitions.Predef_classOf || meth.isMacro) return true bytecodelessMethodOwners(meth.owner) && !bytecodefulObjectMethods(meth) } + private def isByNameParam(p: Type) = isByNameParamType(p) + private def isValueClassParam(p: Type) = p.typeSymbol.isDerivedValueClass + // unlike other mirrors, method mirrors are created by a factory // that's because we want to have decent performance // therefore we move special cases into separate subclasses // rather than have them on a hot path them in a unified implementation of the `apply` method - private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = { - if (isBytecodelessMethod(symbol)) new JavaBytecodelessMethodMirror(receiver, symbol) - else if (symbol.paramss.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror(receiver, symbol) - else new JavaVanillaMethodMirror(receiver, symbol) + private def mkMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): MethodMirror = { + def existsParam(pred: Type => Boolean) = symbol.paramss.flatten.map(_.info).exists(pred) + if (isBytecodelessMethod(symbol)) new BytecodelessMethodMirror(receiver, symbol) + else if (existsParam(isByNameParam) || existsParam(isValueClassParam)) new JavaTransformingMethodMirror(receiver, symbol) + else { + symbol.paramss.flatten.length match { + case 0 => new JavaVanillaMethodMirror0(receiver, symbol) + case 1 => new JavaVanillaMethodMirror1(receiver, symbol) + case 2 => new JavaVanillaMethodMirror2(receiver, symbol) + case 3 => new JavaVanillaMethodMirror3(receiver, symbol) + case 4 => new JavaVanillaMethodMirror4(receiver, symbol) + case _ => new JavaVanillaMethodMirror(receiver, symbol) + } + } } - private abstract class JavaMethodMirror(val symbol: MethodSymbol) - extends MethodMirror { - lazy val jmeth = { - val jmeth = methodToJava(symbol) - if (!jmeth.isAccessible) jmeth.setAccessible(true) - jmeth + private abstract class JavaMethodMirror(val symbol: MethodSymbol, protected val ret: DerivedValueClassMetadata) extends MethodMirror { + lazy val jmeth = ensureAccessible(methodToJava(symbol)) + lazy val jconstr = ensureAccessible(constructorToJava(symbol)) + + def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + else if (receiver == null) jconstr.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*) + else jconstr.newInstance((receiver +: args).asInstanceOf[Seq[AnyRef]]: _*) + def jinvoke(args: Seq[Any]): Any = { + val result = jinvokeraw(args) + if (!symbol.isConstructor && jmeth.getReturnType == java.lang.Void.TYPE) () + else if (!symbol.isConstructor && ret.isDerivedValueClass) ret.boxer.newInstance(result.asInstanceOf[AnyRef]) + else result } - def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { - val result = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) - if (jmeth.getReturnType == java.lang.Void.TYPE) () - else result + override def toString = { + val what = if (symbol.isConstructor) "constructor mirror" else "method mirror" + s"$what for ${showDecl(symbol)} (bound to $receiver)" } + } + + private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaMethodMirror(symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol, ret) + def apply(args: Any*): Any = jinvoke(args) + } + + private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol, ret) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver) + else if (receiver == null) jconstr.newInstance() + else jconstr.newInstance(receiver.asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol, ret) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol, ret) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol, ret) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + } - override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)" + private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol, ret) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) } - private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol) - extends JavaMethodMirror(symbol) { - def apply(args: Any*): Any = jinvoke(jmeth, receiver, args) + // caches MethodSymbol metadata, so that we minimize the work that needs to be done during Mirror.apply + // TODO: vararg is only supported in the last parameter list (SI-6182), so we don't need to worry about the rest for now + private class MethodMetadata(symbol: MethodSymbol) { + private val params = symbol.paramss.flatten.toArray + private val vcMetadata = params.map(p => new DerivedValueClassMetadata(p.info)) + val isByName = params.map(p => isByNameParam(p.info)) + def isDerivedValueClass(i: Int) = vcMetadata(i).isDerivedValueClass + def paramUnboxers(i: Int) = vcMetadata(i).unboxer + val paramCount = params.length + val ret = new DerivedValueClassMetadata(symbol.returnType) } - private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol) - extends JavaMethodMirror(symbol) { + private class JavaTransformingMethodMirror(val receiver: Any, symbol: MethodSymbol, metadata: MethodMetadata) + extends JavaMethodMirror(symbol, metadata.ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new MethodMetadata(symbol)) + override def bind(newReceiver: Any) = new JavaTransformingMethodMirror(newReceiver, symbol, metadata) + import metadata._ + def apply(args: Any*): Any = { - val transformed = map2(args.toList, symbol.paramss.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg) - jinvoke(jmeth, receiver, transformed) + val args1 = new Array[Any](args.length) + var i = 0 + while (i < args1.length) { + val arg = args(i) + if (i >= paramCount) args1(i) = arg // don't transform varargs + else if (isByName(i)) args1(i) = () => arg // don't transform by-name value class params + else if (isDerivedValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg) + i += 1 + } + jinvoke(args1) } } - private class JavaBytecodelessMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol) - extends JavaMethodMirror(symbol) { - def apply(args: Any*): Any = { + private class BytecodelessMethodMirror[T: ClassTag](val receiver: T, val symbol: MethodSymbol) + extends MethodMirror { + def bind(newReceiver: Any) = new BytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) + override def toString = s"bytecodeless method mirror for ${showDecl(symbol)} (bound to $receiver)" + + def apply(args: Any*): Any = { // checking type conformance is too much of a hassle, so we don't do it here // actually it's not even necessary, because we manually dispatch arguments below val params = symbol.paramss.flatten @@ -383,19 +453,22 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val varargMatch = args.length >= params.length - 1 && isVarArgsList(params) if (!perfectMatch && !varargMatch) { val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}" - var s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments" - throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") + val s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments" + abort(s"${showDecl(symbol)} takes $n_arguments $s_arguments") } def objReceiver = receiver.asInstanceOf[AnyRef] def objArg0 = args(0).asInstanceOf[AnyRef] def objArgs = args.asInstanceOf[Seq[AnyRef]] - def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors") + def fail(msg: String) = abort(msg + ", it cannot be invoked with mirrors") def invokePrimitiveMethod = { val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString) assert(jmeths.length == 1, jmeths.toList) - jinvoke(jmeths.head, null, objReceiver +: objArgs) + val jmeth = jmeths.head + val result = jmeth.invoke(null, (objReceiver +: objArgs).asInstanceOf[Seq[AnyRef]]: _*) + if (jmeth.getReturnType == java.lang.Void.TYPE) () + else result } symbol match { @@ -411,8 +484,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case sym if isGetClass(sym) => preciseClass(receiver) case Any_asInstanceOf => fail("Any.asInstanceOf requires a type argument") case Any_isInstanceOf => fail("Any.isInstanceOf requires a type argument") - case Object_asInstanceOf => fail("AnyRef.$asInstanceOf is an internal method") - case Object_isInstanceOf => fail("AnyRef.$isInstanceOf is an internal method") + case Object_asInstanceOf => fail("AnyRef.%s is an internal method" format symbol.name) + case Object_isInstanceOf => fail("AnyRef.%s is an internal method" format symbol.name) case Array_length => ScalaRunTime.array_length(objReceiver) case Array_apply => ScalaRunTime.array_apply(objReceiver, args(0).asInstanceOf[Int]) case Array_update => ScalaRunTime.array_update(objReceiver, args(0).asInstanceOf[Int], args(1)) @@ -420,37 +493,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case sym if isStringConcat(sym) => receiver.toString + objArg0 case sym if sym.owner.isPrimitiveValueClass => invokePrimitiveMethod case sym if sym == Predef_classOf => fail("Predef.classOf is a compile-time function") - case sym if sym.isTermMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function") + case sym if sym.isMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function") case _ => abort(s"unsupported symbol $symbol when invoking $this") } } } - private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol) - extends MethodMirror { - override val receiver = outer - lazy val jconstr = { - val jconstr = constructorToJava(symbol) - if (!jconstr.isAccessible) jconstr.setAccessible(true) - jconstr - } - def apply(args: Any*): Any = { - if (symbol.owner == ArrayClass) - throw new ScalaReflectionException("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") - - val effectiveArgs = - if (outer == null) args.asInstanceOf[Seq[AnyRef]] - else outer +: args.asInstanceOf[Seq[AnyRef]] - jconstr.newInstance(effectiveArgs: _*) - } - override def toString = s"constructor mirror for ${showMethodSig(symbol)} (bound to $outer)" - } - private abstract class JavaTemplateMirror extends TemplateMirror { def outer: AnyRef def erasure: ClassSymbol - lazy val signature = typeToScala(classToJava(erasure)) } private class JavaClassMirror(val outer: AnyRef, val symbol: ClassSymbol) @@ -459,7 +511,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def isStatic = false def reflectConstructor(constructor: MethodSymbol) = { checkConstructorOf(constructor, symbol) - new JavaConstructorMirror(outer, constructor) + mkMethodMirror(outer, constructor) } override def toString = s"class mirror for ${symbol.fullName} (bound to $outer)" } @@ -469,11 +521,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def erasure = symbol.moduleClass.asClass def isStatic = true def instance = { - if (symbol.owner.isPackageClass) + if (symbol.isTopLevel) staticSingletonInstance(classLoader, symbol.fullName) else if (outer == null) staticSingletonInstance(classToJava(symbol.moduleClass.asClass)) - else innerSingletonInstance(outer, symbol.name) + else innerSingletonInstance(outer, symbol.name.toString) } override def toString = s"module mirror for ${symbol.fullName} (bound to $outer)" } @@ -497,16 +549,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } def javaClass(path: String): jClass[_] = - Class.forName(path, true, classLoader) + jClass.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 - } + def tryJavaClass(path: String): Option[jClass[_]] = ( + try Some(javaClass(path)) + catch { case ex @ (_: LinkageError | _: ClassNotFoundException) => None } // TODO - log + ) /** The mirror that corresponds to the classloader that original defined the given Java class */ def mirrorDefining(jclazz: jClass[_]): JavaMirror = { @@ -515,7 +564,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } private object unpickler extends UnPickler { - val global: thisUniverse.type = thisUniverse + val symbolTable: thisUniverse.type = thisUniverse } /** how connected???? @@ -530,7 +579,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def markAbsent(tpe: Type) = setAllInfos(clazz, module, tpe) def handleError(ex: Exception) = { markAbsent(ErrorType) - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() val msg = ex.getMessage() MissingRequirementError.signal( (if (msg eq null) "reflection error while loading " + clazz.name @@ -562,7 +611,9 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}") val bytes = ssig.getBytes val len = ByteCodecs.decode(bytes) + assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) + markAllCompleted(clazz, module) case None => loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match { case Some(slsig) => @@ -570,11 +621,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val encoded = slsig flatMap (_.getBytes) val len = ByteCodecs.decode(encoded) val decoded = encoded.take(len) + assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(decoded, 0, clazz, module, jclazz.getName) + markAllCompleted(clazz, module) 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)) + initClassAndModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz)) } } } catch { @@ -593,6 +646,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def createTypeParameter(jtvar: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = { val tparam = sOwner(jtvar).newTypeParameter(newTypeName(jtvar.getName)) .setInfo(new TypeParamCompleter(jtvar)) + markFlagsCompleted(tparam)(mask = AllFlags) tparamCache enter (jtvar, tparam) tparam } @@ -605,9 +659,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def load(sym: Symbol) = complete(sym) override def complete(sym: Symbol) = { sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala map objToAny)) + markAllCompleted(sym) } } + private def assignAssociatedFile(clazz: Symbol, module: Symbol, jclazz: jClass[_]): Unit = { + val associatedFile = ReflectionUtils.associatedFile(jclazz) + clazz.associatedFile = associatedFile + if (module != NoSymbol) module.associatedFile = associatedFile + } + /** * Copy all annotations of Java annotated element `jann` over to Scala symbol `sym`. * Also creates `@throws` annotations if necessary. @@ -618,13 +679,22 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni sym setAnnotations (jann.getAnnotations map JavaAnnotationProxy).toList // SI-7065: we're not using getGenericExceptionTypes here to be consistent with ClassfileParser val jexTpes = jann match { - case jm: jMethod => jm.getExceptionTypes.toList + case jm: jMethod => jm.getExceptionTypes.toList case jconstr: jConstructor[_] => jconstr.getExceptionTypes.toList - case _ => Nil + case _ => Nil } jexTpes foreach (jexTpe => sym.addThrowsAnnotation(classSymbol(jexTpe))) } + private implicit class jClassOps(val clazz: jClass[_]) { + def javaFlags: JavaAccFlags = JavaAccFlags(clazz) + def scalaFlags: Long = javaFlags.toScalaFlags + } + private implicit class jMemberOps(val member: jMember) { + def javaFlags: JavaAccFlags = JavaAccFlags(member) + def scalaFlags: Long = javaFlags.toScalaFlags + } + /** * 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 @@ -634,7 +704,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * @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 with JavaClassCompleter with FlagAssigningCompleter { + private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType with JavaClassCompleter with FlagAgnosticCompleter { + // one doesn't need to do non-trivial computations to assign flags for Java-based reflection artifacts + // therefore I'm moving flag-assigning logic from completion to construction + val flags = jclazz.scalaFlags + clazz setFlag (flags | JAVA) + if (module != NoSymbol) { + module setFlag (flags & PRIVATE | JAVA) + module.moduleClass setFlag (flags & PRIVATE | JAVA) + } + markFlagsCompleted(clazz, module)(mask = AllFlags) /** used to avoid cycles while initializing classes */ private var parentsLevel = 0 @@ -644,14 +723,9 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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) - } - relatedSymbols foreach (importPrivateWithinFromJavaFlags(_, jclazz.getModifiers)) + assignAssociatedFile(clazz, module, jclazz) + propagatePackageBoundary(jclazz, relatedSymbols: _*) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? @@ -665,18 +739,19 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def complete(sym: Symbol): Unit = { load(sym) completeRest() + markAllCompleted(clazz, module) } - def completeRest(): Unit = thisUniverse.synchronized { + def completeRest(): Unit = gilSynchronized { val tparams = clazz.rawInfo.typeParams val parents = try { parentsLevel += 1 val jsuperclazz = jclazz.getGenericSuperclass val ifaces = jclazz.getGenericInterfaces.toList map typeToScala - val isAnnotation = (jclazz.getModifiers & JAVA_ACC_ANNOTATION) != 0 + val isAnnotation = JavaAccFlags(jclazz).isAnnotation if (isAnnotation) AnnotationClass.tpe :: ClassfileAnnotationClass.tpe :: ifaces - else (if (jsuperclazz == null) AnyClass.tpe else typeToScala(jsuperclazz)) :: ifaces + else (if (jsuperclazz == null) AnyTpe else typeToScala(jsuperclazz)) :: ifaces } finally { parentsLevel -= 1 } @@ -685,7 +760,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) } - def enter(sym: Symbol, mods: Int) = followStatic(clazz, module, mods).info.decls enter sym + def enter(sym: Symbol, mods: JavaAccFlags) = followStatic(clazz, module, mods).info.decls enter sym def enterEmptyCtorIfNecessary(): Unit = { if (jclazz.getConstructors.isEmpty) @@ -697,23 +772,15 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // no need to call enter explicitly } - 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 ::= { () => + jclazz.getDeclaredFields foreach (f => enter(jfieldAsScala(f), f.javaFlags)) + jclazz.getDeclaredMethods foreach (m => enter(jmethodAsScala(m), m.javaFlags)) + jclazz.getConstructors foreach (c => enter(jconstrAsScala(c), c.javaFlags)) enterEmptyCtorIfNecessary() - - } :: pendingLoadActions + } if (parentsLevel == 0) { - while (!pendingLoadActions.isEmpty) { + while (pendingLoadActions.nonEmpty) { val item = pendingLoadActions.head pendingLoadActions = pendingLoadActions.tail item() @@ -724,6 +791,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { override def complete(sym: Symbol) { completeRest() + markAllCompleted(clazz, module) } } } @@ -732,12 +800,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * 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): Symbol = followStatic(clazz, clazz.companionModule, mods) + private def followStatic(clazz: Symbol, mods: JavaAccFlags): Symbol = followStatic(clazz, clazz.companionModule, mods) - private def followStatic(clazz: Symbol, module: Symbol, mods: Int): Symbol = + private def followStatic(clazz: Symbol, module: Symbol, mods: JavaAccFlags): Symbol = // SI-8196 `orElse(clazz)` needed for implementation details of the backend, such as the static // field containing the cache for structural calls. - if (jModifier.isStatic(mods)) module.moduleClass.orElse(clazz) else clazz + if (mods.isStatic) module.moduleClass.orElse(clazz) else clazz /** Methods which need to be treated with care * because they either are getSimpleName or call getSimpleName: @@ -769,39 +837,25 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni /** * 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 - } + // @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. + private def sOwner(jclazz: jClass[_]): Symbol = jclazz match { + case PrimitiveOrArray() => ScalaPackageClass + case EnclosedInMethod(jowner) => methodToScala(jowner) + case EnclosedInConstructor(jowner) => constructorToScala(jowner) + case EnclosedInClass(jowner) => followStatic(classToScala(jowner), jclazz.javaFlags) + case EnclosedInPackage(jowner) => packageToScala(jowner).moduleClass + case _ => packageNameToScala(jclazz.getName take jclazz.getName.lastIndexOf('.')).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) + followStatic(classToScala(jmember.getDeclaringClass), jmember.javaFlags) } /** @@ -817,9 +871,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * 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 + def approximateMatch(sym: Symbol, jstr: String): Boolean = ( + (sym.name string_== jstr) + || sym.isPrivate && (nme.expandedName(sym.name.toTermName, sym.owner) string_== jstr) + ) clazz.info.decl(newTermName(jname)) orElse { (clazz.info.decls.iterator filter (approximateMatch(_, jname))).toList match { @@ -841,7 +896,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def methodToScala1(jmeth: jMethod): MethodSymbol = { val jOwner = jmeth.getDeclaringClass val preOwner = classToScala(jOwner) - val owner = followStatic(preOwner, jmeth.getModifiers) + val owner = followStatic(preOwner, jmeth.javaFlags) (lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)) .asMethod } @@ -855,26 +910,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni toScala(constructorCache, jconstr)(_ constructorToScala1 _) private def constructorToScala1(jconstr: jConstructor[_]): MethodSymbol = { - val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers) + val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.javaFlags) (lookup(owner, jconstr.getName) suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr)) .asMethod } /** - * 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)).asTerm - } - - /** * The Scala package corresponding to given Java package */ def packageToScala(jpkg: jPackage): ModuleSymbol = packageCache.toScala(jpkg) { @@ -896,19 +937,20 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * The Scala package with given fully qualified name. Unlike `packageNameToScala`, * this one bypasses the cache. */ - private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = { + private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = gilSynchronized { val split = fullname lastIndexOf '.' val ownerModule: ModuleSymbol = if (split > 0) packageNameToScala(fullname take split) else this.RootPackage val owner = ownerModule.moduleClass val name = (fullname: TermName) drop split + 1 val opkg = owner.info decl name - if (opkg.isPackage) + if (opkg.hasPackageFlag) opkg.asModule else if (opkg == NoSymbol) { val pkg = owner.newPackage(name) pkg.moduleClass setInfo new LazyPackageType pkg setInfoAndEnter pkg.moduleClass.tpe + markFlagsCompleted(pkg)(mask = AllFlags) info("made Scala "+pkg) pkg } else @@ -977,8 +1019,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni javaTypeToValueClass(jclazz) orElse lookupClass assert (cls.isType, - sm"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"} - | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""") + (if (cls != NoSymbol) s"not a type: symbol $cls" else "no symbol could be") + + s" loaded from $jclazz in $owner with name $simpleName and classloader $classLoader") cls.asClass } @@ -995,7 +1037,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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.asType + case PolyType(tparams, _) => tparams.find(_.name string_== jparam.getName).get.asType } } @@ -1007,6 +1049,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case jmeth: jMethod => methodToScala(jmeth) case jconstr: jConstructor[_] => constructorToScala(jconstr) } + def reflectMemberToScala(m: jMember): Symbol = m match { + case x: GenericDeclaration => genericDeclarationToScala(x) + case x: jField => jfieldAsScala(x) + } /** * Given some Java type arguments, a corresponding list of Scala types, plus potentially @@ -1059,18 +1105,15 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * @param jclazz The Java class * @return A Scala class symbol that wraps all reflection info of `jclazz` */ - private def jclassAsScala(jclazz: jClass[_]): Symbol = { - val clazz = sOwner(jclazz) // sOwner called outside of closure for binary compatibility - toScala(classCache, jclazz){ (mirror, jclazz) => - mirror.jclassAsScala(jclazz, clazz) - } - } + private def jclassAsScala(jclazz: jClass[_]): ClassSymbol = + toScala(classCache, jclazz)(_ jclassAsScala1 _) + + private def jclassAsScala1(jclazz: jClass[_]): ClassSymbol = { + val owner = sOwner(jclazz) + val name = scalaSimpleName(jclazz) + val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, 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, _) = createClassModule(owner, name, completer) - clazz + initAndEnterClassAndModule(owner, name, completer)._1 } /** @@ -1084,11 +1127,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jfieldAsScala1(jfield: jField): TermSymbol = { val field = sOwner(jfield) - .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers)) + .newValue(newTermName(jfield.getName), NoPosition, jfield.scalaFlags) .setInfo(typeToScala(jfield.getGenericType)) - fieldCache enter (jfield, field) - importPrivateWithinFromJavaFlags(field, jfield.getModifiers) + + fieldCache.enter(jfield, field) + propagatePackageBoundary(jfield, field) copyAnnotations(field, jfield) + markAllCompleted(field) field } @@ -1107,15 +1152,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jmethodAsScala1(jmeth: jMethod): MethodSymbol = { val clazz = sOwner(jmeth) - val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, toScalaMethodFlags(jmeth.getModifiers)) + val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, jmeth.scalaFlags) 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) - importPrivateWithinFromJavaFlags(meth, jmeth.getModifiers) + propagatePackageBoundary(jmeth.javaFlags, meth) copyAnnotations(meth, jmeth) - if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info)) + if (jmeth.javaFlags.isVarargs) meth modifyInfo arrayToRepeated + markAllCompleted(meth) meth } @@ -1131,26 +1177,20 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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)) + val constr = clazz.newConstructor(NoPosition, jconstr.scalaFlags) constructorCache enter (jconstr, constr) val tparams = jconstr.getTypeParameters.toList map createTypeParameter val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala - setMethType(constr, tparams, paramtpes, clazz.tpe) + setMethType(constr, tparams, paramtpes, clazz.tpe_*) constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) - importPrivateWithinFromJavaFlags(constr, jconstr.getModifiers) + propagatePackageBoundary(jconstr.javaFlags, constr) copyAnnotations(constr, jconstr) + markAllCompleted(constr) 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 @@ -1166,16 +1206,18 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni valueClassToJavaType(clazz) else if (clazz == ArrayClass) noClass - else if (clazz.owner.isPackageClass) + else if (clazz.isTopLevel) javaClass(clazz.javaClassName) else if (clazz.owner.isClass) { - val childOfClass = !clazz.owner.isModuleClass - val childOfTopLevel = clazz.owner.owner.isPackageClass + val childOfClass = !clazz.owner.isModuleClass + val childOfTopLevel = clazz.owner.isTopLevel val childOfTopLevelObject = clazz.owner.isModuleClass && childOfTopLevel // suggested in https://issues.scala-lang.org/browse/SI-4023?focusedCommentId=54759#comment-54759 var ownerClazz = classToJava(clazz.owner.asClass) - if (childOfTopLevelObject) ownerClazz = Class.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + if (childOfTopLevelObject) + ownerClazz = jClass.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + val ownerChildren = ownerClazz.getDeclaredClasses var fullNameOfJavaClass = ownerClazz.getName @@ -1208,11 +1250,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni else sym.name.toString /** The Java field corresponding to a given Scala field. - * @param meth The Scala field. + * @param fld The Scala field. */ def fieldToJava(fld: TermSymbol): jField = fieldCache.toJava(fld) { val jclazz = classToJava(fld.owner.asClass) - val jname = nme.dropLocalSuffix(fld.name).toString + val jname = fld.name.dropLocal.toString try jclazz getDeclaredField jname catch { case ex: NoSuchFieldException => jclazz getDeclaredField expandedName(fld) @@ -1225,7 +1267,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def methodToJava(meth: MethodSymbol): jMethod = methodCache.toJava(meth) { val jclazz = classToJava(meth.owner.asClass) val paramClasses = transformedType(meth).paramTypes map typeToJavaClass - val jname = nme.dropLocalSuffix(meth.name).toString + val jname = meth.name.dropLocal.toString try jclazz getDeclaredMethod (jname, paramClasses: _*) catch { case ex: NoSuchMethodException => @@ -1242,7 +1284,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val effectiveParamClasses = if (!constr.owner.owner.isStaticOwner) jclazz.getEnclosingClass +: paramClasses else paramClasses - jclazz getConstructor (effectiveParamClasses: _*) + jclazz getDeclaredConstructor (effectiveParamClasses: _*) } private def jArrayClass(elemClazz: jClass[_]): jClass[_] = { @@ -1253,12 +1295,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * 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.asClass) + case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) + case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) + case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias) - case SingleType(_, sym: ModuleSymbol) => classToJava(sym.moduleClass.asClass) - case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") + case SingleType(_, sym: ModuleSymbol) => classToJava(sym.moduleClass.asClass) + case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") } } @@ -1277,11 +1319,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case _ => abort(s"${sym}.enclosingRootClass = ${sym.enclosingRootClass}, which is not a RootSymbol") } - private lazy val syntheticCoreClasses: Map[(String, Name), Symbol] = { - def mapEntry(sym: Symbol): ((String, Name), Symbol) = (sym.owner.fullName, sym.name) -> sym - Map() ++ (definitions.syntheticCoreClasses map mapEntry) - } - /** 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 @@ -1291,22 +1328,20 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def missingHook(owner: Symbol, name: Name): Symbol = { if (owner.hasPackageFlag) { val mirror = mirrorThatLoaded(owner) - // todo. 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) - syntheticCoreClasses get (owner.fullName, name) match { - case Some(tsym) => - // synthetic core classes are only present in root mirrors - // because Definitions.scala, which initializes and enters them, only affects rootMirror - // therefore we need to enter them manually for non-root mirrors - if (mirror ne thisUniverse.rootMirror) owner.info.decls enter tsym - return tsym - case None => - } + if (name == tpnme.AnyRef && owner.owner.isRoot && owner.name == tpnme.scala_) + // when we synthesize the scala.AnyRef symbol, we need to add it to the scope of the scala package + // the problem is that adding to the scope implies doing something like `owner.info.decls enter anyRef` + // which entails running a completer for the scala package + // which will try to unpickle the stuff in scala/package.class + // which will transitively load scala.AnyRef + // which doesn't exist yet, because it hasn't been added to the scope yet + // this missing hook ties the knot without introducing synchronization problems like before + return definitions.AnyRefClass } info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) super.missingHook(owner, name) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index 1b69ca4e89..b5446694ed 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -1,7 +1,11 @@ -package scala.reflect +package scala +package reflect package runtime -import internal.{SomePhase, NoPhase, Phase, TreeGen} +import scala.reflect.internal.{TreeInfo, SomePhase} +import scala.reflect.internal.{SymbolTable => InternalSymbolTable} +import scala.reflect.runtime.{SymbolTable => RuntimeSymbolTable} +import scala.reflect.api.{TreeCreator, TypeCreator, Universe} /** An implementation of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders. * @@ -9,20 +13,125 @@ import internal.{SomePhase, NoPhase, Phase, TreeGen} * * @contentDiagram hideNodes "*Api" "*Extractor" */ -class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.SymbolTable { self => +class JavaUniverse extends InternalSymbolTable with JavaUniverseForce with ReflectSetup with RuntimeSymbolTable { self => + override def inform(msg: String): Unit = log(msg) def picklerPhase = SomePhase - + def erasurePhase = SomePhase lazy val settings = new Settings - def forInteractive = false - def forScaladoc = false + private val isLogging = sys.props contains "scala.debug.reflect" - def log(msg: => AnyRef): Unit = if (settings.debug.value) println(" [] "+msg) + def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) type TreeCopier = InternalTreeCopierOps + implicit val TreeCopierTag: ClassTag[TreeCopier] = ClassTag[TreeCopier](classOf[TreeCopier]) def newStrictTreeCopier: TreeCopier = new StrictTreeCopier def newLazyTreeCopier: TreeCopier = new LazyTreeCopier + def currentFreshNameCreator = globalFreshNameCreator + + override lazy val internal: Internal = new SymbolTableInternal { + override def typeTagToManifest[T: ClassTag](mirror0: Any, tag: Universe # TypeTag[T]): Manifest[T] = { + // SI-6239: make this conversion more precise + val mirror = mirror0.asInstanceOf[Mirror] + val runtimeClass = mirror.runtimeClass(tag.in(mirror).tpe) + Manifest.classType(runtimeClass).asInstanceOf[Manifest[T]] + } + override def manifestToTypeTag[T](mirror0: Any, manifest: Manifest[T]): Universe # TypeTag[T] = + TypeTag(mirror0.asInstanceOf[Mirror], new TypeCreator { + def apply[U <: Universe with Singleton](mirror: scala.reflect.api.Mirror[U]): U # Type = { + mirror.universe match { + case ju: JavaUniverse => + val jm = mirror.asInstanceOf[ju.Mirror] + val sym = jm.classSymbol(manifest.runtimeClass) + val tpe = + if (manifest.typeArguments.isEmpty) sym.toType + else { + val tags = manifest.typeArguments map (targ => ju.internal.manifestToTypeTag(jm, targ)) + ju.appliedType(sym.toTypeConstructor, tags map (_.in(jm).tpe)) + } + tpe.asInstanceOf[U # Type] + case u => + u.internal.manifestToTypeTag(mirror.asInstanceOf[u.Mirror], manifest).in(mirror).tpe + } + } + }) + } + + // can't put this in runtime.Trees since that's mixed with Global in ReflectGlobal, which has the definition from internal.Trees + object treeInfo extends { + val global: JavaUniverse.this.type = JavaUniverse.this + } with TreeInfo + init() -} + // ======= Initialization of runtime reflection ======= + // + // This doc describes the carefully laid out sequence of actions used to initialize reflective universes. + // + // Before reading the text below, read up the section Mirrors in the reflection pre-SIP + // https://docs.google.com/document/d/1nAwSw4TmMplsIlzh2shYLUJ5mVh3wndDa1Zm1H6an9A/edit. + // Take an especially good look at Figure 2, because it illustrates fundamental principles underlying runtime reflection: + // 1) For each universe we have one mirror per classloader + // 2) Package symbols are per-mirror + // 3) Other symbols are per-universe, which means that a symbol (e.g. Seq on the picture) might be shared between multiple owners + // + // Main challenges that runtime reflection presents wrt initialization are: + // 1) Extravagant completion scheme that enters package members on-demand rather than a result of scanning a directory with class files. + // (That's a direct consequence of the fact that in general case we can't enumerate all classes in a classloader. + // As Paul rightfully mentioned, we could specialcase classloaders that point to filesystems, but that is left for future work). + // 2) Presence of synthetic symbols that aren't loaded by normal means (from classfiles) but are synthesized on-the-fly, + // and the necessity to propagate these synthetic symbols from rootMirror to other mirrors, + // complicated by the fact that such symbols depend on normal symbols (e.g. AnyRef depends on Object). + // 3) Necessity to remain thread-safe, which limits our options related to lazy initialization + // (E.g. we cannot use missingHook to enter synthetic symbols, because that's thread-unsafe). + // + // Directly addressing the challenge #3, we create all synthetic symbols fully in advance during init(). + // However, it's not that simple as just calling definitions.symbolsNotPresentInBytecode. + // Before doing that, we need to first initialize ObjectClass, then ScalaPackageClass, and only then deal with synthetics. + // Below you can find a detailed explanation for that. + // + // ### Why ScalaPackageClass? ### + // + // Forcing ScalaPackageClass first thing during startup is important, because syntheticCoreClasses such as AnyRefClass + // need to be entered into ScalaPackageClass, which entails calling ScalaPackageClass.info.decls.enter. + // If ScalaPackageClass isn't initialized by that moment, the following will happen for runtime reflection: + // 1) Initialization of ScalaPackageClass will trigger unpickling. + // 2) Unpickling will need to load some auxiliary types such as, for example, String. + // 3) To load String, runtime reflection will call mirrorDefining(classOf[String]). + // 4) This, in turn, will call runtimeMirror(classOf[String].getClassLoader). + // 5) For some classloader configurations, the resulting mirror will be different from rootMirror. + // 6) In that case, initialization of the resulting mirror will try to import definitions.syntheticCoreClasses into the mirror. + // 7) This will force all the lazy vals corresponding to syntheticCoreClasses. + // 8) By that time, the completer of ScalaPackageClass will have already called setInfo on ScalaPackageClass, so there won't be any stack overflow. + // + // So far so good, no crashes, no problems, right? Not quite. + // If forcing of ScalaPackageClass was called by a syntheticCoreClasses lazy val, + // then this lazy val will be entered twice: once during step 7 and once when returning from the original call. + // To avoid this we need to initialize ScalaPackageClass prior to other synthetics. + // + // ### Why ObjectClass? ### + // + // 1) As explained in JavaMirrors.missingHook, initialization of ScalaPackageClass critically depends on AnyRefClass. + // 2) AnyRefClass is defined as "lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe)", + // which means that initialization of AnyRefClass depends on ObjectClass. + // 3) ObjectClass is defined as "lazy val ObjectClass = getRequiredClass(sn.Object.toString)", + // which means that under some classloader configurations (see JavaMirrors.missingHook for more details) + // dereferencing ObjectClass might trigger an avalanche of initializations calling back into AnyRefClass + // while another AnyRefClass initializer is still on stack. + // 4) That will lead to AnyRefClass being entered two times (once when the recursive call returns and once when the original one returns) + // 5) That will crash PackageScope.enter that helpfully detects double-enters. + // + // Therefore, before initializing ScalaPackageClass, we must pre-initialize ObjectClass + def init() { + definitions.init() + + // workaround for http://groups.google.com/group/scala-internals/browse_thread/thread/97840ba4fd37b52e + // constructors are by definition single-threaded, so we initialize all lazy vals (and local object) in advance + // in order to avoid deadlocks later (e.g. one thread holds a global reflection lock and waits for definitions.Something to initialize, + // whereas another thread holds a definitions.Something initialization lock and needs a global reflection lock to complete the initialization) + + // TODO Convert this into a macro + force() + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala new file mode 100644 index 0000000000..dcd262c288 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -0,0 +1,448 @@ +// Generated Code, validated by run/t6240-universe-code-gen.scala +package scala.reflect +package runtime + +trait JavaUniverseForce { self: runtime.JavaUniverse => + def force() { + Literal(Constant(42)).duplicate + nme.flattenedName() + nme.raw + WeakTypeTag + TypeTag + TypeTag.Byte.tpe + TypeTag.Short.tpe + TypeTag.Char.tpe + TypeTag.Int.tpe + TypeTag.Long.tpe + TypeTag.Float.tpe + TypeTag.Double.tpe + TypeTag.Boolean.tpe + TypeTag.Unit.tpe + TypeTag.Any.tpe + TypeTag.AnyVal.tpe + TypeTag.AnyRef.tpe + TypeTag.Object.tpe + TypeTag.Nothing.tpe + TypeTag.Null.tpe + + this.settings + this.internal + this.treeInfo + this.rootMirror + this.traceSymbols + this.perRunCaches + this.compat + this.treeBuild + this.FreshNameExtractor + this.FixedMirrorTreeCreator + this.FixedMirrorTypeCreator + this.CompoundTypeTreeOriginalAttachment + this.BackquotedIdentifierAttachment + this.ForAttachment + this.SyntheticUnitAttachment + this.SubpatternsAttachment + this.noPrint + this.typeDebug + this.Range + // inaccessible: this.posAssigner + this.ConsoleWriter + this.RefTree + this.PackageDef + this.ClassDef + this.ModuleDef + this.ValOrDefDef + this.ValDef + this.DefDef + this.TypeDef + this.LabelDef + this.ImportSelector + this.Import + this.Template + this.Block + this.CaseDef + this.Alternative + this.Star + this.Bind + this.UnApply + this.ArrayValue + this.Function + this.Assign + this.AssignOrNamedArg + this.If + this.Match + this.Return + this.Try + this.Throw + this.New + this.Typed + this.TypeApply + this.Apply + this.ApplyDynamic + this.Super + this.This + this.Select + this.Ident + this.ReferenceToBoxed + this.Literal + this.Annotated + this.SingletonTypeTree + this.SelectFromTypeTree + this.CompoundTypeTree + this.AppliedTypeTree + this.TypeBoundsTree + this.ExistentialTypeTree + this.TypeTree + this.Modifiers + this.EmptyTree + this.noSelfType + this.pendingSuperCall + this.emptyValDef + this.EmptyTreeTypeSubstituter + this.UnmappableAnnotArg + this.LiteralAnnotArg + this.ArrayAnnotArg + this.NestedAnnotArg + this.ScalaSigBytes + this.AnnotationInfo + this.Annotation + this.UnmappableAnnotation + this.ErroneousAnnotation + this.ThrownException + this.typeNames + this.tpnme + this.fulltpnme + this.binarynme + this.termNames + this.nme + this.sn + this.Constant + this.definitions + this.LookupSucceeded + this.LookupAmbiguous + this.LookupInaccessible + this.LookupNotFound + this.Scope + this.EmptyScope + this.Flag + this.KindErrors + this.Kind + this.ProperTypeKind + this.TypeConKind + this.inferKind + this.UnmappableTree + this.ErrorType + this.WildcardType + this.BoundedWildcardType + this.NoType + this.NoPrefix + this.ThisType + this.SingleType + this.SuperType + this.TypeBounds + this.CompoundType + this.baseClassesCycleMonitor + this.RefinedType + this.ClassInfoType + this.ConstantType + this.TypeRef + this.MethodType + this.NullaryMethodType + this.PolyType + this.ExistentialType + this.OverloadedType + this.ImportType + this.AntiPolyType + this.HasTypeMember + this.ArrayTypeRef + this.TypeVar + this.AnnotatedType + this.StaticallyAnnotatedType + this.NamedType + this.RepeatedType + this.ErasedValueType + this.GenPolyType + this.unwrapToClass + this.unwrapToStableClass + this.unwrapWrapperTypes + this.RecoverableCyclicReference + this.TypeConstraint + this.normalizeAliases + this.dropSingletonType + this.abstractTypesToBounds + this.dropIllegalStarTypes + this.IsDependentCollector + this.ApproximateDependentMap + this.wildcardToTypeVarMap + this.typeVarToOriginMap + this.ErroneousCollector + this.adaptToNewRunMap + this.SubTypePair + this.SymbolKind + this.NoSymbol + this.CyclicReference + this.SymbolOps + this.TermName + this.TypeName + this.Liftable + this.Unliftable + this.BooleanFlag + this.WeakTypeTag + this.TypeTag + this.Expr + this.NoMods + definitions.JavaLangPackage + definitions.JavaLangPackageClass + definitions.ScalaPackage + definitions.ScalaPackageClass + definitions.RuntimePackage + definitions.RuntimePackageClass + definitions.AnyClass + definitions.AnyRefClass + definitions.ObjectClass + definitions.AnyRefTpe + definitions.AnyTpe + definitions.AnyValTpe + definitions.BoxedUnitTpe + definitions.NothingTpe + definitions.NullTpe + definitions.ObjectTpe + definitions.SerializableTpe + definitions.StringTpe + definitions.ThrowableTpe + definitions.ConstantTrue + definitions.ConstantFalse + definitions.ConstantNull + definitions.AnyValClass + definitions.RuntimeNothingClass + definitions.RuntimeNullClass + definitions.NothingClass + definitions.NullClass + definitions.ClassCastExceptionClass + definitions.IndexOutOfBoundsExceptionClass + definitions.InvocationTargetExceptionClass + definitions.MatchErrorClass + definitions.NonLocalReturnControlClass + definitions.NullPointerExceptionClass + definitions.ThrowableClass + definitions.UninitializedErrorClass + definitions.UninitializedFieldConstructor + definitions.PartialFunctionClass + definitions.AbstractPartialFunctionClass + definitions.SymbolClass + definitions.StringClass + definitions.StringModule + definitions.ClassClass + definitions.DynamicClass + definitions.SysPackage + definitions.UnqualifiedModules + definitions.UnqualifiedOwners + definitions.PredefModule + definitions.SpecializableModule + definitions.ScalaRunTimeModule + definitions.SymbolModule + definitions.StringAddClass + definitions.ScalaNumberClass + definitions.TraitSetterAnnotationClass + definitions.DelayedInitClass + definitions.TypeConstraintClass + definitions.SingletonClass + definitions.SerializableClass + definitions.JavaSerializableClass + definitions.ComparableClass + definitions.JavaCloneableClass + definitions.JavaNumberClass + definitions.JavaEnumClass + definitions.RemoteInterfaceClass + definitions.RemoteExceptionClass + definitions.ByNameParamClass + definitions.JavaRepeatedParamClass + definitions.RepeatedParamClass + definitions.ConsClass + definitions.IteratorClass + definitions.IterableClass + definitions.ListClass + definitions.SeqClass + definitions.StringBuilderClass + definitions.TraversableClass + definitions.ListModule + definitions.NilModule + definitions.SeqModule + definitions.ArrayModule + definitions.ArrayModule_overloadedApply + definitions.ArrayClass + definitions.Array_apply + definitions.Array_update + definitions.Array_length + definitions.Array_clone + definitions.SoftReferenceClass + definitions.MethodClass + definitions.EmptyMethodCacheClass + definitions.MethodCacheClass + definitions.ScalaXmlTopScope + definitions.ScalaXmlPackage + definitions.ReflectPackage + definitions.ReflectApiPackage + definitions.ReflectRuntimePackage + definitions.UniverseClass + definitions.PartialManifestModule + definitions.FullManifestClass + definitions.FullManifestModule + definitions.OptManifestClass + definitions.NoManifest + definitions.TreesClass + definitions.ExprsClass + definitions.ClassTagModule + definitions.ClassTagClass + definitions.TypeTagsClass + definitions.ApiUniverseClass + definitions.JavaUniverseClass + definitions.MirrorClass + definitions.TypeCreatorClass + definitions.TreeCreatorClass + definitions.BlackboxContextClass + definitions.WhiteboxContextClass + definitions.MacroImplAnnotation + definitions.StringContextClass + definitions.QuasiquoteClass + definitions.QuasiquoteClass_api + definitions.QuasiquoteClass_api_apply + definitions.QuasiquoteClass_api_unapply + definitions.ScalaSignatureAnnotation + definitions.ScalaLongSignatureAnnotation + definitions.OptionClass + definitions.OptionModule + definitions.SomeClass + definitions.NoneModule + definitions.SomeModule + definitions.VarArityClass + definitions.ProductClass + definitions.TupleClass + definitions.FunctionClass + definitions.AbstractFunctionClass + definitions.MacroContextType + definitions.ProductRootClass + definitions.Any_$eq$eq + definitions.Any_$bang$eq + definitions.Any_equals + definitions.Any_hashCode + definitions.Any_toString + definitions.Any_$hash$hash + definitions.Any_getClass + definitions.Any_isInstanceOf + definitions.Any_asInstanceOf + definitions.primitiveGetClassMethods + definitions.getClassMethods + definitions.Object_$hash$hash + definitions.Object_$eq$eq + definitions.Object_$bang$eq + definitions.Object_eq + definitions.Object_ne + definitions.Object_isInstanceOf + definitions.Object_asInstanceOf + definitions.Object_synchronized + definitions.String_$plus + definitions.ObjectRefClass + definitions.VolatileObjectRefClass + definitions.RuntimeStaticsModule + definitions.BoxesRunTimeModule + definitions.BoxesRunTimeClass + definitions.BoxedNumberClass + definitions.BoxedCharacterClass + definitions.BoxedBooleanClass + definitions.BoxedByteClass + definitions.BoxedShortClass + definitions.BoxedIntClass + definitions.BoxedLongClass + definitions.BoxedFloatClass + definitions.BoxedDoubleClass + definitions.BoxedUnitClass + definitions.BoxedUnitModule + definitions.AnnotationClass + definitions.ClassfileAnnotationClass + definitions.StaticAnnotationClass + definitions.BridgeClass + definitions.ElidableMethodClass + definitions.ImplicitNotFoundClass + definitions.MigrationAnnotationClass + definitions.ScalaStrictFPAttr + definitions.SwitchClass + definitions.TailrecClass + definitions.VarargsClass + definitions.uncheckedStableClass + definitions.uncheckedVarianceClass + definitions.BeanPropertyAttr + definitions.BooleanBeanPropertyAttr + definitions.CompileTimeOnlyAttr + definitions.DeprecatedAttr + definitions.DeprecatedNameAttr + definitions.DeprecatedInheritanceAttr + definitions.DeprecatedOverridingAttr + definitions.NativeAttr + definitions.RemoteAttr + definitions.ScalaInlineClass + definitions.ScalaNoInlineClass + definitions.SerialVersionUIDAttr + definitions.SerialVersionUIDAnnotation + definitions.SpecializedClass + definitions.ThrowsClass + definitions.TransientAttr + definitions.UncheckedClass + definitions.UncheckedBoundsClass + definitions.UnspecializedClass + definitions.VolatileAttr + definitions.BeanGetterTargetClass + definitions.BeanSetterTargetClass + definitions.FieldTargetClass + definitions.GetterTargetClass + definitions.ParamTargetClass + definitions.SetterTargetClass + definitions.ObjectTargetClass + definitions.ClassTargetClass + definitions.MethodTargetClass + definitions.LanguageFeatureAnnot + definitions.languageFeatureModule + definitions.metaAnnotations + definitions.AnnotationDefaultAttr + definitions.isPhantomClass + definitions.syntheticCoreClasses + definitions.syntheticCoreMethods + definitions.hijackedCoreClasses + definitions.symbolsNotPresentInBytecode + definitions.isPossibleSyntheticParent + definitions.abbrvTag + definitions.numericWeight + definitions.boxedModule + definitions.boxedClass + definitions.refClass + definitions.volatileRefClass + definitions.UnitClass + definitions.ByteClass + definitions.ShortClass + definitions.CharClass + definitions.IntClass + definitions.LongClass + definitions.FloatClass + definitions.DoubleClass + definitions.BooleanClass + definitions.UnitTpe + definitions.ByteTpe + definitions.ShortTpe + definitions.CharTpe + definitions.IntTpe + definitions.LongTpe + definitions.FloatTpe + definitions.DoubleTpe + definitions.BooleanTpe + definitions.ScalaNumericValueClasses + definitions.ScalaValueClassesNoUnit + definitions.ScalaValueClasses + + + erasure.GenericArray + erasure.scalaErasure + erasure.specialScalaErasure + erasure.javaErasure + erasure.verifiedJavaErasure + erasure.boxingErasure + } +} diff --git a/src/reflect/scala/reflect/runtime/ReflectSetup.scala b/src/reflect/scala/reflect/runtime/ReflectSetup.scala index 6e28fc8520..6a364ff0be 100644 --- a/src/reflect/scala/reflect/runtime/ReflectSetup.scala +++ b/src/reflect/scala/reflect/runtime/ReflectSetup.scala @@ -1,7 +1,8 @@ -package scala.reflect +package scala +package reflect package runtime -import internal.{SomePhase, NoPhase, Phase, TreeGen} +import internal.{SomePhase, NoPhase, Phase} /** A helper trait to initialize things that need to be set before JavaMirrors and other * reflect specific traits are initialized */ diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index ffed3cc38e..a4bd698068 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -3,14 +3,18 @@ * @author Paul Phillips */ -package scala.reflect.runtime +package scala +package reflect.runtime import java.lang.{Class => jClass} import java.lang.reflect.{ Method, InvocationTargetException, UndeclaredThrowableException } +import scala.reflect.internal.util.AbstractFileClassLoader +import scala.reflect.io._ +import java.io.{File => JFile} /** A few java-reflection oriented utility functions useful during reflection bootstrapping. */ -private[scala] object ReflectionUtils { +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 @@ -33,8 +37,8 @@ private[scala] object ReflectionUtils { def isAbstractFileClassLoader(clazz: Class[_]): Boolean = { if (clazz == null) return false - if (clazz.getName == "scala.tools.nsc.interpreter.AbstractFileClassLoader") return true - return isAbstractFileClassLoader(clazz.getSuperclass) + if (clazz == classOf[AbstractFileClassLoader]) return true + isAbstractFileClassLoader(clazz.getSuperclass) } def inferClasspath(cl: ClassLoader): String = cl match { case cl: java.net.URLClassLoader => @@ -82,4 +86,87 @@ private[scala] object ReflectionUtils { def scalacShouldntLoadClassfile(fileName: String) = isTraitImplementation(fileName) def scalacShouldntLoadClass(name: scala.reflect.internal.SymbolTable#Name) = scalacShouldntLoadClassfile(name + ".class") + + object PrimitiveOrArray { + def unapply(jclazz: jClass[_]) = jclazz.isPrimitive || jclazz.isArray + } + + class EnclosedIn[T](enclosure: jClass[_] => T) { + def unapply(jclazz: jClass[_]): Option[T] = if (enclosure(jclazz) != null) Some(enclosure(jclazz)) else None + } + + object EnclosedInMethod extends EnclosedIn(_.getEnclosingMethod) + object EnclosedInConstructor extends EnclosedIn(_.getEnclosingConstructor) + object EnclosedInClass extends EnclosedIn(_.getEnclosingClass) + object EnclosedInPackage extends EnclosedIn(_.getPackage) + + def associatedFile(clazz: Class[_]): AbstractFile = { + // TODO: I agree with Jason - this implementation isn't something that we'd like to support + // therefore I'm having it commented out and this function will now return NoAbstractFile + // I think we can keep the source code though, because it can be useful to the others + // + // def inferAssociatedFile(clazz: Class[_]): AbstractFile = { + // // http://stackoverflow.com/questions/227486/find-where-java-class-is-loaded-from + // try { + // var cl = clazz.getClassLoader() + // if (cl == null) { + // cl = ClassLoader.getSystemClassLoader() + // while (cl != null && cl.getParent != null) cl = cl.getParent + // } + // var result: AbstractFile = null + // if (cl != null) { + // val name = clazz.getCanonicalName() + // val resource = cl.getResource(name.replace(".", "/") + ".class") + // if (resource != null) { + // def fromFile(file: String) = AbstractFile.getFile(file) + // def fromJarEntry(jarfile: String, entrypath: String) = { + // val jar = fromFile(jarfile) + // new VirtualFile(clazz.getName, entrypath) { + // lazy val impl: AbstractFile = { + // def loop(root: AbstractFile, path: List[String]): AbstractFile = { + // def find(name: String) = root.iterator.find(_.name == name).getOrElse(NoAbstractFile) + // path match { + // case step :: Nil => find(step) + // case step :: rest => loop(find(step), rest) + // case Nil => NoAbstractFile + // } + // } + // loop(ZipArchive.fromFile(new JFile(jarfile)), entrypath.split("/").toList) + // } + // override def container = impl.container + // override def lastModified = impl.lastModified + // override def input = impl.input + // override def sizeOption = impl.sizeOption + // override def underlyingSource = Some(jar) + // override def toString = jarfile + "(" + entrypath + ")" + // } + // } + // def fallback() = new VirtualFile(clazz.getName, resource.toString) + // result = resource.getProtocol match { + // case "file" => + // fromFile(resource.getFile) + // case "jar" => + // val intrajarUrl = new java.net.URL(resource.getFile) + // intrajarUrl.getProtocol match { + // case "file" => + // val file = intrajarUrl.getFile() + // val expectedSuffix = "!/" + name.replace(".", "/") + ".class" + // if (file.endsWith(expectedSuffix)) fromJarEntry(file.stripSuffix(expectedSuffix), expectedSuffix.substring(2)) + // else fallback() + // case _ => fallback() + // } + // case _ => + // fallback() + // } + // } + // } + // if (result != null) result else NoAbstractFile + // } catch { + // case _: Exception => NoAbstractFile + // } + // } + // inferAssociatedFile(clazz) + NoAbstractFile + } } + diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 0e0cf3fc40..27d574b1de 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.reflect.internal.settings.MutableSettings @@ -32,12 +33,15 @@ private[reflect] class Settings extends MutableSettings { val Xexperimental = new BooleanSetting(false) val XfullLubs = new BooleanSetting(false) val XnoPatmatAnalysis = new BooleanSetting(false) - val XoldPatmat = new BooleanSetting(false) + val strictInference = new BooleanSetting(false) val Xprintpos = new BooleanSetting(false) - val Ynotnull = new BooleanSetting(false) + val Yposdebug = new BooleanSetting(false) + val Yrangepos = new BooleanSetting(false) + val Yshowsymowners = new BooleanSetting(false) val Yshowsymkinds = new BooleanSetting(false) + val breakCycles = new BooleanSetting(false) val debug = new BooleanSetting(false) - val deepCloning = new BooleanSetting(false) + val developer = new BooleanSetting(false) val explaintypes = new BooleanSetting(false) val overrideObjects = new BooleanSetting(false) val printtypes = new BooleanSetting(false) @@ -46,4 +50,5 @@ private[reflect] class Settings extends MutableSettings { val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) + def isScala211 = true } diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index b895092639..7ba68b8733 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -1,10 +1,12 @@ -package scala.reflect +package scala +package reflect package runtime import internal.Flags import java.lang.{Class => jClass, Package => jPackage} import scala.collection.mutable import scala.reflect.runtime.ReflectionUtils.scalacShouldntLoadClass +import scala.reflect.internal.Flags._ private[reflect] trait SymbolLoaders { self: SymbolTable => @@ -16,37 +18,16 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => * is found, a package is created instead. */ class TopClassCompleter(clazz: Symbol, module: Symbol) extends SymLoader with FlagAssigningCompleter { -// 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 -// } -// } - + markFlagsCompleted(clazz, module)(mask = ~TopLevelPickledFlags) override def complete(sym: Symbol) = { debugInfo("completing "+sym+"/"+clazz.fullName) assert(sym == clazz || sym == module || sym == module.moduleClass) -// try { - atPhaseNotLaterThan(picklerPhase) { + slowButSafeEnteringPhaseNotLaterThan(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. -// } + // NOTE: can't mark as thread-safe here, because unpickleClass might decide to delegate to FromJavaClassCompleter + // if (!isCompilerUniverse) markAllCompleted(clazz, module) } } override def load(sym: Symbol) = complete(sym) @@ -58,7 +39,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => * @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) = { + protected def initAndEnterClassAndModule(owner: Symbol, name: TypeName, completer: (Symbol, Symbol) => LazyType) = { assert(!(name.toString endsWith "[]"), name) val clazz = owner.newClass(name) val module = owner.newModule(name.toTermName) @@ -68,7 +49,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => owner.info.decls enter clazz owner.info.decls enter module } - initClassModule(clazz, module, completer(clazz, module)) + initClassAndModule(clazz, module, completer(clazz, module)) (clazz, module) } @@ -76,7 +57,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => List(clazz, module, module.moduleClass) foreach (_ setInfo info) } - protected def initClassModule(clazz: Symbol, module: Symbol, completer: LazyType) = + protected def initClassAndModule(clazz: Symbol, module: Symbol, completer: LazyType) = setAllInfos(clazz, module, completer) /** The type completer for packages. @@ -87,15 +68,56 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym) // override def safeToString = pkgClass.toString openPackageModule(sym) + markAllCompleted(sym) } } - class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand + + // Since runtime reflection doesn't have a luxury of enumerating all classes + // on the classpath, it has to materialize symbols for top-level definitions + // (packages, classes, objects) on demand. + // + // Someone asks us for a class named `foo.Bar`? Easy. Let's speculatively create + // a package named `foo` and then look up `newTypeName("bar")` in its decls. + // This lookup, implemented in `SymbolLoaders.PackageScope` tests the waters by + // trying to to `Class.forName("foo.Bar")` and then creates a ClassSymbol upon + // success (the whole story is a bit longer, but the rest is irrelevant here). + // + // That's all neat, but these non-deterministic mutations of the global symbol + // table give a lot of trouble in multi-threaded setting. One of the popular + // reflection crashes happens when multiple threads happen to trigger symbol + // materialization multiple times for the same symbol, making subsequent + // reflective operations stumble upon outrageous stuff like overloaded packages. + // + // Short of significantly changing SymbolLoaders I see no other way than just + // to slap a global lock on materialization in runtime reflection. + class PackageScope(pkgClass: Symbol) extends Scope with SynchronizedScope { assert(pkgClass.isType) - // disable fingerprinting as we do not know entries beforehand - private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. - override def lookupEntry(name: Name): ScopeEntry = { + + // materializing multiple copies of the same symbol in PackageScope is a very popular bug + // this override does its best to guard against it + override def enter[T <: Symbol](sym: T): T = { + // workaround for SI-7728 + if (isCompilerUniverse) super.enter(sym) + else { + val existing = super.lookupEntry(sym.name) + assert(existing == null || existing.sym.isMethod, s"pkgClass = $pkgClass, sym = $sym, existing = $existing") + super.enter(sym) + } + } + + override def enterIfNew[T <: Symbol](sym: T): T = { + val existing = super.lookupEntry(sym.name) + if (existing == null) enter(sym) + else existing.sym.asInstanceOf[T] + } + + // package scopes need to synchronize on the GIL + // because lookupEntry might cause changes to the global symbol table + override def syncLockSynchronized[T](body: => T): T = gilSynchronized(body) + private val negatives = new mutable.HashSet[Name] + override def lookupEntry(name: Name): ScopeEntry = syncLockSynchronized { val e = super.lookupEntry(name) if (e != null) e @@ -109,17 +131,30 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => currentMirror.tryJavaClass(path) match { case Some(cls) => val loadingMirror = currentMirror.mirrorDefining(cls) - val (clazz, module) = + val (_, module) = if (loadingMirror eq currentMirror) { - createClassModule(pkgClass, name.toTypeName, new TopClassCompleter(_, _)) + initAndEnterClassAndModule(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 + // currentMirror.mirrorDefining(cls) might side effect by entering symbols into pkgClass.info.decls + // therefore, even though in the beginning of this method, super.lookupEntry(name) returned null + // entering clazz/module now will result in a double-enter assertion in PackageScope.enter + // here's how it might happen + // 1) we are the rootMirror + // 2) cls.getClassLoader is different from our classloader + // 3) mirrorDefining(cls) looks up a mirror corresponding to that classloader and cannot find it + // 4) mirrorDefining creates a new mirror + // 5) that triggers Mirror.init() of the new mirror + // 6) that triggers definitions.syntheticCoreClasses + // 7) that might materialize symbols and enter them into our scope (because syntheticCoreClasses live in rootMirror) + // 8) now we come back here and try to enter one of the now entered symbols => BAM! + // therefore we use enterIfNew rather than just enter + enterIfNew(clazz) + enterIfNew(module) (clazz, module) } debugInfo(s"created $module/${module.moduleClass} in $pkgClass") diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 5c08e9a508..02155578f8 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.reflect.internal.Flags._ @@ -8,13 +9,13 @@ import scala.reflect.internal.Flags._ * 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.reflect.ReflectGlobal) */ -private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { +private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps with Gil with ThreadLocalStorage { def info(msg: => String) = - if (settings.verbose.value) println("[reflect-compiler] "+msg) + if (settings.verbose) println("[reflect-compiler] "+msg) def debugInfo(msg: => String) = - if (settings.debug.value) info(msg) + if (settings.debug) info(msg) /** Declares that this is a runtime reflection universe. * @@ -27,19 +28,4 @@ private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors w * in order to prevent memory leaks: http://groups.google.com/group/scala-internals/browse_thread/thread/eabcf3d406dab8b2. */ override def isCompilerUniverse = false - - /** Unlike compiler universes, reflective universes can auto-initialize symbols on flag requests. - * - * scalac wasn't designed with such auto-initialization in mind, and quite often it makes assumptions - * that flag requests won't cause initialization. Therefore enabling auto-init leads to cyclic errors. - * We could probably fix those, but at the moment it's too risky. - * - * Reflective universes share codebase with scalac, but their surface is much smaller, which means less assumptions. - * These assumptions are taken care of in this overriden `shouldTriggerCompleter` method. - */ - override protected def shouldTriggerCompleter(symbol: Symbol, completer: Type, isFlagRelated: Boolean, mask: Long) = - completer match { - case _: TopClassCompleter | _: JavaClassCompleter => !isFlagRelated || (mask & TopLevelPickledFlags) != 0 - case _ => super.shouldTriggerCompleter(symbol, completer, isFlagRelated, mask) - } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 7b280e59b9..4a8585d616 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime // SI-6240: test thread-safety, make trees synchronized as well @@ -8,45 +9,50 @@ private[reflect] trait SynchronizedOps extends internal.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) } + override protected def synchronizeNames = true // BaseTypeSeqs override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = - new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + // only need to synchronize BaseTypeSeqs if they contain refined types + if (elems.filter(_.isInstanceOf[RefinedType]).nonEmpty) new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + else new BaseTypeSeq(parents, elems) 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 + override def apply(i: Int): Type = gilSynchronized { super.apply(i) } + override def rawElem(i: Int) = gilSynchronized { super.rawElem(i) } + override def typeSymbol(i: Int): Symbol = gilSynchronized { super.typeSymbol(i) } + override def toList: List[Type] = gilSynchronized { super.toList } + override def copy(head: Type, offset: Int): BaseTypeSeq = gilSynchronized { super.copy(head, offset) } + override def map(f: Type => Type): BaseTypeSeq = gilSynchronized { super.map(f) } + override def exists(p: Type => Boolean): Boolean = gilSynchronized { super.exists(p) } + override lazy val maxDepth = gilSynchronized { maxDepthOfElems } + override def toString = gilSynchronized { super.toString } + + override def lateMap(f: Type => Type): BaseTypeSeq = + // only need to synchronize BaseTypeSeqs if they contain refined types + if (map(f).toList.filter(_.isInstanceOf[RefinedType]).nonEmpty) new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + else new MappedBaseTypeSeq(this, f) } // Scopes - override def newScope = new Scope() with SynchronizedScope - override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope + override def newScope = new Scope 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 } + // we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible + // fancy subclasses of internal.Scopes#Scope should do synchronization themselves (e.g. see PackageScope for an example) + private lazy val syncLock = new Object + def syncLockSynchronized[T](body: => T): T = if (isCompilerUniverse) body else syncLock.synchronized { body } + override def isEmpty: Boolean = syncLockSynchronized { super.isEmpty } + override def size: Int = syncLockSynchronized { super.size } + override def enter[T <: Symbol](sym: T): T = syncLockSynchronized { super.enter(sym) } + override def rehash(sym: Symbol, newname: Name) = syncLockSynchronized { super.rehash(sym, newname) } + override def unlink(e: ScopeEntry) = syncLockSynchronized { super.unlink(e) } + override def unlink(sym: Symbol) = syncLockSynchronized { super.unlink(sym) } + override def lookupAll(name: Name) = syncLockSynchronized { super.lookupAll(name) } + override def lookupEntry(name: Name) = syncLockSynchronized { super.lookupEntry(name) } + override def lookupNextEntry(entry: ScopeEntry) = syncLockSynchronized { super.lookupNextEntry(entry) } + override def toList: List[Symbol] = syncLockSynchronized { super.toList } } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 00f6952dc1..f5e16c6640 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -1,18 +1,26 @@ -package scala.reflect +package scala +package reflect package runtime import scala.reflect.io.AbstractFile +import scala.collection.{ immutable, mutable } +import scala.reflect.internal.Flags._ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => - override protected def nextId() = synchronized { super.nextId() } + private lazy val atomicIds = new java.util.concurrent.atomic.AtomicInteger(0) + override protected def nextId() = atomicIds.incrementAndGet() - override protected def freshExistentialName(suffix: String) = - synchronized { super.freshExistentialName(suffix) } + private lazy val atomicExistentialIds = new java.util.concurrent.atomic.AtomicInteger(0) + override protected def nextExistentialId() = atomicExistentialIds.incrementAndGet() + + private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int]) + override def recursionTable = _recursionTable.get + override def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable.set(value) // 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) } + gilSynchronized { super.connectModuleToClass(m, moduleClass) } override def newFreeTermSymbol(name: TermName, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = new FreeTermSymbol(name, value, origin) with SynchronizedTermSymbol initFlags flags @@ -24,35 +32,127 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb 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 } - + /** (Things written in this comment only applies to runtime reflection. Compile-time reflection, + * especially across phases and runs, is somewhat more complicated, but we won't be touching it, + * because at the moment we only care about synchronizing runtime reflection). + * + * As it has been noted on multiple occasions, generally speaking, reflection artifacts aren't thread-safe. + * Reasons for that differ from artifact to artifact. In some cases it's quite bad (e.g. types use a number + * of non-concurrent compiler caches, so we need to serialize certain operations on types in order to make + * sure that things stay deterministic). However, in case of symbols there's hope, because it's only during + * initializaton that symbols are thread-unsafe. After everything's set up, symbols become immutable + * (sans a few deterministic caches that can be populated simultaneously by multiple threads) and therefore thread-safe. + * + * Note that by saying "symbols become immutable" I mean literally that. In a very common case of PackageClassSymbol's, + * even when a symbol finishes its initialization and becomes immutable, its info forever remains mutable. + * Therefore even if we no longer need to synchronize a PackageClassSymbol after it's initialized, we still have to take + * care of its ClassInfoType (or, more precisely, of the underlying Scope), but that's done elsewhere, and + * here we don't need to worry about that. + * + * Okay, so now we simply check `Symbol.isInitialized` and if it's true, then everything's fine? Haha, nope! + * The thing is that some completers call sym.setInfo when still in-flight and then proceed with initialization + * (e.g. see LazyPackageType). Consequently, setInfo sets _validTo to current period, which means that after + * a call to setInfo isInitialized will start returning true. Unfortunately, this doesn't mean that info becomes + * ready to be used, because subsequent initialization might change the info. + * + * Therefore we need to somehow distinguish between initialized and really initialized symbol states. + * Okay, let's do it on per-completer basis. We have seven kinds of completers to worry about: + * 1) LazyPackageType that initializes packages and their underlying package classes + * 2) TopClassCompleter that initializes top-level Scala-based class-module companion pairs of static definitions + * 3) LazyTypeRef and LazyTypeRefAndAlias set up by TopClassCompleter that initialize (transitive members) of top-level classes/modules + * 4) FromJavaClassCompleter that does the same for both top-level and non-toplevel Java-based classes/modules + * 5) Fully-initialized signatures of non-class/module Java-based reflection artifacts + * 6) Importing completer that transfers metadata from one universe to another + * 7) Signatures of special symbols such as roots and symbolsNotPresentInBytecode + * + * The mechanisms underlying completion are quite complex, and it'd be only natural to suppose that over time we're going to overlook something. + * Wrt isThreadsafe we could have two wrong situations: false positives (isThreadsafe = true, but the symbol isn't actually threadsafe) + * and false negatives (isThreadsafe = false, but the symbol is actually threadsafe). However, even though both are wrong, only the former + * is actively malicious. Indeed, false positives might lead to races, inconsistent state and crashes, while the latter would only cause + * `initialize` to be called and a gil to be taken on every potentially auto-initializable operation. Unpleasant yes, but still robust. + * + * What makes me hopeful is that: + * 1) By default (e.g. if some new completion mechanism gets introduced for a special flavor of symbols and we forget to call markCompleted) + * isThreadsafe is always in false negative state, which is unpleasant but safe. + * 2) Calls to `markCompleted` which are the only potential source of erroneous behavior are few and are relatively easy to place: + * just put them just before your completer's `complete` returns, and you should be fine. + * + * upd. Actually, there's another problem of not keeping initialization mask up-to-date. If we're not careful enough, + * then it might so happen that getting a certain flag that the compiler assumes to be definitively set will spuriously + * return isThreadsafe(purpose = FlagsOp(<flag>)) = false and that will lead to spurious auto-initialization, + * which will cause an SO or a cyclic reference or some other crash. I've done my best to go through all possible completers + * and call `markFlagsCompleted` where appropriate, but again over time something might be overlooked, so to guard against that + * I'm only considering TopLevelPickledFlags to be sources of potential initialization. This ensures that such system flags as + * isMethod, isModule or isPackage are never going to auto-initialize. + */ + override def isThreadsafe(purpose: SymbolOps) = { + if (isCompilerUniverse) false + else if (_initialized) true + else purpose.isFlagRelated && (_initializationMask & purpose.mask & TopLevelPickledFlags) == 0 + } + + /** Communicates with completers declared in scala.reflect.runtime.SymbolLoaders + * about the status of initialization of the underlying symbol. + * + * Unfortunately, it's not as easy as just introducing the `markThreadsafe` method that would be called + * by the completers when they are really done (as opposed to `setInfo` that, as mentioned above, doesn't mean anything). + * + * Since we also want to auto-initialize symbols when certain methods are being called (`Symbol.hasFlag` for example), + * we need to track the identity of the initializer, so as to block until initialization is complete if the caller + * comes from a different thread, but to skip auto-initialization if we're the initializing thread. + * + * Just a volatile var is fine, because: + * 1) Status can only be changed in a single-threaded fashion (this is enforced by gilSynchronized + * that effecively guards `Symbol.initialize`), which means that there can't be update conflicts. + * 2) If someone reads a stale value of status, then the worst thing that might happen is that this someone + * is going to spuriously call `initialize`, which is either a gil-protected operation (if the symbol isn't inited yet) + * or a no-op (if the symbol is already inited), and that is fine in both cases. + * + * upd. It looks like we also need to keep track of a mask of initialized flags to make sure + * that normal symbol initialization routines don't trigger auto-init in Symbol.flags-related routines (e.g. Symbol.getFlag). + * Due to the same reasoning as above, a single volatile var is enough for to store the mask. + */ + @volatile private[this] var _initialized = false + @volatile private[this] var _initializationMask = TopLevelPickledFlags + override def markFlagsCompleted(mask: Long): this.type = { _initializationMask = _initializationMask & ~mask; this } + override def markAllCompleted(): this.type = { _initializationMask = 0L; _initialized = true; this } + + def gilSynchronizedIfNotThreadsafe[T](body: => T): T = { + // TODO: debug and fix the race that doesn't allow us uncomment this optimization + // if (isCompilerUniverse || isThreadsafe(purpose = AllOps)) body + // else gilSynchronized { body } + gilSynchronized { body } + } + + override def validTo = gilSynchronizedIfNotThreadsafe { super.validTo } + override def info = gilSynchronizedIfNotThreadsafe { super.info } + override def rawInfo: Type = gilSynchronizedIfNotThreadsafe { super.rawInfo } + override def typeSignature: Type = gilSynchronizedIfNotThreadsafe { super.typeSignature } + override def typeSignatureIn(site: Type): Type = gilSynchronizedIfNotThreadsafe { super.typeSignatureIn(site) } + + override def typeParams: List[Symbol] = gilSynchronizedIfNotThreadsafe { + if (isCompilerUniverse) super.typeParams + else { + 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) + rawInfo load this + if (validTo == NoPeriod) + rawInfo load this + + rawInfo.typeParams + } + } + } + override def unsafeTypeParams: List[Symbol] = gilSynchronizedIfNotThreadsafe { + if (isCompilerUniverse) super.unsafeTypeParams + else { + if (isMonomorphicType) Nil + else rawInfo.typeParams + } + } // ------ creators ------------------------------------------------------------------- @@ -83,58 +183,44 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb 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) + override protected def createPackageSymbol(name: TermName, pos: Position, newFlags: Long): ModuleSymbol = + createModuleSymbol(name, pos, newFlags) + + override protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long) = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags - // TODO - // override protected def createValueParameterSymbol(name: TermName, pos: Position, newFlags: Long) - // override protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long) + override protected def createValueMemberSymbol(name: TermName, pos: Position, newFlags: Long) = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags } // ------- 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 SynchronizedTermSymbol extends SynchronizedSymbol trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { - override def typeAsMemberOf(pre: Type): Type = synchronized { super.typeAsMemberOf(pre) } - override def paramss: List[List[Symbol]] = synchronized { super.paramss } - override def returnType: Type = synchronized { super.returnType } + // we can keep this lock fine-grained, because it's just a cache over asSeenFrom, which makes deadlocks impossible + // unfortunately we cannot elide this lock, because the cache depends on `pre` + private lazy val typeAsMemberOfLock = new Object + override def typeAsMemberOf(pre: Type): Type = gilSynchronizedIfNotThreadsafe { typeAsMemberOfLock.synchronized { super.typeAsMemberOf(pre) } } } + trait SynchronizedModuleSymbol extends ModuleSymbol with SynchronizedTermSymbol + 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 } + // unlike with typeConstructor, a lock is necessary here, because tpe calculation relies on + // temporarily assigning NoType to tpeCache to detect cyclic reference errors + private lazy val tpeLock = new Object + override def tpe_* : Type = gilSynchronizedIfNotThreadsafe { tpeLock.synchronized { super.tpe_* } } } - trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { - override def associatedFile = synchronized { super.associatedFile } - override def associatedFile_=(f: AbstractFile) = 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 SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol - trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { - override def sourceModule = synchronized { super.sourceModule } - override def implicitMembers: Scope = synchronized { super.implicitMembers } - } + trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index a3e7c28ca4..9bcf85dd6f 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -1,8 +1,11 @@ -package scala.reflect +package scala +package reflect package runtime -import scala.collection.mutable.WeakHashMap -import java.lang.ref.WeakReference +import scala.collection.mutable +import java.lang.ref.{WeakReference => jWeakRef} +import scala.ref.{WeakReference => sWeakRef} +import scala.reflect.internal.Depth /** This trait overrides methods in reflect.internal, bracketing * them in synchronized { ... } to make them thread-safe @@ -12,9 +15,10 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa // No sharing of map objects: override protected def commonOwnerMap = new CommonOwnerMap - private object uniqueLock - - private val uniques = WeakHashMap[Type, WeakReference[Type]]() + // we can keep this lock fine-grained, because super.unique just updates the cache + // and, in particular, doesn't call any reflection APIs which makes deadlocks impossible + private lazy val uniqueLock = new Object + private val uniques = mutable.WeakHashMap[Type, jWeakRef[Type]]() override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { // we need to have weak uniques for runtime reflection // because unlike the normal compiler universe, reflective universe isn't organized in runs @@ -28,7 +32,7 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa val result = if (inCache.isDefined) inCache.get.get else null if (result ne null) result.asInstanceOf[T] else { - uniques(tp) = new WeakReference(tp) + uniques(tp) = new jWeakRef(tp) tp } } else { @@ -36,47 +40,46 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa } } - class SynchronizedUndoLog extends UndoLog { - private val actualLock = new java.util.concurrent.locks.ReentrantLock - - final override def lock(): Unit = actualLock.lock() - final override def unlock(): Unit = actualLock.unlock() - } - - override protected def newUndoLog = new SynchronizedUndoLog - - override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = - synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } + private lazy val _skolemizationLevel = mkThreadLocalStorage(0) + override def skolemizationLevel = _skolemizationLevel.get + override def skolemizationLevel_=(value: Int) = _skolemizationLevel.set(value) - private object subsametypeLock + private lazy val _undoLog = mkThreadLocalStorage(new UndoLog) + override def undoLog = _undoLog.get - override def isSameType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isSameType(tp1, tp2) } + private lazy val _intersectionWitness = mkThreadLocalStorage(perRunCaches.newWeakMap[List[Type], sWeakRef[Type]]()) + override def intersectionWitness = _intersectionWitness.get - override def isDifferentType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } + private lazy val _subsametypeRecursions = mkThreadLocalStorage(0) + override def subsametypeRecursions = _subsametypeRecursions.get + override def subsametypeRecursions_=(value: Int) = _subsametypeRecursions.set(value) - override def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = - subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } + private lazy val _pendingSubTypes = mkThreadLocalStorage(new mutable.HashSet[SubTypePair]) + override def pendingSubTypes = _pendingSubTypes.get - private object lubglbLock + private lazy val _basetypeRecursions = mkThreadLocalStorage(0) + override def basetypeRecursions = _basetypeRecursions.get + override def basetypeRecursions_=(value: Int) = _basetypeRecursions.set(value) - override def glb(ts: List[Type]): Type = - lubglbLock.synchronized { super.glb(ts) } + private lazy val _pendingBaseTypes = mkThreadLocalStorage(new mutable.HashSet[Type]) + override def pendingBaseTypes = _pendingBaseTypes.get - override def lub(ts: List[Type]): Type = - lubglbLock.synchronized { super.lub(ts) } + private lazy val _lubResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def lubResults = _lubResults.get - private object indentLock + private lazy val _glbResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def glbResults = _glbResults.get - 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 lazy val _indent = mkThreadLocalStorage("") + override def indent = _indent.get + override def indent_=(value: String) = _indent.set(value) - private object toStringLock + private lazy val _toStringRecursions = mkThreadLocalStorage(0) + override def toStringRecursions = _toStringRecursions.get + override def toStringRecursions_=(value: Int) = _toStringRecursions.set(value) - override protected def typeToString(tpe: Type): String = - toStringLock.synchronized(super.typeToString(tpe)) + private lazy val _toStringSubjects = mkThreadLocalStorage(new mutable.HashSet[Type]) + override def toStringSubjects = _toStringSubjects.get /* The idea of caches is as follows. * When in reflexive mode, a cache is either null, or one sentinal @@ -89,18 +92,18 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa */ override protected def defineUnderlyingOfSingleType(tpe: SingleType) = - tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } + gilSynchronized { super.defineUnderlyingOfSingleType(tpe) } override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } + gilSynchronized { super.defineBaseClassesOfCompoundType(tpe) } override protected def defineParentsOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineParentsOfTypeRef(tpe) } + gilSynchronized { super.defineParentsOfTypeRef(tpe) } override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } } diff --git a/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala new file mode 100644 index 0000000000..5edc051461 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala @@ -0,0 +1,28 @@ +package scala.reflect +package runtime + +import java.lang.Thread._ + +private[reflect] trait ThreadLocalStorage { + self: SymbolTable => + + // see a discussion at scala-internals for more information: + // http://groups.google.com/group/scala-internals/browse_thread/thread/337ce68aa5e51f79 + trait ThreadLocalStorage[T] { def get: T; def set(newValue: T): Unit } + private class MyThreadLocalStorage[T](initialValue: => T) extends ThreadLocalStorage[T] { + // TODO: how do we use org.cliffc.high_scale_lib.NonBlockingHashMap here? + val values = new java.util.concurrent.ConcurrentHashMap[Thread, T]() + def get: T = { + if (values containsKey currentThread) values.get(currentThread) + else { + val value = initialValue + values.putIfAbsent(currentThread, value) + value + } + } + def set(newValue: T): Unit = { + values.put(currentThread, newValue) + } + } + @inline final def mkThreadLocalStorage[T](x: => T): ThreadLocalStorage[T] = new MyThreadLocalStorage(x) +} diff --git a/src/reflect/scala/reflect/runtime/TwoWayCache.scala b/src/reflect/scala/reflect/runtime/TwoWayCache.scala index 05debcba65..d0fc3dac74 100644 --- a/src/reflect/scala/reflect/runtime/TwoWayCache.scala +++ b/src/reflect/scala/reflect/runtime/TwoWayCache.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.collection.mutable.WeakHashMap @@ -51,16 +52,4 @@ private[runtime] class TwoWayCache[J, S] { result } } - - def toJavaOption(key: S)(body: => Option[J]): Option[J] = synchronized { - toJavaMap get key match { - case SomeRef(v) => - Some(v) - case _ => - val result = body - for (value <- result) enter(value, key) - result - } - } } - diff --git a/src/reflect/scala/reflect/runtime/TwoWayCaches.scala b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala new file mode 100644 index 0000000000..6e2890e536 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala @@ -0,0 +1,68 @@ +package scala.reflect +package runtime + +import scala.collection.mutable.WeakHashMap +import java.lang.ref.WeakReference + +/** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + * + * The cache is two-way weak (i.e. is powered by weak references), + * so that neither Java artifacts prevent Scala artifacts from being garbage collected, + * nor the other way around. + */ +private[runtime] trait TwoWayCaches { self: SymbolTable => + class TwoWayCache[J, S] { + + private val toScalaMap = new WeakHashMap[J, WeakReference[S]] + private val toJavaMap = new WeakHashMap[S, WeakReference[J]] + + def enter(j: J, s: S) = gilSynchronized { + // debugInfo("cached: "+j+"/"+s) + toScalaMap(j) = new WeakReference(s) + toJavaMap(s) = new WeakReference(j) + } + + private object SomeRef { + def unapply[T](optRef: Option[WeakReference[T]]): Option[T] = + if (optRef.nonEmpty) { + val result = optRef.get.get + if (result != null) Some(result) else None + } else None + } + + def toScala(key: J)(body: => S): S = gilSynchronized { + toScalaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(key, result) + result + } + } + + def toJava(key: S)(body: => J): J = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(result, key) + result + } + } + + def toJavaOption(key: S)(body: => Option[J]): Option[J] = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + Some(v) + case _ => + val result = body + for (value <- result) enter(value, key) + result + } + } + } +} + diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala index b97913daf0..e240bed0a7 100644 --- a/src/reflect/scala/reflect/runtime/package.scala +++ b/src/reflect/scala/reflect/runtime/package.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect /** Entry points into runtime reflection. * See [[scala.reflect.api.package the overview page]] for details on how to use them. @@ -6,7 +7,7 @@ package scala.reflect package object runtime { /** The entry point into Scala runtime reflection. - * + * * To use Scala runtime reflection, simply use or import `scala.reflect.runtime.universe._` * * See [[scala.reflect.api.Universe]] or the @@ -20,16 +21,17 @@ package object runtime { */ // implementation hardwired to the `currentMirror` method below // using the mechanism implemented in `scala.tools.reflect.FastTrack` - def currentMirror: universe.Mirror = ??? // macro + def currentMirror: universe.Mirror = macro ??? } package runtime { private[scala] object Macros { - def currentMirror(c: scala.reflect.macros.Context): c.Expr[universe.Mirror] = { + def currentMirror(c: scala.reflect.macros.blackbox.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 scalaPackage = Select(Ident(newTermName("_root_")), newTermName("scala")) + val runtimeUniverse = Select(Select(Select(scalaPackage, newTermName("reflect")), newTermName("runtime")), newTermName("universe")) val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader")))) c.Expr[Nothing](currentMirror)(c.WeakTypeTag.Nothing) } |