diff options
Diffstat (limited to 'src/reflect')
143 files changed, 8358 insertions, 6750 deletions
diff --git a/src/reflect/scala/reflect/api/Annotations.scala b/src/reflect/scala/reflect/api/Annotations.scala index 09eaf7afb4..e19e0cefad 100644 --- a/src/reflect/scala/reflect/api/Annotations.scala +++ b/src/reflect/scala/reflect/api/Annotations.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api import scala.collection.immutable.ListMap @@ -45,12 +46,6 @@ 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 - */ - implicit val AnnotationTag: ClassTag[Annotation] - /** The constructor/extractor for `Annotation` instances. * @group Extractors */ @@ -90,11 +85,6 @@ trait Annotations { self: Universe => */ type JavaArgument >: Null <: AnyRef - /** 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 - */ - implicit val JavaArgumentTag: ClassTag[JavaArgument] /** A literal argument to a Java annotation as `"Use X instead"` in `@Deprecated("Use X instead")` * @template @@ -102,12 +92,6 @@ trait Annotations { self: Universe => */ 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] - /** The constructor/extractor for `LiteralArgument` instances. * @group Extractors */ @@ -137,12 +121,6 @@ trait Annotations { self: Universe => */ 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] - /** The constructor/extractor for `ArrayArgument` instances. * @group Extractors */ @@ -172,12 +150,6 @@ trait Annotations { self: Universe => */ 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] - /** The constructor/extractor for `NestedArgument` instances. * @group Extractors */ @@ -200,4 +172,4 @@ trait Annotations { self: Universe => /** The underlying nested annotation. */ 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 index 0c8e81a220..2e95a01176 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** @@ -59,8 +60,6 @@ private[reflect] trait BuildUtils { self: Universe => def flagsFromBits(bits: Long): FlagSet - def emptyValDef: ValDef - def This(sym: Symbol): Tree def Select(qualifier: Tree, sym: Symbol): Select diff --git a/src/reflect/scala/reflect/api/Constants.scala b/src/reflect/scala/reflect/api/Constants.scala index f3d75c3c00..c654961f4a 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 /** @@ -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..009d9dbfdb 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package api import scala.reflect.runtime.{universe => ru} @@ -174,4 +175,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..8a1d2f7f1d 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 diff --git a/src/reflect/scala/reflect/api/ImplicitTags.scala b/src/reflect/scala/reflect/api/ImplicitTags.scala new file mode 100644 index 0000000000..1b654a4a8d --- /dev/null +++ b/src/reflect/scala/reflect/api/ImplicitTags.scala @@ -0,0 +1,117 @@ +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] + implicit val FreeTermSymbolTag: ClassTag[FreeTermSymbol] + implicit val FreeTypeSymbolTag: ClassTag[FreeTypeSymbol] + + // 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 ReferenceToBoxedTag: ClassTag[ReferenceToBoxed] + 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] +} diff --git a/src/reflect/scala/reflect/api/Importers.scala b/src/reflect/scala/reflect/api/Importers.scala index afc4f2f25d..e6f314b712 100644 --- a/src/reflect/scala/reflect/api/Importers.scala +++ b/src/reflect/scala/reflect/api/Importers.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** diff --git a/src/reflect/scala/reflect/api/JavaMirrors.scala b/src/reflect/scala/reflect/api/JavaMirrors.scala index b678033e1a..23abc23eb9 100644 --- a/src/reflect/scala/reflect/api/JavaMirrors.scala +++ b/src/reflect/scala/reflect/api/JavaMirrors.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** diff --git a/src/reflect/scala/reflect/api/JavaUniverse.scala b/src/reflect/scala/reflect/api/JavaUniverse.scala index 04d091ee9d..6fc76c9693 100644 --- a/src/reflect/scala/reflect/api/JavaUniverse.scala +++ b/src/reflect/scala/reflect/api/JavaUniverse.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** diff --git a/src/reflect/scala/reflect/api/Mirror.scala b/src/reflect/scala/reflect/api/Mirror.scala index 1223326d7c..e0219c9074 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 + * res3: scala.reflect.runtime.universe.Symbol = type List * * scala> res3.fullName * res4: String = scala.List diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index 76a75940ff..d702555ba6 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 /** @@ -350,6 +351,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 +377,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. diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala index 7c12f180a8..f74e0ce014 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> * @@ -43,42 +46,24 @@ trait 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] - /** The abstract type of names representing terms. * @group Names */ type TypeName >: Null <: 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 - */ -implicit val TypeNameTag: ClassTag[TypeName] - /** The abstract type of names representing types. * @group Names */ type TermName >: Null <: 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 - */ - implicit val TermNameTag: ClassTag[TermName] - /** 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` */ @@ -109,10 +94,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..4d5f1f6e67 100644 --- a/src/reflect/scala/reflect/api/Position.scala +++ b/src/reflect/scala/reflect/api/Position.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api import scala.reflect.macros.Attachments diff --git a/src/reflect/scala/reflect/api/Positions.scala b/src/reflect/scala/reflect/api/Positions.scala index 87f00fdb88..8ad46418f8 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 /** @@ -19,13 +20,6 @@ trait Positions { * @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] - /** 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..81d30dec1e 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 } @@ -143,6 +144,7 @@ trait Printers { self: Universe => protected var printIds = 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 } @@ -151,43 +153,48 @@ trait Printers { self: Universe => 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, 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) 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, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newTreePrinter(_), printTypes, printIds, printKinds, printMirrors, printPositions) /** Hook to define what `show(...)` means. * @group Printers @@ -195,12 +202,12 @@ trait Printers { self: Universe => protected def newTreePrinter(out: PrintWriter): TreePrinter /** Renders internal structure of a reflection artifact as the - * visualization of a Scala syntax tree. + * visualization of a Scala syntax tree. * - * @group Printers + * @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) + def showRaw(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newRawTreePrinter(_), printTypes, printIds, printKinds, printMirrors, printPositions) /** Hook to define what `showRaw(...)` means. * @group Printers diff --git a/src/reflect/scala/reflect/api/Scopes.scala b/src/reflect/scala/reflect/api/Scopes.scala index 7f9799393c..2eb477f652 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 /** @@ -33,12 +34,6 @@ trait Scopes { self: Universe => */ 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 */ @@ -61,10 +56,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..bbfebcb434 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 /** diff --git a/src/reflect/scala/reflect/api/StandardNames.scala b/src/reflect/scala/reflect/api/StandardNames.scala index 4886e4f8f7..aec5f19fa0 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? @@ -84,6 +85,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..1250545497 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 /** @@ -61,12 +62,6 @@ trait Symbols { self: Universe => */ 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] - /** The type of type symbols representing type, class, and trait declarations, * as well as type parameters. * @group Symbols @@ -74,12 +69,6 @@ trait Symbols { self: Universe => */ 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] - /** The type of term symbols representing val, var, def, and object declarations as * well as packages and value parameters. * @group Symbols @@ -87,72 +76,36 @@ trait Symbols { self: Universe => */ 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] - /** 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] - /** 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] - /** 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] - /** A special "missing" symbol. Commonly used in the API to denote a default or empty value. * @group Symbols * @template @@ -245,7 +198,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 */ diff --git a/src/reflect/scala/reflect/api/TagInterop.scala b/src/reflect/scala/reflect/api/TagInterop.scala index 5de811578e..51b7c519c5 100644 --- a/src/reflect/scala/reflect/api/TagInterop.scala +++ b/src/reflect/scala/reflect/api/TagInterop.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package api /** 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..f7a6a68946 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 /** @@ -60,12 +61,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all trees support. * The main source of information about trees is the [[scala.reflect.api.Trees]] page. * @group API @@ -75,11 +70,32 @@ 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. + * + * In the compiler the `isEmpty` check and the derived `orElse` method are mostly used + * as a check for a tree being a null object (`EmptyTree` for term trees and empty TypeTree for type trees). + * + * Unfortunately `emptyValDef` is also considered to be `isEmpty`, but this is deemed to be + * a conceptual mistake pending a fix in https://issues.scala-lang.org/browse/SI-6762. + * + * @see `canHaveAttrs` */ def isEmpty: Boolean + /** Is this tree one of the empty trees? + * + * @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: `emptyValDef` and `pendingSuperCall`. + */ + def canHaveAttrs: Boolean + /** The canonical way to test if a Tree represents a term. */ def isTerm: Boolean @@ -209,12 +225,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all term trees support * @group API */ @@ -228,12 +238,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all typ trees support * @group API */ @@ -246,12 +250,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all sym trees support * @group API */ @@ -266,12 +264,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all name trees support * @group API */ @@ -290,12 +282,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all ref trees support * @group API */ @@ -310,18 +296,26 @@ trait Trees { self: Universe => def name: Name } + /** 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 + */ + abstract class RefTreeExtractor { + def apply(qualifier: Tree, name: Name): RefTree + def unapply(refTree: RefTree): Option[(Tree, Name)] + } + /** A tree which defines a symbol-carrying entity. * @group Trees * @template */ type DefTree >: Null <: SymTree with NameTree with DefTreeApi - /** 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 - */ - implicit val DefTreeTag: ClassTag[DefTree] - /** The API that all def trees support * @group API */ @@ -337,12 +331,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all member defs support * @group API */ @@ -357,12 +345,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `PackageDef` instances. * @group Extractors */ @@ -396,12 +378,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all impl defs support * @group API */ @@ -416,12 +392,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `ClassDef` instances. * @group Extractors */ @@ -467,12 +437,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `ModuleDef` instances. * @group Extractors */ @@ -513,12 +477,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all val defs and def defs support * @group API */ @@ -550,12 +508,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `ValDef` instances. * @group Extractors */ @@ -605,12 +557,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `DefDef` instances. * @group Extractors */ @@ -660,12 +606,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `TypeDef` instances. * @group Extractors */ @@ -725,12 +665,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `LabelDef` instances. * @group Extractors */ @@ -787,12 +721,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 */ @@ -839,12 +767,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Import` instances. * @group Extractors */ @@ -897,12 +819,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Template` instances. * @group Extractors */ @@ -955,12 +871,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Block` instances. * @group Extractors */ @@ -1000,12 +910,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `CaseDef` instances. * @group Extractors */ @@ -1017,7 +921,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 +942,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 } @@ -1053,12 +957,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Alternative` instances. * @group Extractors */ @@ -1091,12 +989,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Star` instances. * @group Extractors */ @@ -1132,12 +1024,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Bind` instances. * @group Extractors */ @@ -1201,12 +1087,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `UnApply` instances. * @group Extractors */ @@ -1243,12 +1123,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Function` instances. * @group Extractors */ @@ -1287,12 +1161,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Assign` instances. * @group Extractors */ @@ -1329,12 +1197,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `AssignOrNamedArg` instances. * @group Extractors */ @@ -1376,12 +1238,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `If` instances. * @group Extractors */ @@ -1433,12 +1289,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Match` instances. * @group Extractors */ @@ -1474,12 +1324,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Return` instances. * @group Extractors */ @@ -1512,12 +1356,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Try` instances. * @group Extractors */ @@ -1556,12 +1394,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Throw` instances. * @group Extractors */ @@ -1592,12 +1424,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `New` instances. * @group Extractors */ @@ -1648,12 +1474,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Typed` instances. * @group Extractors */ @@ -1687,12 +1507,6 @@ trait Trees { self: Universe => */ 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] - /** The API that all applies support * @group API */ @@ -1714,12 +1528,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `TypeApply` instances. * @group Extractors */ @@ -1758,12 +1566,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Apply` instances. * @group Extractors */ @@ -1801,12 +1603,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Super` instances. * @group Extractors */ @@ -1853,12 +1649,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `This` instances. * @group Extractors */ @@ -1894,12 +1684,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Select` instances. * @group Extractors */ @@ -1939,12 +1723,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Ident` instances. * @group Extractors */ @@ -1984,12 +1762,6 @@ trait Trees { self: Universe => */ 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 */ @@ -2034,12 +1806,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Literal` instances. * @group Extractors */ @@ -2073,12 +1839,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `Annotated` instances. * @group Extractors */ @@ -2113,12 +1873,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `SingletonTypeTree` instances. * @group Extractors */ @@ -2149,12 +1903,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `SelectFromTypeTree` instances. * @group Extractors */ @@ -2196,12 +1944,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `CompoundTypeTree` instances. * @group Extractors */ @@ -2232,12 +1974,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `AppliedTypeTree` instances. * @group Extractors */ @@ -2280,12 +2016,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `TypeBoundsTree` instances. * @group Extractors */ @@ -2323,12 +2053,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `ExistentialTypeTree` instances. * @group Extractors */ @@ -2366,12 +2090,6 @@ trait Trees { self: Universe => */ 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] - /** The constructor/extractor for `TypeTree` instances. * @group Extractors */ @@ -2405,6 +2123,15 @@ trait Trees { self: Universe => */ val emptyValDef: ValDef + /** An empty superclass constructor call corresponding to: + * super.<init>() + * This is used as a placeholder in the primary constructor body in class templates + * to denote the insertion point of a call to superclass constructor after the typechecker + * figures out the superclass of a given template. + * @group Trees + */ + val pendingSuperCall: Apply + // ---------------------- factories ---------------------------------------------- /** A factory method for `ClassDef` nodes. @@ -2897,7 +2624,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 +2635,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 emptyValDef) tree + else transform(tree).asInstanceOf[ValDef] /** Transforms a list of `ValDef` nodes. */ def transformValDefs(trees: List[ValDef]): List[ValDef] = trees mapConserve (transformValDef(_)) @@ -2926,8 +2655,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 = { @@ -2956,12 +2687,6 @@ trait Trees { self: Universe => */ 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 +2718,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/Types.scala b/src/reflect/scala/reflect/api/Types.scala index 72163ef0e9..9d2d06da69 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,7 +51,8 @@ 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). @@ -59,12 +61,6 @@ trait Types { self: Universe => */ 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] - /** This constant is used as a special value that indicates that no meaningful type exists. * @group Types */ @@ -214,6 +210,12 @@ trait Types { self: Universe => /******************* 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. */ @@ -256,12 +258,6 @@ trait Types { self: Universe => */ type SingletonType >: Null <: 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 - */ - implicit val SingletonTypeTag: ClassTag[SingletonType] - /** A singleton type that describes types of the form on the left with the * corresponding `ThisType` representation to the right: * {{{ @@ -272,12 +268,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `ThisType` instances. * @group Extractors */ @@ -316,12 +306,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `SingleType` instances. * @group Extractors */ @@ -361,12 +345,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `SuperType` instances. * @group Extractors */ @@ -406,12 +384,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `ConstantType` instances. * @group Extractors */ @@ -450,12 +422,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `TypeRef` instances. * @group Extractors */ @@ -497,12 +463,6 @@ trait Types { self: Universe => */ type CompoundType >: Null <: AnyRef 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 - */ - implicit val CompoundTypeTag: ClassTag[CompoundType] - /** The `RefinedType` type defines types of any of the forms on the left, * with their RefinedType representations to the right. * {{{ @@ -515,12 +475,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `RefinedType` instances. * @group Extractors */ @@ -567,12 +521,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `ClassInfoType` instances. * @group Extractors */ @@ -610,12 +558,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `MethodType` instances. * @group Extractors */ @@ -660,12 +602,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `NullaryMethodType` instances. * @group Extractors */ @@ -696,12 +632,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `PolyType` instances. * @group Extractors */ @@ -736,12 +666,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `ExistentialType` instances. * @group Extractors */ @@ -777,12 +701,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `AnnotatedType` instances. * @group Extractors */ @@ -828,12 +746,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `TypeBounds` instances. * @group Extractors */ @@ -885,12 +797,6 @@ trait Types { self: Universe => */ 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] - /** The constructor/extractor for `BoundedWildcardType` instances. * @group Extractors */ diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 4928b8bb38..cb629f9c5a 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 /** @@ -68,6 +69,7 @@ abstract class Universe extends Symbols with Exprs with TypeTags with TagInterop + with ImplicitTags with StandardDefinitions with StandardNames with BuildUtils @@ -92,5 +94,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 -}
\ No newline at end of file + 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 032b45316e..73ca74f08e 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -3,17 +3,17 @@ * @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 /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.Annotations { self: SymbolTable => - import definitions.{ ThrowsClass, StaticAnnotationClass, isMetaAnnotation } + import definitions.{ ThrowsClass, ThrowableClass, StaticAnnotationClass, isMetaAnnotation } // Common annotation code between Symbol and Type. // For methods altering the annotation list, on Symbol it mutates @@ -40,8 +40,7 @@ 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 */ @@ -122,25 +121,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 @@ -304,10 +310,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 { @@ -342,13 +344,15 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => 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 +360,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..e3498a95a6 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 @@ -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)) @@ -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 @@ -180,7 +180,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 +193,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 } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 9f41f0336e..ece2d28be3 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -1,8 +1,7 @@ -package scala.reflect +package scala +package reflect package internal -import Flags._ - trait BuildUtils { self: SymbolTable => class BuildImpl extends BuildApi { @@ -47,8 +46,6 @@ trait BuildUtils { self: SymbolTable => def flagsFromBits(bits: Long): FlagSet = bits - def emptyValDef: ValDef = self.emptyValDef - def This(sym: Symbol): Tree = self.This(sym) def Select(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym) 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 c198271fb1..a7ce044780 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 @@ -345,7 +345,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 @@ -353,7 +353,7 @@ object ClassfileConstants { } private def translateFlags(jflags: Int, baseFlags: Long): Long = { var res: Long = JAVA | baseFlags - /** fast, elegant, maintainable, pick any two... */ + /* fast, elegant, maintainable, pick any two... */ res |= translateFlag(jflags & JAVA_ACC_PRIVATE) res |= translateFlag(jflags & JAVA_ACC_PROTECTED) res |= translateFlag(jflags & JAVA_ACC_FINAL) @@ -375,7 +375,7 @@ object ClassfileConstants { } def methodFlags(jflags: Int): Long = { initFields(jflags) - translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE else 0) + translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) BRIDGE | ARTIFACT else 0) } } object FlagTranslation extends FlagTranslation { } @@ -383,11 +383,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..511b39b8c6 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 = { diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 629d82d254..4f2b7e2642 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.{getModule, getPackage, getClassByName, getRequiredClass, getRequiredModule, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageObjectIfDefined, requiredClass, requiredModule} object definitions extends DefinitionsClass @@ -31,7 +32,7 @@ trait Definitions extends api.StandardDefinitions { val clazz = owner.newClassSymbol(name, NoPosition, flags) clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz) } - private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol = { + 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) @@ -128,15 +129,15 @@ trait Definitions extends api.StandardDefinitions { 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 + 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,7 +152,6 @@ trait Definitions extends api.StandardDefinitions { FloatClass, DoubleClass ) - def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses } @@ -159,9 +159,6 @@ trait Definitions extends api.StandardDefinitions { 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 @@ -176,25 +173,13 @@ trait Definitions extends api.StandardDefinitions { // 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,47 +209,77 @@ 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) = ( + (sym ne NoSymbol) + && (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)) 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 AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe) 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 + + // 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 // Note: this is not the type alias AnyRef, it's a companion-like // object used by the @specialize annotation. @@ -273,12 +288,11 @@ trait Definitions extends api.StandardDefinitions { def Predef_AnyRef = AnyRefModule lazy val AnyValClass: ClassSymbol = (ScalaPackageClass.info member tpnme.AnyVal orElse { - val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, List(AnyClass.tpe, NotNullClass.tpe), ABSTRACT) + val anyval = enterNewClass(ScalaPackageClass, tpnme.AnyVal, AnyTpe :: Nil, ABSTRACT) val av_constr = anyval.newClassConstructor(NoPosition) anyval.info.decls enter av_constr anyval }).asInstanceOf[ClassSymbol] - lazy val AnyValTpe = definitions.AnyValClass.toTypeConstructor def AnyVal_getClass = getMemberMethod(AnyValClass, nme.getClass_) // bottom types @@ -301,8 +315,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 +326,11 @@ trait Definitions extends api.StandardDefinitions { lazy val ThrowableClass = getClassByName(sn.Throwable) lazy val UninitializedErrorClass = requiredClass[UninitializedFieldError] + @deprecated("Same effect but more compact: `throw null`. Details in JVM spec, `athrow` instruction.", "2.11.0") + lazy val NPEConstructor = getMemberMethod(NullPointerExceptionClass, nme.CONSTRUCTOR) suchThat (_.paramss.flatten.isEmpty) + + lazy val UninitializedFieldConstructor = UninitializedErrorClass.primaryConstructor + // fundamental reference classes lazy val PartialFunctionClass = requiredClass[PartialFunction[_,_]] lazy val AbstractPartialFunctionClass = requiredClass[scala.runtime.AbstractPartialFunction[_,_]] @@ -337,15 +354,11 @@ trait Definitions extends api.StandardDefinitions { 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) + 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` @@ -361,7 +374,6 @@ trait Definitions extends api.StandardDefinitions { 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) @@ -371,27 +383,19 @@ trait Definitions extends api.StandardDefinitions { def arrayLengthMethod = getMemberMethod(ScalaRunTimeModule, nme.array_length) def arrayCloneMethod = getMemberMethod(ScalaRunTimeModule, nme.array_clone) def ensureAccessibleMethod = getMemberMethod(ScalaRunTimeModule, nme.ensureAccessible) - def scalaRuntimeSameElements = getMemberMethod(ScalaRunTimeModule, nme.sameElements) def arrayClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayClass) - def arrayElementClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayElementClass) // classes with special meanings lazy val StringAddClass = requiredClass[scala.runtime.StringAdd] lazy val ArrowAssocClass = getRequiredClass("scala.Predef.ArrowAssoc") // SI-5731 lazy val StringAdd_+ = getMemberMethod(StringAddClass, nme.PLUS) - lazy val NotNullClass = getRequiredClass("scala.NotNull") lazy val ScalaNumberClass = requiredClass[scala.math.ScalaNumber] lazy val TraitSetterAnnotationClass = requiredClass[scala.runtime.TraitSetter] lazy val DelayedInitClass = requiredClass[scala.DelayedInit] def delayedInitMethod = getMemberMethod(DelayedInitClass, nme.delayedInit) - // a dummy value that communicates that a delayedInit call is compiler-generated - // from phase UnCurry to phase Constructors - // !!! This is not used anywhere (it was checked in that way.) - // def delayedInitArgVal = EmptyPackageClass.newValue(NoPosition, nme.delayedInitArg) - // .setInfo(UnitClass.tpe) lazy val TypeConstraintClass = requiredClass[scala.annotation.TypeConstraint] - lazy val SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, anyparam, ABSTRACT | TRAIT | FINAL) + lazy val SingletonClass = enterNewClass(ScalaPackageClass, tpnme.Singleton, AnyTpe :: Nil, ABSTRACT | TRAIT | FINAL) lazy val SerializableClass = requiredClass[scala.Serializable] lazy val JavaSerializableClass = requiredClass[java.io.Serializable] modifyInfo fixupAsAnyTrait lazy val ComparableClass = requiredClass[java.lang.Comparable[_]] modifyInfo fixupAsAnyTrait @@ -401,8 +405,7 @@ trait Definitions extends api.StandardDefinitions { 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 +413,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) @@ -425,34 +429,18 @@ trait Definitions extends api.StandardDefinitions { 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 repeatedToSingle(tp: Type): Type = elementExtract(RepeatedParamClass, tp) orElse tp + 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) + object ExprClassOf { def unapply(tp: Type): Option[Type] = elementExtractOption(ExprClass, 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 ListClass = requiredClass[scala.collection.immutable.List[_]] lazy val SeqClass = requiredClass[scala.collection.Seq[_]] @@ -463,8 +451,6 @@ trait Definitions extends api.StandardDefinitions { lazy val List_apply = getMemberMethod(ListModule, nme.apply) lazy val NilModule = requiredModule[scala.collection.immutable.Nil.type] lazy val SeqModule = requiredModule[scala.collection.Seq.type] - lazy val IteratorModule = requiredModule[scala.collection.Iterator.type] - lazy val Iterator_apply = getMemberMethod(IteratorModule, nme.apply) // arrays and their members lazy val ArrayModule = requiredModule[scala.Array.type] @@ -479,9 +465,7 @@ 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_) @@ -491,8 +475,8 @@ trait Definitions extends api.StandardDefinitions { 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 PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest) lazy val PartialManifestModule = requiredModule[scala.reflect.ClassManifestFactory.type] @@ -501,25 +485,31 @@ trait Definitions extends api.StandardDefinitions { 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 TreesTreeType = TreesClass.map(sym => getTypeMember(sym, tpnme.Tree)) + object TreeType { + def unapply(tpe: Type): Boolean = unapply(tpe.typeSymbol) + def unapply(sym: Symbol): Boolean = sym.overrideChain contains TreesTreeType + } + lazy val ExprsClass = getClassIfDefined("scala.reflect.api.Exprs") // defined in scala-reflect.jar, so we need to be careful - lazy val ExprClass = if (ExprsClass != NoSymbol) getMemberClass(ExprsClass, tpnme.Expr) else NoSymbol - def ExprSplice = if (ExprsClass != NoSymbol) getMemberMethod(ExprClass, nme.splice) else NoSymbol - def ExprValue = if (ExprsClass != NoSymbol) getMemberMethod(ExprClass, nme.value) else NoSymbol - lazy val ExprModule = if (ExprsClass != NoSymbol) getMemberModule(ExprsClass, nme.Expr) else NoSymbol + lazy val 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 + 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)) 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 + def materializeWeakTypeTag = ReflectApiPackage.map(sym => getMemberMethod(sym, nme.materializeWeakTypeTag)) + def materializeTypeTag = ReflectApiPackage.map(sym => getMemberMethod(sym, nme.materializeTypeTag)) 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 + def ApiUniverseReify = ApiUniverseClass.map(sym => getMemberMethod(sym, nme.reify)) 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 +517,18 @@ 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] + lazy val MacroClass = getClassIfDefined("scala.reflect.macros.Macro") // 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 = MacroContextClass.map(sym => getMemberMethod(sym, nme.prefix)) + def MacroContextPrefixType = MacroContextClass.map(sym => getTypeMember(sym, tpnme.PrefixType)) + def MacroContextUniverse = MacroContextClass.map(sym => getMemberMethod(sym, nme.universe)) + def MacroContextExprClass = MacroContextClass.map(sym => getTypeMember(sym, tpnme.Expr)) + def MacroContextWeakTypeTagClass = MacroContextClass.map(sym => getTypeMember(sym, tpnme.WeakTypeTag)) + def MacroContextTreeType = MacroContextClass.map(sym => getTypeMember(sym, tpnme.Tree)) + lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl] - lazy val StringContextClass = requiredClass[scala.StringContext] - def StringContext_f = getMemberMethod(StringContextClass, nme.f) + lazy val StringContextClass = requiredClass[scala.StringContext] + def StringContext_f = getMemberMethod(StringContextClass, nme.f) lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature] lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature] @@ -553,7 +546,7 @@ trait Definitions extends api.StandardDefinitions { // 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 + def isArrowAssoc(sym: Symbol) = sym.owner == ArrowAssocClass // 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 { @@ -563,12 +556,6 @@ 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] = { @@ -591,7 +578,6 @@ trait Definitions extends api.StandardDefinitions { /** 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) @@ -606,14 +592,10 @@ 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 @@ -651,7 +633,13 @@ trait Definitions extends api.StandardDefinitions { len <= MaxTupleArity && sym == TupleClass(len) case _ => false } - def isTupleType(tp: Type) = isTupleTypeDirect(tp.normalize) + def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden) + + def isMacroBundleType(tp: Type) = { + val isNonTrivial = tp != ErrorType && tp != NothingTpe && tp != NullTpe + val isMacroCompatible = MacroClass != NoSymbol && tp <:< MacroClass.tpe + isNonTrivial && isMacroCompatible + } lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product] def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity) @@ -659,13 +647,8 @@ 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 { @@ -678,19 +661,17 @@ trait Definitions extends api.StandardDefinitions { case _ => tp } - def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match { - case RefinedType(p :: _, _) => p.normalize + def unapplyUnwrap(tpe:Type) = tpe.finalResultType.dealiasWiden match { + case RefinedType(p :: _, _) => p.dealiasWiden case tp => tp } - def functionApply(n: Int) = getMemberMethod(FunctionClass(n), nme.apply) - def abstractFunctionForFunctionType(tp: Type) = { assert(isFunctionType(tp), tp) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) } - def isFunctionType(tp: Type): Boolean = tp.normalize match { + def isFunctionType(tp: Type): Boolean = tp.dealiasWiden match { case TypeRef(_, sym, args) if args.nonEmpty => val arity = args.length - 1 // -1 is the return type arity <= MaxFunctionArity && sym == FunctionClass(arity) @@ -703,13 +684,6 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } - def isSeqType(tp: Type) = elementType(SeqClass, tp.normalize) != NoType - - def elementType(container: Symbol, tp: Type): Type = tp match { - case TypeRef(_, `container`, arg :: Nil) => arg - case _ => NoType - } - def arrayType(arg: Type) = appliedType(ArrayClass, arg) def byNameType(arg: Type) = appliedType(ByNameParamClass, arg) def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp) @@ -717,14 +691,23 @@ 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) + def ClassType(arg: Type) = if (phase.erasedTypes) ClassClass.tpe else appliedType(ClassClass, arg) - def ClassType(arg: Type) = - if (phase.erasedTypes || forMSIL) 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,9 +716,6 @@ 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 @@ -743,56 +723,18 @@ trait Definitions extends api.StandardDefinitions { * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. */ def classExistentialType(clazz: Symbol): Type = - newExistentialType(clazz.typeParams, clazz.tpe) - - /** Given type U, creates a Type representing Class[_ <: U]. - */ - def boundedClassType(upperBound: Type) = - appliedTypeAsUpperBounds(ClassClass.typeConstructor, List(upperBound)) - - /** To avoid unchecked warnings on polymorphic classes, translate - * a Foo[T] into a Foo[_] for use in the pattern matcher. - */ - @deprecated("Use classExistentialType", "2.10.0") - def typeCaseType(clazz: Symbol): Type = classExistentialType(clazz) - - // - // .NET backend - // + existentialAbstraction(clazz.typeParams, clazz.tpe_*) - 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 - }) - } + def unsafeClassExistentialType(clazz: Symbol): Type = + existentialAbstraction(clazz.unsafeTypeParams, clazz.tpe_*) // 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_== = enterNewMethod(AnyClass, nme.EQ, AnyTpe :: Nil, BooleanTpe, FINAL) + lazy val Any_!= = enterNewMethod(AnyClass, nme.NE, AnyTpe :: Nil, BooleanTpe, 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 +746,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 +776,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 +802,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 +830,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, AnyRefTpe :: Nil, BooleanTpe, FINAL) + lazy val Object_!= = enterNewMethod(ObjectClass, nme.NE, AnyRefTpe :: 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_) @@ -933,12 +866,6 @@ trait Definitions extends api.StandardDefinitions { lazy val BoxedNumberClass = getClassByName(sn.BoxedNumber) lazy val BoxedCharacterClass = getClassByName(sn.BoxedCharacter) lazy val BoxedBooleanClass = getClassByName(sn.BoxedBoolean) - lazy val BoxedByteClass = requiredClass[java.lang.Byte] - lazy val BoxedShortClass = requiredClass[java.lang.Short] - lazy val BoxedIntClass = requiredClass[java.lang.Integer] - lazy val BoxedLongClass = requiredClass[java.lang.Long] - lazy val BoxedFloatClass = requiredClass[java.lang.Float] - lazy val BoxedDoubleClass = requiredClass[java.lang.Double] lazy val Boxes_isNumberOrBool = getDecl(BoxesRunTimeClass, nme.isBoxedNumberOrBoolean) lazy val Boxes_isNumber = getDecl(BoxesRunTimeClass, nme.isBoxedNumber) @@ -959,7 +886,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,7 +894,6 @@ 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 DeprecatedAttr = requiredClass[scala.deprecated] lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] @@ -993,8 +918,8 @@ 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] @@ -1013,11 +938,21 @@ trait Definitions extends api.StandardDefinitions { // 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)) @@ -1039,7 +974,6 @@ trait Definitions extends api.StandardDefinitions { 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) @@ -1079,7 +1013,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") @@ -1107,16 +1040,13 @@ 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)) } @@ -1141,7 +1071,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] @@ -1158,8 +1087,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( @@ -1201,8 +1129,6 @@ trait Definitions extends api.StandardDefinitions { /** 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? */ @@ -1219,7 +1145,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!!! @@ -1231,10 +1157,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, '/') + ";" } @@ -1243,49 +1169,11 @@ 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 - } - def init() { if (isInitialized) return // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = symbolsNotPresentInBytecode + val _ = symbolsNotPresentInBytecode isInitialized = true } //init - - var nbScalaCallers: Int = 0 - def newScalaCaller(delegateType: Type): MethodSymbol = { - assert(forMSIL, "scalaCallers can only be created if target is .NET") - // object: reference to object on which to call (scala-)method - val paramTypes: List[Type] = List(ObjectClass.tpe) - val name = newTermName("$scalaCaller$$" + nbScalaCallers) - // tparam => resultType, which is the resultType of PolyType, i.e. the result type after applying the - // type parameter =-> a MethodType in this case - // TODO: set type bounds manually (-> MulticastDelegate), see newTypeParam - val newCaller = enterNewMethod(DelegateClass, name, paramTypes, delegateType, FINAL | STATIC) - // val newCaller = newPolyMethod(DelegateClass, name, - // tparam => MethodType(paramTypes, tparam.typeConstructor)) setFlag (FINAL | STATIC) - Delegate_scalaCallers = Delegate_scalaCallers ::: List(newCaller) - nbScalaCallers += 1 - newCaller - } - - // def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol, delType: Type) { - // assert(Delegate_scalaCallers contains scalaCaller) - // Delegate_scalaCallerInfos += (scalaCaller -> (methSym, delType)) - // } - - def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol) { - assert(Delegate_scalaCallers contains scalaCaller) - Delegate_scalaCallerTargets += (scalaCaller -> methSym) - } } } diff --git a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala index 3bcb793926..073f124630 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. @@ -77,14 +77,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) @@ -106,7 +106,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..961adb2c57 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 diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 5ebe02d95d..b8e3407824 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 } @@ -116,8 +117,21 @@ 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 DEFAULTMETHOD = 1L << 47 // symbol is a java default method + final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode + final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method + + /** Symbols which are marked ARTIFACT. (Expand this list?) + * + * - $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) + */ // Overridden. def flagToString(flag: Long): String = "" @@ -163,11 +177,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 +263,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: @@ -296,7 +309,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, @@ -422,8 +439,8 @@ class Flags extends ModifierFlags { case VARARGS => "<varargs>" // (1L << 43) case TRIEDCOOKING => "<triedcooking>" // (1L << 44) case SYNCHRONIZED => "<synchronized>" // (1L << 45) - case 0x400000000000L => "" // (1L << 46) - case DEFAULTMETHOD => "<defaultmethod>" // (1L << 47) + case ARTIFACT => "<artifact>" // (1L << 46) + case DEFAULTMETHOD => "<defaultmethod>" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) case 0x4000000000000L => "" // (1L << 50) @@ -497,4 +514,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/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 4a3663b8ea..420b5fef81 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._ @@ -158,13 +159,14 @@ trait HasFlags { else nonAccess + " " + access } + // Guess this can't be deprecated seeing as it's in the reflect API. + def isParameter = hasFlag(PARAM) + // Backward compat section @deprecated( "Use isTrait", "2.10.0") def hasTraitFlag = hasFlag(TRAIT) @deprecated("Use hasDefault", "2.10.0") def hasDefaultFlag = hasFlag(DEFAULTPARAM) - @deprecated("Use isValueParameter or isTypeParameter", "2.10.0") - def isParameter = hasFlag(PARAM) @deprecated("Use flagString", "2.10.0") def defaultFlagString = flagString @deprecated("Use flagString(mask)", "2.10.0") diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index 43902c1930..f8584ac9b0 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -1,21 +1,22 @@ -package scala.reflect +package scala +package reflect package internal import scala.collection.mutable.WeakHashMap import scala.ref.WeakReference // SI-6241: move importers to a mirror -trait Importers extends api.Importers { self: SymbolTable => +trait Importers extends api.Importers { to: SymbolTable => def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( - if (self eq from0) { + 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 +29,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 +50,162 @@ 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) + } + } + } 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) + 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 +214,70 @@ 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.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, selfsym) => + AnnotatedType(annots map importAnnotationInfo, importType(result), importSymbol(selfsym)) + 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 +286,136 @@ 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.emptyValDef => + emptyValDef + case from.pendingSuperCall => + pendingSuperCall + case from.ValDef(mods, name, tpt, rhs) => + new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs)) + case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) => + 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 importTree) + 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() + } + my + } + + // ============== MISCELLANEOUS ============== def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = { val atp1 = importType(ann.atp) @@ -303,11 +436,9 @@ trait Importers extends api.Importers { self: SymbolTable => NestedAnnotArg(importAnnotationInfo(annInfo)) } - def importTypeConstraint(constr: from.TypeConstraint): TypeConstraint = { - val result = new TypeConstraint(constr.loBounds map importType, constr.hiBounds map importType) - result.inst = importType(constr.inst) - result - } + // todo. 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,137 +446,12 @@ 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() - mytree - } - def importValDef(tree: from.ValDef): ValDef = importTree(tree).asInstanceOf[ValDef] def importTypeDef(tree: from.TypeDef): TypeDef = importTree(tree).asInstanceOf[TypeDef] def importTemplate(tree: from.Template): Template = importTree(tree).asInstanceOf[Template] 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/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 08686832ef..46a95c7d26 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) @@ -201,7 +202,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 + ")" ) @@ -229,4 +230,180 @@ trait Kinds { } } } -}
\ No newline at end of file + + /** + * 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()) + } + } + 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..bf38c3bf1e 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 @@ -33,18 +36,18 @@ trait Mirrors extends api.Mirrors { 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 +79,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 @@ -168,14 +173,15 @@ trait Mirrors extends api.Mirrors { 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 = + @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,8 +189,8 @@ 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)) override def staticPackage(fullname: String): ModuleSymbol = ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) @@ -192,8 +198,8 @@ trait Mirrors extends api.Mirrors { /************************ 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 +208,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 +234,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 +246,8 @@ trait Mirrors extends api.Mirrors { RootClass.info.decls enter EmptyPackage RootClass.info.decls enter RootPackage + + initialized = true } } 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..027e3a340a --- /dev/null +++ b/src/reflect/scala/reflect/internal/Mode.scala @@ -0,0 +1,142 @@ +/* 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) + def typingTypeByValue = inAll(TYPEmode | BYVALmode) + + 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..ed5b92565d 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -3,29 +3,15 @@ * @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 @@ -49,7 +35,7 @@ trait Names extends api.Names with LowPriorityNames { 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 +43,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 } @@ -135,9 +121,6 @@ trait Names extends api.Names with LowPriorityNames { def newTypeName(bs: Array[Byte], offset: Int, len: Int): TypeName = newTermName(bs, offset, len).toTypeName - def nameChars: Array[Char] = chrs - @deprecated("", "2.9.0") def view(s: String): TermName = newTermName(s) - // Classes ---------------------------------------------------------------------- /** The name class. @@ -186,28 +169,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 +196,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 +209,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 +217,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 +237,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 +261,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 +274,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 +282,7 @@ 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 } @@ -334,7 +294,7 @@ 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 } @@ -366,16 +326,18 @@ trait Names extends api.Names with LowPriorityNames { final def endsWith(char: Char): Boolean = len > 0 && endChar == char final def endsWith(name: String): Boolean = endsWith(newTermName(name)) - 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. @@ -424,24 +386,40 @@ trait Names extends api.Names with LowPriorityNames { def append(ch: Char) = newName("" + this + ch) def append(suffix: String) = newName("" + this + suffix) def append(suffix: Name) = newName("" + this + suffix) - def prepend(ch: Char) = newName("" + ch + this) def prepend(prefix: String) = newName("" + prefix + this) - def prepend(prefix: Name) = newName("" + prefix + this) def decodedName: ThisNameType = newName(decode) - def isOperatorName: Boolean = decode != toString + def 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 = stripSuffix(suffix: TermName) + 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]) @@ -485,7 +463,7 @@ trait Names extends api.Names with LowPriorityNames { type ThisNameType = TermName protected[this] def thisName: TermName = this - var next: TermName = termHashtable(hash) + val next: TermName = termHashtable(hash) termHashtable(hash) = this def isTermName: Boolean = true def isTypeName: Boolean = false @@ -510,11 +488,16 @@ trait Names extends api.Names with LowPriorityNames { implicit val TermNameTag = ClassTag[TermName](classOf[TermName]) + 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, hash: Int) extends Name(index0, len0) { type ThisNameType = TypeName protected[this] def thisName: TypeName = this - var next: TypeName = typeHashtable(hash) + val next: TypeName = typeHashtable(hash) typeHashtable(hash) = this def isTermName: Boolean = false def isTypeName: Boolean = true @@ -539,4 +522,9 @@ trait Names extends api.Names with LowPriorityNames { } 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..450c90ed56 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) { diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index faa161d6b1..eef8159ad4 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 new RangePosition(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) new OffsetPosition(source, point) + else new RangePosition(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(new RangePosition(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/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 80d247c0ea..1603029340 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 } @@ -25,26 +26,27 @@ 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.name.toTermName == nme.ERROR) + s"<$qname: error>" + else if (sym == null || sym == NoSymbol) + qname + 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. @@ -67,12 +69,12 @@ trait Printers extends api.Printers { self: SymbolTable => printIds = settings.uniqid.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) print(tree.pos.show) def println() { out.println() @@ -91,8 +93,8 @@ trait Printers extends api.Printers { self: SymbolTable => } 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) + 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) { @@ -168,7 +170,7 @@ trait Printers extends api.Printers { self: SymbolTable => ) def printFlags(flags: Long, privateWithin: String) { - var mask: Long = if (settings.debug.value) -1L else PrintableFlags + val mask: Long = if (settings.debug) -1L else PrintableFlags val s = flagsToString(flags & mask, privateWithin) if (s != "") print(s + " ") } @@ -208,7 +210,7 @@ trait Printers extends api.Printers { self: SymbolTable => case ModuleDef(mods, name, impl) => printAnnotations(tree) - printModifiers(tree, mods); + printModifiers(tree, mods) print("object " + symName(tree, name), " extends ", impl) case ValDef(mods, name, tp, rhs) => @@ -327,10 +329,10 @@ trait Printers extends api.Printers { self: SymbolTable => print(lhs, " = ", rhs) case If(cond, thenp, elsep) => - print("if (", cond, ")"); indent; println() - print(thenp); undent + print("if (", cond, ")"); indent(); println() + print(thenp); undent() if (!elsep.isEmpty) { - println(); print("else"); indent; println(); print(elsep); undent + println(); print("else"); indent(); println(); print(elsep); undent() } case Return(expr) => @@ -375,7 +377,7 @@ trait Printers extends api.Printers { self: SymbolTable => if (!qual.isEmpty) print(symName(tree, qual) + ".") print("this") - case Select(qual @ New(tpe), name) if (!settings.debug.value) => + case Select(qual @ New(tpe), name) if !settings.debug => print(qual) case Select(qualifier, name) => @@ -389,7 +391,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) { @@ -420,13 +422,20 @@ 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. +// 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, _) => @@ -435,7 +444,7 @@ trait Printers extends api.Printers { self: SymbolTable => case tree => xprintTree(this, tree) } - if (printTypes && tree.isTerm && !tree.isEmpty) { + if (printTypes && tree.isTerm && tree.canHaveAttrs) { print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") } } @@ -475,8 +484,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 +532,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 +549,17 @@ trait Printers extends api.Printers { self: SymbolTable => print(")") case EmptyTree => print("EmptyTree") - case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => + case self.emptyValDef => print("emptyValDef") + case self.pendingSuperCall => + print("pendingSuperCall") case tree: Tree => - val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol - val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString + val 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 +569,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, "]") @@ -651,7 +661,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(")") @@ -672,7 +682,7 @@ trait Printers extends api.Printers { self: SymbolTable => case nme.CONSTRUCTOR => "nme.CONSTRUCTOR" case nme.ROOTPKG => "nme.ROOTPKG" case _ => - val prefix = if (name.isTermName) "newTermName(\"" else "newTypeName(\"" + val prefix = if (name.isTermName) "TermName(\"" else "TypeName(\"" prefix + name.toString + "\")" } 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/Required.scala b/src/reflect/scala/reflect/internal/Required.scala index 842491d56d..14db252a16 100644 --- a/src/reflect/scala/reflect/internal/Required.scala +++ b/src/reflect/scala/reflect/internal/Required.scala @@ -1,15 +1,13 @@ -package scala.reflect +package scala +package reflect package internal import settings.MutableSettings trait Required { self: SymbolTable => - def picklerPhase: Phase - def settings: MutableSettings - def forInteractive: Boolean - - 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..8d20c8e546 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 @@ -61,6 +68,11 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** 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? @@ -82,6 +94,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 +110,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 +126,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,8 +133,6 @@ 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)) @@ -175,8 +187,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** remove entry - * - * @param e ... */ def unlink(e: ScopeEntry) { if (elems == e) { @@ -192,30 +202,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 = lookupAll(name.toTermName) find (_.isModule) getOrElse NoSymbol + def lookupClass(name: Name): Symbol = lookupAll(name.toTypeName) find (_.isClass) getOrElse NoSymbol + + /** 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 +267,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 +314,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,36 +371,16 @@ 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 - + 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") def reverse: List[Symbol] = toList.reverse diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index b782353ed3..9eb66db01e 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 { @@ -29,17 +30,4 @@ trait StdAttachments { * Therefore we need this hack (see `Reshape.toPreTyperTypeTree` for a detailed explanation). */ 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. - */ - case class MacroExpansionAttachment(original: Tree) - - /** 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). - */ - case object SuppressMacroExpansionAttachment } 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 de7af4340d..81fffc833c 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 final object compactify extends (String => String) { @@ -92,8 +88,12 @@ 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 SETTER_SUFFIX_STRING: String = NameTransformer.SETTER_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" @@ -104,7 +104,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" @@ -121,16 +120,12 @@ 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" @@ -206,10 +201,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>" @@ -220,12 +216,10 @@ trait StdNames { final val Any: NameType = "Any" final val AnyVal: NameType = "AnyVal" - final val ExprApi: NameType = "ExprApi" final val Mirror: NameType = "Mirror" final val Nothing: NameType = "Nothing" final val Null: NameType = "Null" final val Object: NameType = "Object" - final val PartialFunction: NameType = "PartialFunction" final val PrefixType: NameType = "PrefixType" final val Product: NameType = "Product" final val Serializable: NameType = "Serializable" @@ -239,7 +233,6 @@ trait StdNames { final val Group: NameType = "Group" final val Tree: NameType = "Tree" final val Type : NameType = "Type" - final val TypeTree: NameType = "TypeTree" // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" @@ -249,13 +242,11 @@ 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) @@ -272,26 +263,24 @@ 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 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 INTERPRETER_IMPORT_WRAPPER = "$iw" - val INTERPRETER_LINE_PREFIX = "line" - val INTERPRETER_VAR_PREFIX = "res" - val INTERPRETER_WRAPPER_SUFFIX = "$object" val LOCALDUMMY_PREFIX = "<local " // owner of local blocks val PROTECTED_PREFIX = "protected$" val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" val SUPER_PREFIX_STRING = "super$" - val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" val WHILE_PREFIX = "while$" // Compiler internal names @@ -300,12 +289,12 @@ 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 MACRO_INVOKER_PACKAGE: NameType = "scala.reflect.macros.synthetic" + // TODO: if I use dollars in MACRO_INVOKER_SUFFIX, as in "$Invoker$", then Scala reflection fails to load implementations + val MACRO_INVOKER_SUFFIX: NameType = "Invoker" val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." - val UNIVERSE_BUILD: NameType = "$u.build" val UNIVERSE_PREFIX: NameType = "$u." val UNIVERSE_SHORT: NameType = "$u" val MIRROR_PREFIX: NameType = "$m." @@ -318,21 +307,16 @@ trait StdNames { 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 @@ -340,7 +324,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) @@ -357,33 +340,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. @@ -393,24 +375,26 @@ trait StdNames { name drop idx + 2 } + @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 + + @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 + def unspecializedName(name: Name): Name = ( 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, * {{{ @@ -432,51 +416,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) @@ -488,8 +444,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" @@ -585,14 +539,10 @@ 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 ConstantType: NameType = "ConstantType" val EmptyPackage: NameType = "EmptyPackage" val EmptyPackageClass: NameType = "EmptyPackageClass" - val ExistentialTypeTree: NameType = "ExistentialTypeTree" val Flag : NameType = "Flag" val Ident: NameType = "Ident" val Import: NameType = "Import" @@ -601,10 +551,8 @@ trait StdNames { val Modifiers: NameType = "Modifiers" val NestedAnnotArg: NameType = "NestedAnnotArg" val NoFlags: NameType = "NoFlags" - val NoPrefix: NameType = "NoPrefix" val NoSymbol: NameType = "NoSymbol" val Nothing: NameType = "Nothing" - val NoType: NameType = "NoType" val Null: NameType = "Null" val Object: NameType = "Object" val RootPackage: NameType = "RootPackage" @@ -614,17 +562,14 @@ trait StdNames { val StringContext: NameType = "StringContext" val This: NameType = "This" val ThisType: NameType = "ThisType" - val Tree : NameType = "Tree" val Tuple2: NameType = "Tuple2" val TYPE_ : NameType = "TYPE" - val TypeApply: NameType = "TypeApply" val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" val add_ : NameType = "add" val annotation: NameType = "annotation" val anyValClass: NameType = "anyValClass" - val append: NameType = "append" val apply: NameType = "apply" val applyDynamic: NameType = "applyDynamic" val applyDynamicNamed: NameType = "applyDynamicNamed" @@ -632,34 +577,26 @@ trait StdNames { 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 clone_ : NameType = "clone" val conforms: NameType = "conforms" 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 drop: NameType = "drop" @@ -670,30 +607,23 @@ trait StdNames { 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 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 implicitly: NameType = "implicitly" val in: NameType = "in" - val info: NameType = "info" val inlinedEquals: NameType = "inlinedEquals" val isArray: NameType = "isArray" val isDefinedAt: NameType = "isDefinedAt" @@ -705,57 +635,41 @@ 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 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 rootMirror : NameType = "rootMirror" - val runOrElse: NameType = "runOrElse" val runtime: NameType = "runtime" val runtimeClass: NameType = "runtimeClass" val runtimeMirror: NameType = "runtimeMirror" - val sameElements: NameType = "sameElements" val scala_ : NameType = "scala" val selectDynamic: NameType = "selectDynamic" val selectOverloadedMethod: NameType = "selectOverloadedMethod" val selectTerm: NameType = "selectTerm" val selectType: NameType = "selectType" val self: NameType = "self" - val setAccessible: NameType = "setAccessible" val setAnnotations: NameType = "setAnnotations" val setSymbol: NameType = "setSymbol" val setType: NameType = "setType" @@ -765,21 +679,18 @@ trait StdNames { val staticModule : NameType = "staticModule" val staticPackage : NameType = "staticPackage" val synchronized_ : NameType = "synchronized" - val tail: NameType = "tail" - val `then` : NameType = "then" + 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 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" @@ -793,14 +704,10 @@ trait StdNames { 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" // unencoded operators object raw { - final val AMP : NameType = "&" final val BANG : NameType = "!" final val BAR : NameType = "|" final val DOLLAR: NameType = "$" @@ -809,7 +716,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 = "~" @@ -865,14 +771,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" @@ -1004,7 +903,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,16 +917,11 @@ trait StdNames { val javanme = nme.javaKeywords object nme extends TermNames { - - def isModuleVarName(name: Name): Boolean = - stripAnonNumberSuffix(name) endsWith MODULE_VAR_SUFFIX - def moduleVarName(name: TermName): TermName = newTermNameCached("" + name + MODULE_VAR_SUFFIX) def getCause = sn.GetCause def getClass_ = sn.GetClass - def getComponentType = sn.GetComponentType def getMethod_ = sn.GetMethod def invoke_ = sn.Invoke @@ -1041,55 +934,12 @@ trait StdNames { 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 { private val kw = new KeywordSetBuilder @@ -1147,7 +997,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" @@ -1157,22 +1011,16 @@ 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") @@ -1189,52 +1037,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/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 5ccf81b4b5..2ae9f81a09 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.annotation.elidable @@ -15,6 +16,7 @@ abstract class SymbolTable extends macros.Universe with Names with Symbols with Types + with Variances with Kinds with ExistentialsAndSkolems with FlagSets @@ -38,6 +40,7 @@ abstract class SymbolTable extends macros.Universe with StdAttachments with StdCreators with BuildUtils + with PrivateWithin { val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } @@ -45,28 +48,33 @@ abstract class SymbolTable extends macros.Universe def log(msg: => AnyRef): Unit 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)) def shouldLogAtThisPhase = false + def isPastTyper = false @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 (settings.debug) Console.err.println(msg) def throwableAsString(t: Throwable): String = "" + t /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */ - def debugStack(t: Throwable): Unit = debugwarn(throwableAsString(t)) + 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,6 +89,16 @@ 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) @@ -108,17 +126,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 +153,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 +196,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 +213,17 @@ 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) } + @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) - /** Since when it is to be "at" a phase is inherently ambiguous, - * a couple unambiguously named methods. - */ - @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) - @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) - @inline final def afterCurrentPhase[T](op: => T): T = atPhase(phase.next)(op) - @inline final def beforePrevPhase[T](op: => T): T = atPhase(phase.prev)(op) - - @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = - if (isAtPhaseAfter(target)) atPhase(target)(op) else op + @inline final def enteringPhaseNotLaterThan[T](target: Phase)(op: => T): T = + if (isAtPhaseAfter(target)) enteringPhase(target)(op) else op final def isValid(period: Period): Boolean = period != 0 && runId(period) == currentRunId && { @@ -231,7 +236,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,28 +307,20 @@ abstract class SymbolTable extends macros.Universe } object perRunCaches { - import java.lang.ref.WeakReference - import scala.runtime.ScalaRunTime.stringOf import scala.collection.generic.Clearable // Weak references so the garbage collector will take care of // letting us know when a cache is really out of commission. - private val caches = mutable.HashSet[WeakReference[Clearable]]() + private val caches = WeakHashSet[Clearable]() def recordCache[T <: Clearable](cache: T): T = { - caches += new WeakReference(cache) + caches += cache cache } def clearAll() = { debuglog("Clearing " + caches.size + " caches.") - caches foreach { ref => - val cache = ref.get() - if (cache == null) - caches -= ref - else - cache.clear() - } + caches foreach (_.clear) } def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]()) @@ -346,26 +343,15 @@ abstract class SymbolTable extends macros.Universe */ def isCompilerUniverse = false + @deprecated("Use enteringPhase", "2.10.0") + @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) + @deprecated("Use enteringPhaseNotLaterThan", "2.10.0") + @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = enteringPhaseNotLaterThan(target)(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 45c16b7302..2da9fa1cca 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 */ @@ -70,11 +70,30 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ val originalOwner = perRunCaches.newMap[Symbol, Symbol]() + // 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) + } + } + abstract class SymbolContextApiImpl extends SymbolContextApi { this: Symbol => 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 @@ -171,13 +190,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 +202,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 +237,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 +265,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 +350,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,7 +365,6 @@ 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)) @@ -409,14 +428,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) @@ -532,14 +543,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. @@ -589,13 +598,24 @@ 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 /** Does this symbol denote a wrapper created by the repl? */ final def isInterpreterWrapper = ( (this hasFlag MODULE) - && owner.isPackageClass + && isTopLevel && nme.isReplWrapperName(name) ) final def getFlag(mask: Long): Long = { @@ -607,6 +627,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = 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 @@ -642,11 +664,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 +701,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 @@ -713,10 +756,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) @@ -726,14 +766,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 @@ -751,19 +787,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 = @@ -813,19 +847,28 @@ 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 - ) + || 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 */ final def isLocal: Boolean = owner.isTerm @@ -843,41 +886,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 || @@ -886,9 +924,23 @@ 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 @@ -914,14 +966,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 @@ -950,18 +1002,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => rawowner } + // Like owner, but NoSymbol.owner == NoSymbol instead of throwing an exception. + final def safeOwner: Symbol = if (this eq NoSymbol) NoSymbol 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) @@ -991,10 +1040,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 `==`. */ @@ -1002,7 +1053,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 @@ -1021,7 +1072,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 @@ -1042,7 +1093,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else ((effectiveOwner.enclClass.fullNameAsName(separator) append separator): Name) append 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. @@ -1080,9 +1131,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 @@ -1099,12 +1147,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 @@ -1185,20 +1234,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). @@ -1234,13 +1324,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 } @@ -1252,9 +1342,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 @@ -1365,6 +1455,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isInitialized) info this } + def maybeInitialize = { + try { initialize ; true } + catch { case _: CyclicReference => debuglog("Hit cycle in maybeInitialize of $this") ; false } + } /** Called when the programmer requests information that might require initialization of the underlying symbol. * @@ -1407,14 +1501,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => !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 - } - - /** Was symbol's type updated during given phase? */ final def hasTypeAt(pid: Phase#Id): Boolean = { assert(isCompilerUniverse) var infos = this.infos @@ -1427,21 +1513,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. @@ -1465,7 +1550,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ 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 @@ -1477,9 +1562,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => // 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 } @@ -1546,7 +1631,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) } @@ -1636,6 +1721,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) @@ -1654,12 +1752,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 @@ -1740,10 +1849,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def thisSym: Symbol = this /** 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 @@ -1764,7 +1873,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 "$")) @@ -1783,7 +1892,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 @@ -1877,15 +1986,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 @@ -1922,7 +2023,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The top-level class containing this symbol. */ def enclosingTopLevelClass: Symbol = - if (owner.isPackageClass) { + if (isTopLevel) { if (isClass) this else moduleClass } else owner.enclosingTopLevelClass @@ -1931,11 +2032,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => (this.rawInfo ne NoType) && (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: @@ -2031,70 +2132,111 @@ 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 = ( + (this ne NoSymbol) + && 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 + var bcs = base.info.baseClasses dropWhile (owner != _) drop 1 var sym: Symbol = NoSymbol while (!bcs.isEmpty && sym == NoSymbol) { if (!bcs.head.isImplClass) @@ -2107,22 +2249,23 @@ 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) + final def getter(base: Symbol): Symbol = + base.info decl getterName filter (_.hasAccessorFlag) - def getterName: TermName = ( - if (isSetter) nme.setterToGetter(name.toTermName) - else if (nme.isLocalName(name)) nme.localToGetter(name.toTermName) - else name.toTermName - ) + 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) + final def setter(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = + base.info decl setterNameInBase(base, hasExpandedName) filter (_.hasAccessorFlag) - 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) - } + 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. @@ -2179,29 +2322,17 @@ 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) } @@ -2222,9 +2353,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ toString ------------------------------------------------------------------- - /** A tag which (in the ideal case) uniquely identifies class symbols */ - final def tag: Int = fullName.## - /** The simple name of this Symbol */ final def simpleName: Name = name @@ -2251,7 +2379,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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") @@ -2314,12 +2442,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If settings.uniqid, adds 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 id_s = if (settings.uniqid.value) "#" + id else "" + val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" + + name_s + id_s + kind_s + } def fullNameString: String = { def recur(sym: Symbol): String = { @@ -2455,6 +2584,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isMethod = this hasFlag METHOD override def isModule = this hasFlag MODULE override def isOverloaded = this hasFlag OVERLOADED + /*** !!! 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 isPackage = this hasFlag PACKAGE override def isValueParameter = this hasFlag PARAM @@ -2467,13 +2604,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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. @@ -2553,36 +2684,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]) @@ -2669,11 +2770,28 @@ trait Symbols extends api.Symbols { self: SymbolTable => class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { type TypeOfClonedSymbol = TypeSymbol + override def variance = if (hasLocalFlag) 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 @@ -2702,7 +2820,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 @@ -2720,7 +2837,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 @@ -2742,63 +2858,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) { @@ -2824,15 +2940,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) } @@ -2862,11 +2969,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 */ @@ -2925,7 +3031,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) @@ -2951,23 +3057,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isLocalClass = ( isAnonOrRefinementClass || isLocal - || !owner.isPackageClass && owner.isLocalClass + || !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 @@ -2976,8 +3067,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 @@ -2994,7 +3084,11 @@ 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 = { @@ -3043,9 +3137,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 } @@ -3153,6 +3245,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]]. */ @@ -3180,8 +3274,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 @@ -3231,7 +3323,7 @@ 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 associatedFile = NoAbstractFile override def ownerChain: List[Symbol] = List() override def ownersIterator: Iterator[Symbol] = Iterator.empty override def alternatives: List[Symbol] = List() @@ -3239,15 +3331,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => 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 @@ -3324,6 +3413,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. @@ -3352,12 +3444,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ----- 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 } diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index c1753fc5a1..b75fd72526 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal abstract class TreeGen extends macros.TreeBuilder { @@ -11,10 +12,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) setSymbol AnyRefClass // used in ide def scalaFunctionConstr(argtpes: List[Tree], restpe: Tree, abstractFun: Boolean = false): Tree = { val cls = if (abstractFun) @@ -116,7 +114,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 +124,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 +159,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) @@ -213,7 +211,7 @@ abstract class TreeGen extends macros.TreeBuilder { 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,9 +270,13 @@ 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()) + if (elems.isEmpty) Literal(Constant(())) else Apply( Select(mkAttributedRef(TupleClass(elems.length).caseModule), nme.apply), elems) @@ -295,4 +293,13 @@ abstract class TreeGen extends macros.TreeBuilder { assert(ReflectRuntimeUniverse != NoSymbol) mkAttributedRef(ReflectRuntimeUniverse) setType singleType(ReflectRuntimeUniverse.owner.thisPrefix, ReflectRuntimeUniverse) } + + def mkPackageDef(packageName: String, stats: List[Tree]): PackageDef = { + PackageDef(mkUnattributedRef(newTermName(packageName)), stats) + } + + def mkSeqApply(arg: Tree): Apply = { + val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply) + Apply(factory, List(arg)) + } } diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index fa4441e513..3a8d3fd460 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, MacroContextClass, MacroContextPrefixType, uncheckedStableClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -65,6 +66,83 @@ 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 Ident(_) => symOk(tree.symbol) && tree.symbol.isStable && !tree.symbol.hasVolatileType // TODO SPEC: not required by spec + 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) + ) + + /** 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) @@ -108,80 +186,69 @@ 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) = tree match { + 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) || { + 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 { @@ -196,7 +263,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 @@ -210,16 +277,16 @@ abstract class TreeInfo { * same object? */ def isSelfConstrCall(tree: Tree): Boolean = tree match { - case Applied(Ident(nme.CONSTRUCTOR), _, _) => true + case Applied(Ident(nme.CONSTRUCTOR), _, _) => true case Applied(Select(This(_), nme.CONSTRUCTOR), _, _) => true - case _ => false + case _ => false } /** Is tree a super constructor call? */ def isSuperConstrCall(tree: Tree): Boolean = tree match { case Applied(Select(Super(_, _), nme.CONSTRUCTOR), _, _) => true - case _ => false + case _ => false } /** @@ -325,10 +392,6 @@ 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 - } /** The first constructor definitions in `stats` */ def firstConstructor(stats: List[Tree]): Tree = stats find { @@ -346,6 +409,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 @@ -384,15 +450,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 @@ -408,8 +476,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: Typed): Option[Tree] = tree match { + case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => Some(expr) + case _ => None + } } /** If this tree has type parameters, those. Otherwise Nil. @@ -445,6 +520,23 @@ abstract class TreeInfo { case _ => false } + private def hasNoSymbol(t: Tree) = t.symbol == null || t.symbol == NoSymbol + + /** If this CaseDef assigns a name to its top-level pattern, + * in the form 'expr @ pattern' or 'expr: pattern', returns + * the name. Otherwise, nme.NO_NAME. + * + * Note: in the case of Constant patterns such as 'case x @ "" =>', + * the pattern matcher eliminates the binding and inlines the constant, + * so as far as this method is likely to be able to determine, + * the name is NO_NAME. + */ + def assignedNameOfPattern(cdef: CaseDef): Name = cdef.pat match { + case Bind(name, _) => name + case Ident(name) => name + case _ => nme.NO_NAME + } + /** 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 { @@ -453,14 +545,13 @@ abstract class TreeInfo { } /** Does this CaseDef catch Throwable? */ - def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe) - - /** Does this CaseDef catch everything of a certain Type? */ - def catchesAllOf(cdef: CaseDef, threshold: Type) = - isDefaultCase(cdef) || (cdef.guard.isEmpty && (unbind(cdef.pat) match { - case Typed(Ident(nme.WILDCARD), tpt) => (tpt.tpe != null) && (threshold <:< tpt.tpe) - case _ => false - })) + def catchesThrowable(cdef: CaseDef) = ( + cdef.guard.isEmpty && (unbind(cdef.pat) match { + case Ident(nme.WILDCARD) => true + case i@Ident(name) => hasNoSymbol(i) + case _ => false + }) + ) /** Is this pattern node a catch-all or type-test pattern? */ def isCatchCase(cdef: CaseDef) = cdef match { @@ -485,7 +576,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 _ => @@ -538,6 +629,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: * @@ -633,6 +728,12 @@ abstract class TreeInfo { } loop(tree) } + + override def toString = { + val tstr = if (targs.isEmpty) "" else targs.mkString("[", ", ", "]") + val astr = argss map (args => args.mkString("(", ", ", ")")) mkString "" + s"$core$tstr$astr" + } } /** Returns a wrapper that knows how to destructure and analyze applications. @@ -649,6 +750,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)) @@ -656,20 +759,9 @@ abstract class TreeInfo { unapply(dissectApplied(tree)) } - /** Does list of trees start with a definition of - * a class of module with given name (ignoring imports) - */ - def firstDefinesClassOrObject(trees: List[Tree], name: Name): Boolean = trees match { - case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name) - case Annotated(_, tree1) :: Nil => firstDefinesClassOrObject(List(tree1), name) - case ModuleDef(_, `name`, _) :: Nil => true - case ClassDef(_, `name`, _, _) :: Nil => true - case _ => false - } - - /** Is this file the body of a compilation unit which should not - * have Predef imported? + * have Predef imported? This is the case iff the first import in the + * unit explicitly refers to Predef. */ def noPredefImportForUnit(body: Tree) = { // Top-level definition whose leading imports include Predef. @@ -678,13 +770,7 @@ abstract class TreeInfo { case Import(expr, _) => isReferenceToPredef(expr) case _ => false } - // Compilation unit is class or object 'name' in package 'scala' - def isUnitInScala(tree: Tree, name: Name) = tree match { - case PackageDef(Ident(nme.scala_), defs) => firstDefinesClassOrObject(defs, name) - case _ => false - } - - isUnitInScala(body, nme.Predef) || isLeadingPredefImport(body) + isLeadingPredefImport(body) } def isAbsTypeDef(tree: Tree) = tree match { @@ -750,8 +836,18 @@ 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 owner = + if (isBundle) qual.tpe.typeSymbol + else { + val sym = if (qual.hasSymbolField) qual.symbol else NoSymbol + if (sym.isModule) sym.moduleClass else sym + } + Some((isBundle, owner, ref.symbol, dissectApplied(tree).targs)) + } + case _ => None } } @@ -761,4 +857,15 @@ abstract class TreeInfo { case tree: RefTree => true case _ => false }) + + def isMacroApplication(tree: Tree): Boolean = + !tree.isDef && tree.symbol != null && tree.symbol.isMacro && !tree.symbol.isErroneous + + def isMacroApplicationOrBlock(tree: Tree): Boolean = tree match { + case Block(_, expr) => isMacroApplicationOrBlock(expr) + case tree => isMacroApplication(tree) + } + + def isNonTrivialMacroApplication(tree: Tree): Boolean = + isMacroApplication(tree) && dissectApplied(tree).core != tree } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 2585b541ed..8781423a6d 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import Flags._ @@ -14,6 +15,23 @@ 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.safeLine, 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.safeLine) 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 +42,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 +86,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 } @@ -158,6 +182,9 @@ trait Trees extends api.Trees { self: SymbolTable => override def substituteThis(clazz: Symbol, to: Tree): Tree = new ThisSubstituter(clazz, to) transform this + def replace(from: Tree, to: Tree): Tree = + new TreeReplacer(from, to, positionAware = false) transform this + def hasSymbolWhich(f: Symbol => Boolean) = (symbol ne null) && (symbol ne NoSymbol) && f(symbol) @@ -210,12 +237,15 @@ trait Trees extends api.Trees { self: SymbolTable => trait TypTree extends Tree with TypTreeApi abstract class SymTree extends Tree with SymTreeContextApi { - override def hasSymbol = true + 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 +253,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 { @@ -416,6 +451,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 { @@ -511,7 +556,7 @@ trait Trees extends api.Trees { self: SymbolTable => case t => t } - orig = followOriginal(tree); setPos(tree.pos); + orig = followOriginal(tree); setPos(tree.pos) this } @@ -603,6 +648,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]) = @@ -866,7 +912,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 +957,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,13 +970,16 @@ 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]) @@ -965,12 +1014,23 @@ trait Trees extends api.Trees { self: SymbolTable => def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree) - object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) { - override def isEmpty = true + trait CannotHaveAttrs extends Tree { + override def canHaveAttrs = false + + private def unsupported(what: String, args: Any*) = + throw new UnsupportedOperationException(s"$what($args) inapplicable for "+self.toString) + super.setPos(NoPosition) - override def setPos(pos: Position) = { assert(false); this } + override def setPos(pos: Position) = unsupported("setPos", pos) + + super.setType(NoType) + override def tpe_=(t: Type) = if (t != NoType) unsupported("tpe_=", t) } + case object EmptyTree extends TermTree with CannotHaveAttrs { override def isEmpty = true; val asList = List(this) } + object emptyValDef 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 + def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = atPos(sym.pos) { assert(sym != NoSymbol) @@ -1050,6 +1110,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: _*) @@ -1130,7 +1193,7 @@ trait Trees extends api.Trees { self: SymbolTable => traverse(annot); traverse(arg) case Template(parents, self, body) => traverseTrees(parents) - if (!self.isEmpty) traverse(self) + if (self ne emptyValDef) traverse(self) traverseStats(body, tree.symbol) case Block(stats, expr) => traverseTrees(stats); traverse(expr) @@ -1372,6 +1435,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 +1460,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 +1473,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 +1483,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 +1520,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 => @@ -1517,6 +1589,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,14 +1607,7 @@ trait Trees extends api.Trees { self: SymbolTable => } } - private lazy val duplicator = new Transformer { - override val treeCopy = newStrictTreeCopier - override def transform(t: Tree) = { - val t1 = super.transform(t) - if ((t1 ne t) && t1.pos.isRange) t1 setPos t.pos.focus - t1 - } - } + def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree // ------ copiers ------------------------------------------- @@ -1590,6 +1664,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)) diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala index 68b4fa69a1..71f84ab557 100644 --- a/src/reflect/scala/reflect/internal/TypeDebugging.scala +++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala @@ -3,14 +3,13 @@ * @author Paul Phillips */ -package scala.reflect +package scala +package reflect package internal trait TypeDebugging { self: SymbolTable => - import definitions._ - // @M toString that is safe during debugging (does not normalize, ...) object typeDebug { private def to_s(x: Any): String = x match { @@ -20,7 +19,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 { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fc3f5de77f..967146a130 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3,7 +3,8 @@ * @author Martin Odersky */ -package scala.reflect +package scala +package reflect package internal import scala.collection.{ mutable, immutable, generic } @@ -14,8 +15,8 @@ import Flags._ import scala.util.control.ControlThrowable import scala.annotation.tailrec import util.Statistics -import scala.runtime.ObjectRef import util.ThreeValues._ +import Variance._ /* A standard type pattern match: case ErrorType => @@ -68,33 +69,37 @@ 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 { 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 + protected[internal] final val DefaultLogThreshhold = 50 + private final val LogPendingBaseTypesThreshold = DefaultLogThreshhold + private final val LogVolatileThreshold = DefaultLogThreshhold /** A don't care value for the depth parameter in lubs/glbs and related operations. */ - private final val AnyDepth = -3 + protected[internal] 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 + protected[internal] 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 + private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn off type parameter bounds being used * to seed type constraints. */ @@ -102,107 +107,23 @@ trait Types extends api.Types { self: SymbolTable => 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) + + def apply(from: List[Symbol], to: List[Type]): SubstTypeMap = { + if ((cached.from ne from) || (cached.to ne to)) + cached = new SubstTypeMap(from, to) + + cached + } + } /** The current skolemization level, needed for the algorithms * in isSameType, isSubType that do constraint solving under a prefix. */ var skolemizationLevel = 0 - /** A log of type variable with their original constraints. Used in order - * to undo constraints in the case of isSubType/isSameType failure. - */ - lazy val undoLog = newUndoLog - - protected def newUndoLog = new UndoLog - - class UndoLog extends Clearable { - private type UndoPairs = List[(TypeVar, TypeConstraint)] - //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() - } - } - /** 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, @@ -223,7 +144,6 @@ 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 @@ -256,7 +176,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 @@ -272,7 +199,6 @@ trait Types extends api.Types { self: SymbolTable => 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) @@ -297,7 +223,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 { @@ -361,9 +286,6 @@ trait Types extends api.Types { self: SymbolTable => */ def isVolatile: Boolean = false - /** Is this type guaranteed not to have `null` as a value? */ - def isNotNull: Boolean = false - /** Is this type a structural refinement type (it ''refines'' members that have not been inherited) */ def isStructuralRefinement: Boolean = false @@ -384,15 +306,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 +407,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,11 +442,6 @@ 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 @@ -557,13 +470,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,6 +495,26 @@ 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 @@ -598,6 +524,8 @@ trait Types extends api.Types { self: SymbolTable => /** 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 @@ -686,16 +614,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 +634,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 +667,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 +687,7 @@ trait Types extends api.Types { self: SymbolTable => * }}} */ def memberInfo(sym: Symbol): Type = { + require(sym ne NoSymbol, this) sym.info.asSeenFrom(this, sym.owner) } @@ -792,8 +710,7 @@ trait Types extends api.Types { self: SymbolTable => * 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 +747,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 +754,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,9 +778,6 @@ 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) @@ -874,23 +789,27 @@ trait Types extends api.Types { self: SymbolTable => } /** 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) && { + log(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) @@ -921,12 +840,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 +857,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. @@ -971,7 +885,7 @@ trait Types extends api.Types { self: SymbolTable => def baseTypeSeqDepth: Int = 1 /** The list of all baseclasses of this type (including its own typeSymbol) - * in reverse linearization order, starting with the class itself and ending + * in linearization order, starting with the class itself and ending * in class Any. */ def baseClasses: List[Symbol] = List() @@ -1058,7 +972,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 @@ -1072,69 +986,66 @@ trait Types extends api.Types { self: SymbolTable => } 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 + def findMembersInternal: Scope = { + var members: Scope = null + 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 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 } - 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 + 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 (members eq null) EmptyScope else members + } + + if (this.isGround) findMembersInternal + else suspendingTypeVars(typeVarsInType(this))(findMembersInternal) } /** @@ -1148,110 +1059,106 @@ trait Types extends api.Types { self: SymbolTable => */ //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 + def findMemberInternal: Symbol = { + var member: Symbol = NoSymbol + var members: List[Symbol] = null + var lastM: ::[Symbol] = null + 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 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) } - 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) || + 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 && !sym.hasVolatileType)) { + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + 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 (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 + 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 } - } 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) + 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 (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) + } } + + 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,10 +1187,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 @@ -1316,17 +1219,6 @@ trait Types extends api.Types { self: SymbolTable => override def baseTypeSeq: BaseTypeSeq = supertype.baseTypeSeq override def baseTypeSeqDepth: Int = supertype.baseTypeSeqDepth override def baseClasses: List[Symbol] = supertype.baseClasses - override def isNotNull = supertype.isNotNull - } - - case class NotNullType(override val underlying: Type) extends SubType with RewrappingTypeProxy { - def supertype = underlying - protected def rewrap(newtp: Type): Type = NotNullType(newtp) - override def isNotNull: Boolean = true - override def notNull = this - override def deconst: Type = underlying //todo: needed? - override def safeToString: String = underlying.toString + " with NotNull" - override def kind = "NotNullType" } /** A base class for types that represent a single value @@ -1376,7 +1268,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 +1277,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,7 +1301,6 @@ 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" } @@ -1421,7 +1310,6 @@ trait Types extends api.Types { self: SymbolTable => override def isStable: Boolean = true override def prefixString = "" override def safeToString: String = "<noprefix>" - // override def isNullable: Boolean = true override def kind = "NoPrefixType" } @@ -1434,15 +1322,13 @@ trait Types extends api.Types { self: SymbolTable => 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 +1346,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 +1361,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 = { @@ -1491,7 +1375,7 @@ 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 isVolatile : Boolean = underlying.isVolatile && (sym.hasVolatileType || !sym.isStable) /* override def narrow: Type = { if (phase.erasedTypes) this @@ -1531,7 +1415,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 +1427,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,23 +1454,36 @@ 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 sytanx. + * 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] } @@ -1657,20 +1553,48 @@ 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); - override def safeToString: String = parentsString(parents) + ( - (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + (if (settings.debug || 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) = { val period = tpe.baseTypeSeqPeriod if (period != currentPeriod) { @@ -1690,7 +1614,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 +1633,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 +1655,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) } } @@ -1960,8 +1904,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 +1948,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 +1956,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,15 +1967,10 @@ 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) + if (settings.debug || decls.size > 1) formattedToString else super.safeToString @@ -2040,7 +1979,7 @@ trait Types extends api.Types { self: SymbolTable => */ def formattedToString: String = parents.mkString("\n with ") + ( - if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + if (settings.debug || parents.isEmpty || (decls.elems ne null)) fullyInitializeScope(decls).mkString(" {\n ", "\n ", "\n}") else "" ) @@ -2052,19 +1991,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" } @@ -2094,7 +2028,7 @@ trait Types extends api.Types { self: SymbolTable => // 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)) + devWarning(s"$this.transform($tp), but tparams.isEmpty and args=$args") asSeenFromOwner(tp).instantiateTypeParams(sym.typeParams, args) } @@ -2108,14 +2042,13 @@ 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 isStable = pre.isStable 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 @@ -2124,6 +2057,7 @@ trait Types extends api.Types { self: SymbolTable => } class PackageTypeRef(pre0: Type, sym0: Symbol) extends ModuleTypeRef(pre0, sym0) { require(sym.isPackageClass, sym) + override def isStable = true override protected def finishPrefix(rest: String) = packagePrefix + rest } class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { @@ -2252,7 +2186,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 @@ -2326,7 +2260,6 @@ trait Types extends api.Types { self: SymbolTable => } 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" } @@ -2359,7 +2292,7 @@ trait Types extends api.Types { self: SymbolTable => 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) } @@ -2420,9 +2353,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 @@ -2458,7 +2388,7 @@ trait Types extends api.Types { self: SymbolTable => // 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)) ) @@ -2483,7 +2413,7 @@ trait Types extends api.Types { self: SymbolTable => case RepeatedParamClass => args.head + "*" case ByNameParamClass => "=> " + args.head case _ => - def targs = normalize.typeArgs + def targs = dealiasWiden.typeArgs if (isFunctionType(this)) { // Aesthetics: printing Function1 as T => R rather than (T) => R @@ -2491,7 +2421,7 @@ trait Types extends api.Types { self: SymbolTable => // from (T1, T2) => R. targs match { case in :: out :: Nil if !isTupleType(in) => - // A => B => C should be (A => B) => C or A => (B => C). + // 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 @@ -2509,12 +2439,12 @@ trait Types extends api.Types { self: SymbolTable => "" } 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 +2455,32 @@ trait Types extends api.Types { self: SymbolTable => else super.prefixString ) + def copy(pre: Type = this.pre, sym: Symbol = this.sym, args: List[Type] = this.args) = TypeRef(pre, sym, args) 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 (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 +2492,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) } } } @@ -2820,10 +2759,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 @@ -2847,7 +2786,7 @@ trait Types extends api.Types { self: SymbolTable => 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) && + solve(tvars, quantifiedFresh, quantifiedFresh map (_ => Invariant), upper = false, depth) && isWithinBounds(NoPrefix, NoSymbol, quantifiedFresh, tvars map (_.constr.inst)) } } @@ -2865,9 +2804,13 @@ 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) + } /** A class remembering a type instantiation for some a set of overloaded * polymorphic symbols. @@ -2875,23 +2818,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 +2838,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 +2870,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 +2914,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 +2924,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.) @@ -3024,10 +2938,6 @@ 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 + "[", ", ", "]") - ) } trait UntouchableTypeVar extends TypeVar { @@ -3057,7 +2967,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,7 +2987,6 @@ 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 @@ -3132,7 +3041,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 @@ -3187,7 +3099,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 +3126,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] * }}} @@ -3226,10 +3138,9 @@ trait Types extends api.Types { self: SymbolTable => sameLength(typeArgs, tp.typeArgs) && { val lhs = if (isLowerBound) tp.typeArgs else typeArgs val rhs = if (isLowerBound) typeArgs else tp.typeArgs - // this is a higher-kinded type var with same arity as tp. - // side effect: adds the type constructor itself as a bound - addBound(tp.typeConstructor) - isSubArgs(lhs, rhs, params, AnyDepth) + // This is a higher-kinded type var with same arity as tp. + // If so (see SI-7517), side effect: adds the type constructor itself as a bound. + isSubArgs(lhs, rhs, params, AnyDepth) && { addBound(tp.typeConstructor); true } } } // The type with which we can successfully unify can be hidden @@ -3282,16 +3193,19 @@ 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 + def checkIsSameType(tp: Type) = ( + if (typeVarLHS) constr.inst =:= tp + else tp =:= constr.inst + ) if (suspended) tp =:= origin else if (constr.instValid) checkIsSameType(tp) else isRelatable(tp) && { val newInst = wildcardToTypeVarMap(tp) - (constr isWithinBounds newInst) && { setInst(tp); true } + (constr isWithinBounds newInst) && { + setInst(newInst) + true + } } } @@ -3302,7 +3216,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 { @@ -3353,8 +3267,7 @@ 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 @@ -3465,22 +3378,12 @@ 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. + /** As with NamedType, used only when calling isApplicable. + * Records that the application has a wildcard star (aka _*) + * at the end of it. */ - case class DeBruijnIndex(level: Int, idx: Int, args: List[Type]) extends Type { - override def safeToString: String = "De Bruijn index("+level+","+idx+")" - } - - /** A binder defining data associated with De Bruijn indices. Only used - * as a serialization format. - */ - case class DeBruijnBinder(pnames: List[Name], ptypes: List[Type], restpe: Type) extends Type { - override def safeToString = { - val kind = if (pnames.head.isTypeName) "poly" else "method" - "De Bruijn "+kind+"("+(pnames mkString ",")+";"+(ptypes mkString ",")+";"+restpe+")" - } + case class RepeatedType(tp: Type) extends Type { + override def safeToString: String = tp + ": _*" } /** A temporary type representing the erasure of a user-defined value type. @@ -3525,17 +3428,12 @@ 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 => sym.isType || (sym.isStable && !sym.hasVolatileType)) orElse sym } /** Convert a `super` prefix to a this-type if `sym` is abstract or final. */ @@ -3564,7 +3462,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) @@ -3574,10 +3472,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) @@ -3686,7 +3580,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 ", "}]""" @@ -3697,16 +3591,16 @@ 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 ErrorType | WildcardType => tycon case _ => abort(debugString(tycon)) } } @@ -3715,25 +3609,6 @@ trait Types extends api.Types { self: SymbolTable => 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). @@ -3807,142 +3682,18 @@ 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 -------------------------------------------------------------- private val initialUniquesCapacity = 4096 - private var uniques: util.HashSet[Type] = _ + private var uniques: util.WeakHashSet[Type] = _ private var uniqueRunId = NoRunId protected def unique[T <: Type](tp: T): T = { if (Statistics.canEnable) Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { - uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) + uniques = util.WeakHashSet[Type](initialUniquesCapacity) perRunCaches.recordCache(uniques) uniqueRunId = currentRunId } @@ -3951,121 +3702,6 @@ 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) @@ -4076,309 +3712,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) => @@ -4389,630 +3785,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) - } - } - - 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 @@ -5060,204 +3849,10 @@ 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 - } - } - - class SubTypePair(val tp1: Type, val tp2: Type) { - override def hashCode = tp1.hashCode * 41 + tp2.hashCode - override def equals(other: Any) = other match { - case stp: SubTypePair => - // suspend TypeVars in types compared by =:=, - // since we don't want to mutate them simply to check whether a subtype test is pending - // in addition to making subtyping "more correct" for type vars, - // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion) - // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold) - def suspend(tp: Type) = - if (tp.isGround) null else suspendTypeVarsInType(tp) - def revive(suspension: List[TypeVar]) = - if (suspension ne null) suspension foreach (_.suspended = false) - - val suspensions = Array(tp1, stp.tp1, tp2, stp.tp2) map suspend - - val sameTypes = (tp1 =:= stp.tp1) && (tp2 =:= stp.tp2) - - suspensions foreach revive - - sameTypes - case _ => - false - } - override def toString = tp1+" <:<? "+tp2 - } - // Helper Methods ------------------------------------------------------------- /** The maximum allowable depth of lubs or glbs over types `ts`. @@ -5273,47 +3868,15 @@ trait Types extends api.Types { self: SymbolTable => * 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 + if (settings.XfullLubs) 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 typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) - private def baseTypeSeqDepth(tps: List[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) + private def symTypeDepth(syms: List[Symbol]): Int = typeDepth(syms map (_.info)) + private def typeDepth(tps: List[Type]): Int = maxDepth(tps) + private def baseTypeSeqDepth(tps: List[Type]): Int = maxBaseTypeSeqDepth(tps) /** Is intersection of given types populated? That is, * for all types tp1, tp2 in intersection @@ -5326,32 +3889,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 @@ -5394,85 +3960,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: @@ -5484,288 +3979,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)(_ =:= _) @@ -5783,64 +3997,9 @@ 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 - else - try { - pendingSubTypes += p - isSubType2(tp1, tp2, depth) - } finally { - pendingSubTypes -= p - } - } else { - isSubType2(tp1, tp2, depth) - } - } 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() - } /** 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, @@ -5858,18 +4017,6 @@ trait Types extends api.Types { self: SymbolTable => 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 isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType) def isSingleType(tp: Type) = tp match { @@ -5882,6 +4029,16 @@ trait Types extends api.Types { self: SymbolTable => case _ => false } + def isExistentialType(tp: Type): Boolean = tp.dealias match { + case ExistentialType(_, _) => true + 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 * level types which are not value types (e.g. MethodType) without excluding * necessary internal types such as WildcardType. There are also non-value @@ -5907,10 +4064,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 @@ -5918,6 +4076,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 @@ -5963,7 +4122,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? @@ -5974,7 +4133,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 @@ -5983,285 +4142,41 @@ 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)) + def isSubArg(t1: Type, t2: Type, variance: Variance) = ( + (variance.isContravariant || isSubType(t1, t2, depth)) + && (variance.isCovariant || isSubType(t2, t1, 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 - } - } - - /** 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 - } - - /** 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: Int): 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`? - */ - def isSubTypes(tps1: List[Type], tps2: List[Type]): Boolean = (tps1 corresponds tps2)(_ <:< _) - - /** Does type `tp` implement symbol `sym` with same or - * stronger type? Exact only if `sym` is a member of some - * refinement type, otherwise we might return false negatives. - */ - def specializesSym(tp: Type, sym: Symbol): Boolean = - 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))) - } - /** 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 = { + protected[internal] def specializesSym(tp1: Type, sym1: Symbol, tp2: Type, sym2: Symbol, depth: Int): Boolean = { + require((sym1 ne NoSymbol) && (sym2 ne NoSymbol), ((tp1, sym1, tp2, sym2, depth))) 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.isTerm && isSubType(info1, info2, depth) && (!sym2.isStable || sym1.isStable) && (!sym1.hasVolatileType || sym2.hasVolatileType) || sym2.isAbstractType && { val memberTp1 = tp1.memberType(sym1) // println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner))) @@ -6281,7 +4196,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(_, _) => @@ -6301,7 +4216,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 _ => @@ -6314,7 +4229,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 _ => @@ -6328,7 +4243,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 } @@ -6337,7 +4252,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 => @@ -6390,7 +4305,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 => @@ -6411,7 +4326,7 @@ trait Types extends api.Types { self: SymbolTable => * `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 + if (xs.isEmpty || ys.isEmpty) xs else { val x1 = f(xs.head, ys.head) val xs1 = map2Conserve(xs.tail, ys.tail)(f) @@ -6419,87 +4334,6 @@ trait Types extends api.Types { self: SymbolTable => 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 = { @@ -6512,563 +4346,12 @@ 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 - } - def elimAnonymousClass(t: Type) = t match { case TypeRef(pre, clazz, Nil) if clazz.isAnonymousClass => clazz.classBound.asSeenFrom(pre, clazz.owner) case _ => t } - def elimRefinement(t: Type) = t match { - case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) - case _ => t - } - - /** Eliminate from list of types all elements which are a subtype - * of some other element of the list. */ - private def elimSub(ts: List[Type], depth: Int): List[Type] = { - def elimSub0(ts: List[Type]): List[Type] = ts match { - case List() => List() - case 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] = { @@ -7079,29 +4362,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: Int): 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 { @@ -7110,12 +4396,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 { @@ -7123,30 +4410,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 == 0) { + 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)) + else if (tparam.variance == variance.flip) glb(as, decr(depth)) else { val l = lub(as, decr(depth)) val g = glb(as, decr(depth)) if (l <:< g) l - else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we + 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 @@ -7157,22 +4442,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") } @@ -7189,7 +4474,7 @@ trait Types extends api.Types { self: SymbolTable => 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 } } @@ -7202,51 +4487,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 */ @@ -7259,7 +4499,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( @@ -7271,26 +4511,26 @@ trait Types extends api.Types { self: SymbolTable => } /** The current indentation string for traces */ - private var indent: String = "" + protected[internal] var indent: String = "" /** Perform operation `p` on arguments `tp1`, `arg2` and print trace of computation. */ protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { - Console.println(indent + tp1 + " " + op + " " + arg2 + "?" /* + "("+tp1.getClass+","+arg2.getClass+")"*/) + 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. */ @@ -7300,28 +4540,31 @@ 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 ) /** 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( @@ -7334,32 +4577,8 @@ 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 @@ -7369,11 +4588,54 @@ trait Types extends api.Types { self: SymbolTable => private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations.nonEmpty 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): Int = tp match { + case TypeRef(pre, sym, args) => + math.max(typeDepth(pre), typeDepth(args) + 1) + case RefinedType(parents, decls) => + math.max(typeDepth(parents), symTypeDepth(decls.toList) + 1) + case TypeBounds(lo, hi) => + math.max(typeDepth(lo), typeDepth(hi)) + case MethodType(paramtypes, result) => + typeDepth(result) + case NullaryMethodType(result) => + typeDepth(result) + case PolyType(tparams, result) => + math.max(typeDepth(result), symTypeDepth(tparams) + 1) + case ExistentialType(tparams, result) => + math.max(typeDepth(result), symTypeDepth(tparams) + 1) + case _ => + 1 + } + + def withUncheckedVariance(tp: Type): Type = + tp withAnnotation (AnnotationInfo marker uncheckedVarianceClass.tpe) + + //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]): Int = { + @tailrec def loop(tps: List[Type], acc: Int): Int = tps match { + case tp :: rest => loop(rest, math.max(acc, typeDepth(tp))) + case _ => acc + } + loop(tps, 0) + } + private[scala] def maxBaseTypeSeqDepth(tps: List[Type]): Int = { + @tailrec def loop(tps: List[Type], acc: Int): Int = tps match { + case tp :: rest => loop(rest, math.max(acc, tp.baseTypeSeqDepth)) + case _ => acc + } + loop(tps, 0) + } + @tailrec private def typesContain(tps: List[Type], sym: Symbol): Boolean = tps match { case tp :: rest => (tp contains sym) || typesContain(rest, sym) case _ => false @@ -7413,7 +4675,6 @@ trait Types extends api.Types { self: SymbolTable => 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") @@ -7435,7 +4696,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..3480161567 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Variance.scala @@ -0,0 +1,91 @@ +/* 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 (isBivariant) "+/-" + else 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..78df3c9617 --- /dev/null +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -0,0 +1,197 @@ +/* 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.hasLocalFlag + */ + @tailrec final def checkForEscape(sym: Symbol, site: Symbol) { + if (site == sym.owner || site == sym.owner.moduleClass || site.isPackage) () // 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 + && (sym.hasLocalFlag || 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. + * + * An alias which does not override anything is treated as Bivariant; + * this is OK because we always expand aliases for variance checking. + * However if it does override a type in a base class, we must assume Invariant + * because there may be references to the type parameter that are not checked. + */ + 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) Invariant + 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) { + log(s"verifying $sym (${sym.variance}${sym.locationString}) is $required at $base in ${base.owner}") + 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.hasLocalFlag + || sym.owner.isConstructor + || sym.owner.isCaseApplyOrUnapply + ) + tree match { + case defn: MemberDef if skip => + log(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 _ => + } + } + } + + /** 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 index 058ff61fbf..2c9f909629 100644 --- a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala +++ b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package internal package annotations diff --git a/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala b/src/reflect/scala/reflect/internal/pickling/ByteCodecs.scala index 367a3b8b19..eb266e8125 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 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/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 603fff4f1c..6cffdbc193 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 @@ -28,8 +29,8 @@ abstract class UnPickler { * 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) { @@ -159,9 +160,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 +187,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 = { @@ -325,7 +325,7 @@ abstract class UnPickler { */ protected def readType(forceProperType: Boolean = false): Type = { val tag = readByte() - val end = readNat() + readIndex + val end = readEnd() (tag: @switch) match { case NOtpe => NoType @@ -344,7 +344,7 @@ abstract class UnPickler { case TYPEREFtpe => val pre = readTypeRef() val sym = readSymbolRef() - var args = until(end, readTypeRef) + val args = until(end, readTypeRef) TypeRef(pre, sym, args) case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) @@ -431,7 +431,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 +450,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 { @@ -486,7 +486,7 @@ abstract class UnPickler { val tag = readByte() if (tag != SYMANNOT) errorBadSignature("symbol annotation expected ("+ tag +")") - val end = readNat() + readIndex + val end = readEnd() val target = readSymbolRef() target.addAnnotation(readAnnotationInfo(end)) } @@ -497,7 +497,7 @@ abstract class UnPickler { val tag = readByte() if (tag != ANNOTINFO) errorBadSignature("annotation expected (" + tag + ")") - val end = readNat() + readIndex + val end = readEnd() readAnnotationInfo(end) } @@ -506,7 +506,7 @@ abstract class UnPickler { val outerTag = readByte() if (outerTag != TREE) errorBadSignature("tree expected (" + outerTag + ")") - val end = readNat() + readIndex + val end = readEnd() val tag = readByte() val tpe = if (tag == EMPTYtree) NoType else readTypeRef() @@ -517,18 +517,18 @@ abstract class UnPickler { var mods: Modifiers = null var name: Name = null - /** Read a Symbol, Modifiers, and a Name */ + /* Read a Symbol, Modifiers, and a Name */ def setSymModsName() { symbol = readSymbolRef() mods = readModifiersRef() name = readNameRef() } - /** Read a Symbol and a Name */ + /* Read a Symbol and a Name */ def setSymName() { symbol = readSymbolRef() name = readNameRef() } - /** Read a Symbol */ + /* Read a Symbol */ def setSym() { symbol = readSymbolRef() } @@ -764,7 +764,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 +797,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 { @@ -843,7 +843,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) } @@ -853,7 +852,8 @@ abstract class UnPickler { private val p = phase override def complete(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - atPhase(p) (sym setInfo tp) + if (p ne null) + enteringPhase(p) (sym setInfo tp) if (currentRunId != definedAtRunId) sym.setInfo(adaptToNewRunMap(tp)) } @@ -871,7 +871,7 @@ abstract class UnPickler { super.complete(sym) var alias = at(j, readSymbol) if (alias.isOverloaded) - alias = atPhase(picklerPhase)((alias suchThat (alt => sym.tpe =:= sym.owner.thisType.memberType(alt)))) + alias = enteringPhase(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..e21e95903b 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. @@ -35,16 +36,22 @@ abstract class MutableSettings extends AbsSettings { def overrideObjects: BooleanSetting def printtypes: BooleanSetting def debug: BooleanSetting - def Ynotnull: BooleanSetting def explaintypes: BooleanSetting def verbose: BooleanSetting def uniqid: BooleanSetting def Yshowsymkinds: BooleanSetting + def Yposdebug: BooleanSetting + def Yrangepos: BooleanSetting def Xprintpos: BooleanSetting def Yrecursion: IntSetting def maxClassfileName: IntSetting def Xexperimental: BooleanSetting - def XoldPatmat: BooleanSetting def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting + def breakCycles: BooleanSetting +} +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/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala new file mode 100644 index 0000000000..1d3c6b0f23 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -0,0 +1,613 @@ +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" + + /** 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: 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)), 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 + } + } + + /** 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: Int): List[Type] = { + var lubListDepth = 0 + // 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 += 1 + + 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 fbounds = findRecursiveBounds(ts0) map (_._2) + val tcLubList = typeConstructorLubList(ts0) + def isRecursive(tp: Type) = tp.typeSymbol.typeParams exists fbounds.contains + + val ts1 = ts0 map { t => + if (isRecursive(t)) { + tcLubList map (t baseType _.typeSymbol) find (t => !isRecursive(t)) match { + case Some(tp) => logResult(s"Breaking recursion in lublist, substituting weaker type.\n Was: $t\n Now")(tp) + case _ => t + } + } + else t + } + val tails = tsBts map (_.tail) + mergePrefixAndArgs(elimSub(ts1, depth) map elimHigherOrderTypeParam, Covariant, depth) match { + case NoType => 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: 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) + } + + /** 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[(Int, List[Type]), Type] + private val glbResults = new mutable.HashMap[(Int, List[Type]), Type] + + /** Given a list of types, finds all the base classes they have in + * common, then returns a list of type constructors derived directly + * from the symbols (so any more specific type information is ignored.) + * The list is filtered such that every type constructor in the list + * expects the same number of type arguments, which is chosen based + * on the deepest class among the common baseclasses. + */ + def typeConstructorLubList(ts: List[Type]): List[Type] = { + val bcs = ts.flatMap(_.baseClasses).distinct sortWith (_ isLess _) + val tcons = bcs filter (clazz => ts forall (_.typeSymbol isSubClass clazz)) + + tcons map (_.typeConstructor) match { + case Nil => Nil + case t :: ts => t :: ts.filter(_.typeParams.size == t.typeParams.size) + } + } + + 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: Int): 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 < 0) 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 == 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) + || 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, 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 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 = 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() => 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: Int): 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: Int): 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 < 0) 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 == 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 => 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..63f17dff34 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -0,0 +1,581 @@ +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 = DefaultLogThreshhold + + private val pendingSubTypes = new mutable.HashSet[SubTypePair] + + class SubTypePair(val tp1: Type, val tp2: Type) { + override def hashCode = tp1.hashCode * 41 + tp2.hashCode + override def equals(other: Any) = (this eq other.asInstanceOf[AnyRef]) || (other match { + // suspend TypeVars in types compared by =:=, + // since we don't want to mutate them simply to check whether a subtype test is pending + // in addition to making subtyping "more correct" for type vars, + // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion) + // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold) + case stp: SubTypePair => + val tvars = List(tp1, stp.tp1, tp2, stp.tp2) flatMap (t => if (t.isGround) Nil else typeVarsInType(t)) + suspendingTypeVars(tvars)(tp1 =:= stp.tp1 && tp2 =:= stp.tp2) + case _ => + false + }) + override def toString = tp1+" <:<? "+tp2 + } + + 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) 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) + // } + + 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() + } + + // @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) => p1.info =:= subst(p2.info)) + && (res1 =:= subst(res2)) + ) + } + + 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): 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 + else + try { + pendingSubTypes += p + isSubType1(tp1, tp2, depth) + } finally { + pendingSubTypes -= p + } + } else { + isSubType1(tp1, tp2, depth) + } + } 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() + } + + /** 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: Int): 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) = sub2(p2.info) <:< sub1(p1.info) + + (tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2)) + } + } + + // @assume tp1.isHigherKinded || tp2.isHigherKinded + def isHKSubType(tp1: Type, tp2: Type, depth: Int): 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: Int): 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) || 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..123c296f95 --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala @@ -0,0 +1,283 @@ +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. + */ + lazy val undoLog = newUndoLog + + 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 an 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) + self.log("Clearing " + log.size + " entries from the undoLog.") + log = Nil + } 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() + } + } + + /** @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 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 + } + } + + /** 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): Boolean = + solve(tvars, tparams, variances, upper, AnyDepth) + + def solve(tvars: List[TypeVar], tparams: List[Symbol], + variances: List[Variance], upper: Boolean, depth: Int): 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) { + 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)) + } +} 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..bebc419c7c --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -0,0 +1,1163 @@ +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 = 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, _) => + 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, selfsym) => + val annots1 = mapOverAnnotations(annots) + val atp1 = this(atp) + if ((annots1 eq annots) && (atp1 eq atp)) tp + else if (annots1.isEmpty) atp1 + else AnnotatedType(annots1, atp1, selfsym) + /* + case ErrorType => tp + case WildcardType => tp + case NoType => tp + case NoPrefix => tp + case 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) + logResult(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.safeOwner == 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.safeOwner.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 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) = pre.widen match { + case _: TypeVar => false + case wide => (clazz == candidate) && (wide.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 { + 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 _ => + 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) + } + } + + 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 + } + } + + /** 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) { + 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.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) { + 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..16929cca0f --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala @@ -0,0 +1,30 @@ +package scala +package reflect +package internal +package tpe + +private[internal] trait TypeToStrings { + self: SymbolTable => + + /** 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) { + devWarning("Exceeded recursion depth attempting to print " + util.shortClassOfInstance(tpe)) + if (settings.debug) + (new Throwable).printStackTrace + + "..." + } + else + try { + tostringRecursions += 1 + tpe.safeToString + } finally { + tostringRecursions -= 1 + } +} diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 52d1657dc3..580ada8254 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 @@ -69,7 +74,7 @@ trait Erasure { // // 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? + if (cls.owner.isClass) cls.owner.tpe_* else pre // why not cls.isNestedClass? } def unboxDerivedValueClassMethod(clazz: Symbol): Symbol = @@ -101,13 +106,10 @@ 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 = @@ -122,15 +124,15 @@ trait Erasure { 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 apply(sym.info asSeenFrom (pre, sym.owner)) // alias type or abstract type case PolyType(tparams, restpe) => apply(restpe) case ExistentialType(tparams, restpe) => @@ -138,7 +140,7 @@ 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))) @@ -149,7 +151,7 @@ trait Erasure { 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 _ => @@ -214,9 +216,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 +224,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 +254,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 } @@ -311,7 +302,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 +324,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 +351,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/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..fa185db22f 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 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..e127d577e1 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 @@ -40,8 +41,6 @@ trait Collections { mforeach(xss)(x => if ((res eq null) && p(x)) res = Some(x)) if (res eq null) None else res } - final def mfilter[A](xss: List[List[A]])(p: A => Boolean) = - for (xs <- xss; x <- xs; if p(x)) yield x final def map2[A, B, C](xs1: List[A], xs2: List[B])(f: (A, B) => C): List[C] = { val lb = new ListBuffer[C] @@ -141,7 +140,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 +188,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 @@ -223,5 +210,4 @@ trait Collections { } } -object Collections extends Collections { } - +object Collections extends Collections 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..ddd0a64675 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -4,7 +4,8 @@ * */ -package scala.reflect.internal.util +package scala +package reflect.internal.util import scala.reflect.ClassTag import scala.reflect.internal.FatalError @@ -20,18 +21,12 @@ object Position { else if (posIn.isDefined) posIn.inUltimateSource(posIn.source) else posIn ) - def file = pos.source.file - def prefix = if (shortenFile) file.name else file.path + val prefix = if (shortenFile) pos.sourceName else pos.sourcePath 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" + case _ => "%s:%s: %s\n%s\n%s".format(prefix, pos.line, msg, pos.lineContent, pos.lineCarat) } } } @@ -129,7 +124,7 @@ abstract class Position extends scala.reflect.api.Position { self => def endOrPoint: Int = point @deprecated("use point instead", "2.9.0") - def offset: Option[Int] = if (isDefined) Some(point) else None + def offset: Option[Int] = if (isDefined) Some(point) else None // used by sbt /** The same position with a different start value (if a range) */ def withStart(off: Int): Position = this @@ -206,12 +201,39 @@ abstract class Position extends scala.reflect.api.Position { self => def column: Int = throw new UnsupportedOperationException("Position.column") + /** A line with a ^ padded with the right number of spaces. + */ + def lineCarat: String = " " * (column - 1) + "^" + + /** The line of code and the corresponding carat pointing line, trimmed + * to the maximum specified width, with the trimmed versions oriented + * around the point to give maximum context. + */ + def lineWithCarat(maxWidth: Int): (String, String) = { + val radius = maxWidth / 2 + var start = scala.math.max(column - radius, 0) + var result = lineContent drop start take maxWidth + + if (result.length < maxWidth) { + result = lineContent takeRight maxWidth + start = lineContent.length - result.length + } + + (result, lineCarat drop start take maxWidth) + } + /** 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" + /** The source code corresponding to the range, if this is a range position. + * Otherwise the empty string. + */ + def sourceCode = "" + def sourceName = "<none>" + def sourcePath = "<none>" + def lineContent = "<none>" + def lengthInChars = 0 + def lengthInLines = 0 /** Map this position to a position in an original source * file. If the SourceFile is a normal SourceFile, simply @@ -240,7 +262,10 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e 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 line = source.offsetToLine(point) + 1 + override def sourceName = source.file.name + override def sourcePath = source.file.path + override def lineContent = source.lineToString(line - 1) override def column: Int = { var idx = source.lineToOffset(source.offsetToLine(point)) @@ -266,46 +291,3 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e } override def show = "["+point+"]" } - -/** new for position ranges */ -class RangePosition(source: SourceFile, override val start: Int, point: Int, override val end: Int) -extends OffsetPosition(source, point) { - if (start > end) 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 - } - - override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" - override def show = "["+start+":"+end+"]" - private var focusCache: Position = NoPosition -} - -class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int) extends RangePosition(source, start, point, end) { - override def isOpaqueRange: Boolean = false - override def isTransparent = true - override def makeTransparent = this - override def show = "<"+start+":"+end+">" -} diff --git a/src/reflect/scala/reflect/internal/util/RangePosition.scala b/src/reflect/scala/reflect/internal/util/RangePosition.scala new file mode 100644 index 0000000000..0d09a53cd9 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/RangePosition.scala @@ -0,0 +1,50 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala +package reflect.internal.util + +/** 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) scala.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 + } + + override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" + override def show = "["+start+":"+end+"]" + private var focusCache: Position = NoPosition +} + +class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int) extends RangePosition(source, start, point, end) { + override def isOpaqueRange: Boolean = false + override def isTransparent = true + override def makeTransparent = this + override def show = "<"+start+":"+end+">" +} 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..a7fd787dfc --- /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..75dcfaa59b 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,8 +19,6 @@ 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 diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index bc2d0ee4db..ea4c9a9b68 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 @@ -24,7 +25,6 @@ abstract class SourceFile { assert(offset < length, file + ": " + offset + " >= " + length) new OffsetPosition(this, offset) } - def position(line: Int, column: Int) : Position = new OffsetPosition(this, lineToOffset(line) + column) def offsetToLine(offset: Int): Int def lineToOffset(index : Int): Int @@ -37,9 +37,6 @@ abstract class SourceFile { 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 "" @@ -81,7 +78,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) @@ -91,7 +87,6 @@ object ScriptSourceFile { stripped } } -import ScriptSourceFile._ class ScriptSourceFile(underlying: BatchSourceFile, content: Array[Char], override val start: Int) extends BatchSourceFile(underlying.file, content) { override def isSelfContained = false diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index cbd27b0d65..b583137059 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. @@ -155,7 +156,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 { @@ -257,7 +258,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..4d98a344d8 100644 --- a/src/reflect/scala/reflect/internal/util/StringOps.scala +++ b/src/reflect/scala/reflect/internal/util/StringOps.scala @@ -6,8 +6,8 @@ ** |/ ** \* */ - -package scala.reflect.internal.util +package scala +package reflect.internal.util /** This object provides utility methods to extract elements * from Strings. @@ -16,22 +16,14 @@ 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 trimTrailingSpace(s: String) = { - if (s.length == 0 || !s.charAt(s.length - 1).isWhitespace) s - else { - var idx = s.length - 1 - while (idx >= 0 && s.charAt(idx).isWhitespace) - idx -= 1 - - s.substring(0, idx + 1) + def oempty(xs: String*) = xs filterNot (x => x == null || x == "") + def ojoin(xs: String*): String = oempty(xs: _*) mkString " " + def longestCommonPrefix(xs: List[String]): String = { + if (xs.isEmpty || xs.contains("")) "" + else xs.head.head match { + case ch => + if (xs.tail forall (_.head == ch)) "" + ch + longestCommonPrefix(xs map (_.tail)) + else "" } } @@ -49,14 +41,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 +49,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 +61,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..1626da2c93 100644 --- a/src/reflect/scala/reflect/internal/util/TableDef.scala +++ b/src/reflect/scala/reflect/internal/util/TableDef.scala @@ -1,4 +1,5 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util import TableDef._ import scala.language.postfixOps @@ -19,8 +20,8 @@ class TableDef[T](_cols: Column[T]*) { * if none is specified, a space is used. */ def ~(next: Column[T]) = retThis(cols :+= next) - def >>(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, false) - def <<(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, true) + def >>(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, left = false) + def <<(pair: (String, T => Any)) = this ~ Column(pair._1, pair._2, left = true) def >+(sep: String) = retThis(separators += ((cols.size - 1, sep))) /** Below this point should all be considered private/internal. @@ -67,12 +68,6 @@ class TableDef[T](_cols: Column[T]*) { 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..97cc19952c 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,38 +41,6 @@ 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 def dashes(s: Any): String = ("" + s) map (_ => '-') @@ -119,7 +84,7 @@ 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 = { @@ -129,7 +94,7 @@ trait TraceSymbolActivity { } ph } - private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) + private def runBeforeErasure[T](body: => T): T = enteringPhase(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..c7a35d4637 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/TriState.scala @@ -0,0 +1,26 @@ +package scala +package reflect +package internal +package util + +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 9882aad5e5..9b792a3f43 100644 --- a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -1,61 +1,430 @@ -package scala.reflect.internal.util +package scala +package reflect.internal.util -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.collection.mutable.Builder -import scala.collection.mutable.SetBuilder +import java.lang.ref.{WeakReference, ReferenceQueue} +import scala.annotation.tailrec import scala.collection.generic.Clearable -import scala.runtime.AbstractFunction1 +import scala.collection.mutable.{Set => mSet} -/** A bare-bones implementation of a mutable `Set` that uses weak references - * to hold the elements. +/** + * A HashSet where the elements are stored weakly. Elements in this set are elligible for GC if no other + * hard references are associated with them. Its primary use case is as a canonical reference + * identity holder (aka "hash-consing") via findEntryOrUpdate * - * This implementation offers only add/remove/test operations, - * therefore it does not fulfill the contract of Scala collection sets. + * This Set implementation cannot hold null. Any attempt to put a null in it will result in a NullPointerException + * + * 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. */ -class WeakHashSet[T <: AnyRef] extends AbstractFunction1[T, Boolean] with Clearable { - private val underlying = mutable.HashSet[WeakReferenceWithEquals[T]]() +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) + + type This = WeakHashSet[A] + + /** + * queue of Entries that hold elements scheduled for GC + * the removeStaleEntries() method works through the queue to remeove + * stale entries from the table + */ + private[this] val queue = new ReferenceQueue[A] + + /** + * the number of elements in this set + */ + private[this] var count = 0 + + /** + * from a specified initial capacity compute the capacity we'll use as being the next + * power of two equal to or greater than the specified initial capacity + */ + private def computeCapacity = { + if (initialCapacity < 0) throw new IllegalArgumentException("initial capacity cannot be less than 0"); + var candidate = 1 + while (candidate < initialCapacity) { + candidate *= 2 + } + candidate + } + + /** + * the underlying table of entries which is an array of Entry linked lists + */ + private[this] var table = new Array[Entry[A]](computeCapacity) + + /** + * the limit at which we'll increase the size of the hash table + */ + var threshhold = computeThreshHold + + private[this] def computeThreshHold: Int = (table.size * loadFactor).ceil.toInt - /** Add the given element to this set. */ - def +=(elem: T): this.type = { - underlying += new WeakReferenceWithEquals(elem) - this + /** + * 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 + // same algorithm as java.util.HashMap + var h = hash + h ^= h >>> 20 ^ h >>> 12 + h ^= h >>> 7 ^ h >>> 4 + + // this is finding h % table.length, but takes advantage of the + // fact that table length is a power of 2, + // if you don't do bit flipping in your head, if table.length + // is binary 100000.. (with n 0s) then table.length - 1 + // is 1111.. with n 1's. + // In other words this masks on the last n bits in the hash + h & (table.length - 1) } - /** Remove the given element from this set. */ - def -=(elem: T): this.type = { - underlying -= new WeakReferenceWithEquals(elem) - this + /** + * remove a single entry from a linked list in a given bucket + */ + private[this] def remove(bucket: Int, prevEntry: Entry[A], entry: Entry[A]) { + prevEntry match { + case null => table(bucket) = entry.tail + case _ => prevEntry.tail = entry.tail + } + count -= 1 } - /** Does the given element belong to this set? */ - def contains(elem: T): Boolean = - underlying.contains(new WeakReferenceWithEquals(elem)) + /** + * remove entries associated with elements that have been gc'ed + */ + private[this] def removeStaleEntries() { + def poll(): Entry[A] = queue.poll().asInstanceOf[Entry[A]] - /** Does the given element belong to this set? */ - def apply(elem: T): Boolean = contains(elem) + @tailrec + def queueLoop { + val stale = poll() + if (stale != null) { + val bucket = bucketFor(stale.hash) - /** Return the number of elements in this set, including reclaimed elements. */ - def size = underlying.size + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = if (stale eq entry) remove(bucket, prevEntry, entry) + else if (entry != null) linkedListLoop(entry, entry.tail) - /** Remove all elements in this set. */ - def clear() = underlying.clear() -} + linkedListLoop(null, table(bucket)) -/** A WeakReference implementation that implements equals and hashCode by - * delegating to the referent. - */ -class WeakReferenceWithEquals[T <: AnyRef](ref: T) { - def get(): T = underlying.get() + queueLoop + } + } + + queueLoop + } + + /** + * Double the size of the internal table + */ + private[this] def resize() { + val oldTable = table + table = new Array[Entry[A]](oldTable.size * 2) + threshhold = computeThreshHold + + @tailrec + def tableLoop(oldBucket: Int): Unit = if (oldBucket < oldTable.size) { + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => () + case _ => { + val bucket = bucketFor(entry.hash) + val oldNext = entry.tail + entry.tail = table(bucket) + table(bucket) = entry + linkedListLoop(oldNext) + } + } + linkedListLoop(oldTable(oldBucket)) + + tableLoop(oldBucket + 1) + } + tableLoop(0) + } + + // from scala.reflect.internal.Set, find an element or null if it isn't contained + override def findEntry(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => null.asInstanceOf[A] + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(table(bucket)) + } + } + // add an element to this set unless it's already in there and return the element + def findEntryOrUpdate(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() = { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + elem + } + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => add() + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(oldHead) + } + } + + // add an element to this set unless it's already in there and return this set + override def +(elem: A): this.type = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + } + + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => add() + case _ if (elem == entry.get) => () + case _ => linkedListLoop(entry.tail) + } + + linkedListLoop(oldHead) + this + } + } + + def +=(elem: A) = this + elem + + // from scala.reflect.interanl.Set + 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 { + case null => this + case _ => { + removeStaleEntries() + val bucket = bucketFor(elem.hashCode) + + + + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = entry match { + case null => () + case _ if (elem == entry.get) => remove(bucket, prevEntry, entry) + case _ => linkedListLoop(entry, entry.tail) + } - override val hashCode = ref.hashCode + linkedListLoop(null, table(bucket)) + this + } + } + + def -=(elem: A) = this - elem + + // empty this set + override def clear(): Unit = { + table = new Array[Entry[A]](table.size) + threshhold = computeThreshHold + count = 0 + + // drain the queue - doesn't do anything because we're throwing away all the values anyway + @tailrec def queueLoop(): Unit = if (queue.poll() != null) queueLoop() + queueLoop() + } + + // true if this set is empty + override def empty: This = new WeakHashSet[A](initialCapacity, loadFactor) + + // the number of elements in this set + override def size: Int = { + removeStaleEntries() + count + } + + override def apply(x: A): Boolean = this contains x + + override def foreach[U](f: A => U): Unit = iterator foreach f + + override def toList(): List[A] = iterator.toList + + // Iterator over all the elements in this set in no particular order + override def iterator: Iterator[A] = { + removeStaleEntries() + + new Iterator[A] { + + /** + * the bucket currently being examined. Initially it's set past the last bucket and will be decremented + */ + private[this] var currentBucket: Int = table.size + + /** + * the entry that was last examined + */ + 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] + + @tailrec + def hasNext: Boolean = { + while (entry == null && currentBucket > 0) { + currentBucket -= 1 + entry = table(currentBucket) + } + + if (entry == null) false + else { + lookaheadelement = entry.get + if (lookaheadelement == null) { + // element null means the weakref has been cleared since we last did a removeStaleEntries(), move to the next entry + entry = entry.tail + hasNext + } else { + true + } + } + } - override def equals(other: Any): Boolean = other match { - case wf: WeakReferenceWithEquals[_] => - underlying.get() == wf.get() - case _ => - false + def next(): A = if (lookaheadelement == null) + throw new IndexOutOfBoundsException("next on an empty iterator") + else { + val result = lookaheadelement + lookaheadelement = null.asInstanceOf[A] + entry = entry.tail + result + } + } } - private val underlying = new java.lang.ref.WeakReference(ref) + /** + * Diagnostic information about the internals of this set. Not normally + * needed by ordinary code, but may be useful for diagnosing performance problems + */ + private[util] class Diagnostics { + /** + * Verify that the internal structure of this hash set is fully consistent. + * Throws an assertion error on any problem. In order for it to be reliable + * the entries must be stable. If any are garbage collected during validation + * then an assertion may inappropriately fire. + */ + def fullyValidate { + var computedCount = 0 + var bucket = 0 + while (bucket < table.size) { + var entry = table(bucket) + while (entry != null) { + assert(entry.get != null, s"$entry had a null value indicated that gc activity was happening during diagnostic validation or that a null value was inserted") + computedCount += 1 + val cachedHash = entry.hash + val realHash = entry.get.hashCode + assert(cachedHash == realHash, s"for $entry cached hash was $cachedHash but should have been $realHash") + val computedBucket = bucketFor(realHash) + assert(computedBucket == bucket, s"for $entry the computed bucket was $computedBucket but should have been $bucket") + + entry = entry.tail + } + + bucket += 1 + } + + assert(computedCount == count, s"The computed count was $computedCount but should have been $count") + } + + /** + * Produces a diagnostic dump of the table that underlies this hash set. + */ + def dump = table.deep + + /** + * Number of buckets that hold collisions. Useful for diagnosing performance issues. + */ + def collisionBucketsCount: Int = + (table filter (entry => entry != null && entry.tail != null)).size + + /** + * Number of buckets that are occupied in this hash table. + */ + def fullBucketsCount: Int = + (table filter (entry => entry != null)).size + + /** + * 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 } + +/** + * Companion object for WeakHashSet + */ +object WeakHashSet { + /** + * A single entry in a WeakHashSet. It's a WeakReference plus a cached hash code and + * a link to the next Entry in the same bucket + */ + private class Entry[A](element: A, val hash:Int, var tail: Entry[A], queue: ReferenceQueue[A]) extends WeakReference[A](element, queue) + + val defaultInitialCapacity = 16 + 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..49164d366c 100644 --- a/src/reflect/scala/reflect/internal/util/package.scala +++ b/src/reflect/scala/reflect/internal/util/package.scala @@ -1,7 +1,37 @@ -package scala.reflect +package scala +package reflect package internal +import scala.language.existentials // SI-6541 + package object util { + import StringOps.longestCommonPrefix + + // 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..a4e869ed41 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 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..eabf1dcbab 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 @@ -232,3 +232,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..9e05f343e6 100644 --- a/src/reflect/scala/reflect/macros/Aliases.scala +++ b/src/reflect/scala/reflect/macros/Aliases.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index 007df3b6e2..c1ab269268 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 /** @@ -56,8 +57,6 @@ abstract class Attachments { self => // 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) diff --git a/src/reflect/scala/reflect/macros/Context.scala b/src/reflect/scala/reflect/macros/Context.scala index aa1c1db227..434b7c1b9c 100644 --- a/src/reflect/scala/reflect/macros/Context.scala +++ b/src/reflect/scala/reflect/macros/Context.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros // todo. introduce context hierarchy @@ -36,7 +37,8 @@ trait Context extends Aliases with Typers with Parsers with Evals - with ExprUtils { + with ExprUtils + with Synthetics { /** The compile-time universe. */ val universe: Universe @@ -51,7 +53,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/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala index a4ad71c348..d6ba5f39cd 100644 --- a/src/reflect/scala/reflect/macros/Enclosures.scala +++ b/src/reflect/scala/reflect/macros/Enclosures.scala @@ -1,6 +1,9 @@ -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> * @@ -15,19 +18,32 @@ trait Enclosures { /** 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 + + /** The semantic role that `macroApplication` plays in the code. + */ + type MacroRole + + /** The role that represents an application of a term macro, + * e.g. `M(2)(3)` in `val x = M(2)(3)` or `M(a, b)` in `x match { case x @ M(a, b) => }`. + */ + def APPLY_ROLE: MacroRole + + /** The semantic role that `macroApplication` plays in the code. + */ + def macroRole: MacroRole /** 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] + 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, @@ -39,28 +55,56 @@ trait Enclosures { * 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 enclosingImplicits: List[ImplicitCandidate] /** 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. */ - val enclosingMethod: Tree + @deprecated("Use enclosingDef instead, but be wary of changes in semantics", "2.10.1") + def enclosingMethod: Tree /** Tree that corresponds to the enclosing class, or EmptyTree if not applicable. */ - val enclosingClass: Tree + @deprecated("Use enclosingImpl instead, but be wary of changes in semantics", "2.10.1") + def enclosingClass: Tree + + /** Tree that corresponds to the enclosing DefDef tree. + * Throws `EnclosureException` if there's no such enclosing tree. + */ + def enclosingDef: universe.DefDef + + /** Tree that corresponds to the enclosing Template tree. + * Throws `EnclosureException` if there's no such enclosing tree. + */ + 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. + */ + def enclosingImpl: universe.ImplDef + + /** Tree that corresponds to the enclosing PackageDef tree. + * Throws `EnclosureException` if there's no such enclosing tree. + */ + def enclosingPackage: universe.PackageDef /** Compilation unit that contains this macro application. */ - val enclosingUnit: CompilationUnit + def enclosingUnit: CompilationUnit /** Compilation run that contains this macro application. */ - val enclosingRun: Run -}
\ No newline at end of file + def enclosingRun: Run + + /** Indicates than one of the enclosure methods failed to find a tree + * of required type among enclosing trees. + */ + 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..70b2ab58d4 100644 --- a/src/reflect/scala/reflect/macros/Evals.scala +++ b/src/reflect/scala/reflect/macros/Evals.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -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..af11bd6efc 100644 --- a/src/reflect/scala/reflect/macros/ExprUtils.scala +++ b/src/reflect/scala/reflect/macros/ExprUtils.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** diff --git a/src/reflect/scala/reflect/macros/FrontEnds.scala b/src/reflect/scala/reflect/macros/FrontEnds.scala index 67b24088b5..6abd8c335b 100644 --- a/src/reflect/scala/reflect/macros/FrontEnds.scala +++ b/src/reflect/scala/reflect/macros/FrontEnds.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -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..eb63fb7b7f 100644 --- a/src/reflect/scala/reflect/macros/Infrastructure.scala +++ b/src/reflect/scala/reflect/macros/Infrastructure.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -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/Macro.scala b/src/reflect/scala/reflect/macros/Macro.scala new file mode 100644 index 0000000000..44bedf483d --- /dev/null +++ b/src/reflect/scala/reflect/macros/Macro.scala @@ -0,0 +1,39 @@ +package scala.reflect +package macros + +/** + * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> + * + * Traditionally macro implementations are defined as methods, + * but this trait provides an alternative way of encoding macro impls as + * bundles, traits which extend `scala.reflect.macros.Macro`. + * + * Instead of: + * + * def impl[T: c.WeakTypeTag](c: Context)(x: c.Expr[Int]) = ... + * + * One can write: + * + * trait Impl extends Macro { + * def apply[T: c.WeakTypeTag](x: c.Expr[Int]) = ... + * } + * + * Without changing anything else at all. + * + * This language feature is useful in itself in cases when macro implementations + * are complex and need to be modularized. State of the art technique of addressing this need is quite heavyweight: + * http://docs.scala-lang.org/overviews/macros/overview.html#writing_bigger_macros. + * + * However utility of this approach to writing macros isn't limited to just convenience. + * When a macro implementation becomes not just a function, but a full-fledged module, + * it can define callbacks that will be called by the compiler upon interesting events. + * In subsequent commits I will add support for programmable type inference + */ +trait Macro { + /** The context to be used by the macro implementation. + * + * Vanilla macro implementations have to carry it in their signatures, however when a macro is a full-fledged module, + * it can define the context next to the implementation, makes implementation signature more lightweight. + */ + val c: Context +} diff --git a/src/reflect/scala/reflect/macros/Names.scala b/src/reflect/scala/reflect/macros/Names.scala index 8bbaa5f848..8773175561 100644 --- a/src/reflect/scala/reflect/macros/Names.scala +++ b/src/reflect/scala/reflect/macros/Names.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -11,13 +12,27 @@ trait Names { self: Context => /** Creates a unique string. */ + @deprecated("Use freshName instead", "2.11.0") def fresh(): String /** Creates a unique string having a given prefix. */ + @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. */ + @deprecated("Use freshName instead", "2.11.0") def fresh[NameType <: Name](name: NameType): NameType + + /** Creates a unique string. */ + def freshName(): String + + /** Creates a unique string having a given prefix. */ + def freshName(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. + */ + 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..3b25309614 100644 --- a/src/reflect/scala/reflect/macros/Parsers.scala +++ b/src/reflect/scala/reflect/macros/Parsers.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -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..6ebd2db730 100644 --- a/src/reflect/scala/reflect/macros/Reifiers.scala +++ b/src/reflect/scala/reflect/macros/Reifiers.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -11,16 +12,16 @@ trait Reifiers { self: 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.treeBuild.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/Synthetics.scala b/src/reflect/scala/reflect/macros/Synthetics.scala new file mode 100644 index 0000000000..5e422ee89f --- /dev/null +++ b/src/reflect/scala/reflect/macros/Synthetics.scala @@ -0,0 +1,107 @@ +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 + * exposes functions to introduce synthetic definitions. + * + * @define TOPLEVEL_TREE Top-level tree is a tree that represents a non-inner class or object in one of the currently compiled source files. + * Note that top-level isn't equivalent to [[scala.reflect.api.Symbols#SymbolApi.isStatic]], + * because static also embraces definitions nested in static objects + * + * @define INTRODUCE_TOP_LEVEL Allowed definitions include classes (represented by `ClassDef` trees), traits (represented + * by `ClassDef` trees having the `TRAIT` flag set in `mods`) and objects (represented by `ModuleDef` trees). + * + * The definitions are put into the package with a prototype provided in `packagePrototype`. + * Supported prototypes are (see [[PackageSpec]] for more details): + * * Strings and names representing a fully-qualified name of the package + * * Trees that can work as package ids + * * Package or package class symbols + * + * Typical value for a package prototype is a fully-qualified name in a string. + * For example, to generate a class available at `foo.bar.Test`, call this method as follows: + * + * introduceTopLevel("foo.bar", ClassDef(<mods>, TypeName("Test"), <tparams>, <template>)) + * + * It is possible to add definitions to the empty package by using `nme.EMPTY_PACKAGE_NAME.toString`, but + * that's not recommended, since such definitions cannot be seen from outside the empty package. + * + * Only the multi-parameter overload of this method can be used to introduce companions. + * If companions are introduced by two different calls, then they will be put into different virtual files, and `scalac` + * will show an error about companions being defined in different files. By the way, this also means that there's currently no way + * to define a companion for an existing class or module + */ +trait Synthetics { + self: Context => + + import universe._ + + /** Looks up a top-level definition tree with a given fully-qualified name + * (term name for modules, type name for classes). $TOPLEVEL_TREE. + * If such a tree does not exist, returns `EmptyTree`. + */ + def topLevelDef(name: Name): Tree + + /** Returns a reference to a top-level definition tree with a given fully-qualified name + * (term name for modules, type name for classes). $TOPLEVEL_TREE. + * If such a tree does not exist, returns `EmptyTree`. + */ + def topLevelRef(name: Name): Tree + + /** Adds a top-level definition to the compiler's symbol table. $INTRODUCE_TOP_LEVEL. + * + * Returns a fully-qualified reference to the introduced definition. + */ + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definition: ImplDef): RefTree + + /** Adds a list of top-level definitions to the compiler's symbol table. $INTRODUCE_TOP_LEVEL. + * + * Returns a list of fully-qualified references to the introduced definitions. + */ + def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: ImplDef*): List[RefTree] + + /** A factory which can create a package def from a prototype and a list of declarations. + */ + trait PackageSpec[T] { def mkPackageDef(prototype: T, stats: List[Tree]): PackageDef } + + /** Hosts supported package specs. + */ + object PackageSpec { + /** Package def can be created from a fully-qualified name and a list of definitions. + * The name is converted into an Ident or a chain of Selects. + */ + implicit val stringIsPackageSpec = new PackageSpec[String] { + def mkPackageDef(prototype: String, stats: List[Tree]): PackageDef = self.mkPackageDef(prototype, stats) + } + + /** Package def can be created from a fully-qualified term name and a list of definitions. + * The name is converted into an Ident or a chain of Selects. + */ + implicit val termNameIsPackageSpec = new PackageSpec[TermName] { + def mkPackageDef(prototype: TermName, stats: List[Tree]): PackageDef = self.mkPackageDef(prototype, stats) + } + + /** Package def can be created from a package id tree and a list of definitions. + * If the tree is not a valid package id, i.e. is not a term-name ident or a chain of term-name selects, + * then the produced PackageDef will fail compilation at some point in the future. + */ + implicit val refTreeIsPackageSpec = new PackageSpec[RefTree] { + def mkPackageDef(prototype: RefTree, stats: List[Tree]): PackageDef = self.mkPackageDef(prototype, stats) + } + + /** Package def can be created from a package/package class symbol and a list of definitions. + * If the provided symbol is not a package symbol or a package class symbol, package construction will throw an exception. + */ + implicit val SymbolIsPackageSpec = new PackageSpec[Symbol] { + def mkPackageDef(prototype: Symbol, stats: List[Tree]): PackageDef = self.mkPackageDef(prototype, stats) + } + } + + protected def mkPackageDef(name: String, stats: List[Tree]): PackageDef + protected def mkPackageDef(name: TermName, stats: List[Tree]): PackageDef + protected def mkPackageDef(tree: RefTree, stats: List[Tree]): PackageDef + protected def mkPackageDef(sym: Symbol, stats: List[Tree]): PackageDef +} diff --git a/src/reflect/scala/reflect/macros/TreeBuilder.scala b/src/reflect/scala/reflect/macros/TreeBuilder.scala index 204dc40858..427b4f70d1 100644 --- a/src/reflect/scala/reflect/macros/TreeBuilder.scala +++ b/src/reflect/scala/reflect/macros/TreeBuilder.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -11,7 +12,6 @@ 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 @@ -28,19 +28,25 @@ abstract class TreeBuilder { 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 + def mkAttributedRef(pre: Type, sym: Symbol): RefTree /** Builds a typed reference to given symbol. */ - def mkAttributedRef(sym: Symbol): Tree + def mkAttributedRef(sym: Symbol): RefTree + + /** 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): Tree + def mkAttributedThis(sym: Symbol): This /** Builds a typed Ident with an underlying symbol. */ - def mkAttributedIdent(sym: Symbol): Tree + def mkAttributedIdent(sym: Symbol): RefTree /** Builds a typed Select with an underlying symbol. */ - def mkAttributedSelect(qual: Tree, sym: Symbol): Tree + 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. @@ -67,6 +73,6 @@ abstract class TreeBuilder { def mkNullaryCall(method: Symbol, targs: List[Type]): Tree - /** A tree that refers to the runtime reflexive universe, ``scala.reflect.runtime.universe''. */ + /** 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..d7aec9b3ef 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -10,8 +11,6 @@ package macros trait Typers { self: Context => - import universe._ - /** Contexts that represent macros in-flight, including the current one. Very much like a stack trace, but for macros only. * Can be useful for interoperating with other macros and for imposing compiler-friendly limits on macro expansion. * @@ -28,13 +27,22 @@ trait Typers { * 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[(Type, Tree)] + def openImplicits: List[ImplicitCandidate] /** Typechecks the provided tree against the expected type `pt` in the macro callsite context. * @@ -49,7 +57,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, 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. @@ -92,4 +100,4 @@ trait Typers { /** 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..d1d90f53c9 100644 --- a/src/reflect/scala/reflect/macros/Universe.scala +++ b/src/reflect/scala/reflect/macros/Universe.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package macros /** @@ -114,7 +115,7 @@ abstract class Universe extends scala.reflect.api.Universe { def setPos(newpos: Position): Tree /** Sets the `tpe` of the tree. Returns `Unit`. */ - def tpe_=(t: Type): Unit + @deprecated("Use setType", "2.11.0") def tpe_=(t: Type): Unit /** Sets the `tpe` of the tree. Returns the tree itself. */ def setType(tp: Type): Tree @@ -238,4 +239,4 @@ abstract class Universe extends scala.reflect.api.Universe { /** The AST that corresponds to this compilation unit. */ def body: Tree } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/macros/package.scala b/src/reflect/scala/reflect/macros/package.scala index 21d189bb25..2e2e8e79f8 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> diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 55f08f0586..16405a88b4 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,23 +7,20 @@ 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 internal.Flags._ -//import scala.tools.nsc.util.ScalaClassLoader -//import scala.tools.nsc.util.ScalaClassLoader._ import ReflectionUtils.{staticSingletonInstance, innerSingletonInstance} 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 => @@ -63,7 +61,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni /** 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 => @@ -91,12 +89,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // ----------- 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 +102,35 @@ 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 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. @@ -214,7 +208,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 @@ -240,16 +233,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 { @@ -276,20 +265,14 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol) extends FieldMirror { - lazy val jfield = { - val jfield = fieldToJava(symbol) - if (!jfield.isAccessible) jfield.setAccessible(true) - jfield - } - def get = jfield.get(receiver) + lazy val jfield = ensureAccessible(fieldToJava(symbol)) + 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) + def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol) override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)" } @@ -320,13 +303,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // 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 == Predef_classOf || meth.isMacro) return true bytecodelessMethodOwners(meth.owner) && !bytecodefulObjectMethods(meth) } @@ -337,19 +320,23 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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 abstract class JavaMethodMirror(val symbol: MethodSymbol) - extends MethodMirror { - lazy val jmeth = { - val jmeth = methodToJava(symbol) - if (!jmeth.isAccessible) jmeth.setAccessible(true) - jmeth + 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 = ensureAccessible(methodToJava(symbol)) + def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { - val result = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + val result = jinvokeraw(jmeth, receiver, args) if (jmeth.getReturnType == java.lang.Void.TYPE) () else result } @@ -359,11 +346,43 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol) def apply(args: Any*): Any = jinvoke(jmeth, receiver, args) } + private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver) + } + + private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + } + + private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol) + extends JavaVanillaMethodMirror(receiver, symbol) { + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol) + override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + } + private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaByNameMethodMirror(newReceiver, symbol) 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) @@ -372,6 +391,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaBytecodelessMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { + def bind(newReceiver: Any) = new JavaBytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) 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 @@ -383,14 +403,14 @@ 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"${showMethodSig(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) @@ -420,7 +440,7 @@ 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") } } @@ -428,15 +448,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol) extends MethodMirror { + def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol) override val receiver = outer - lazy val jconstr = { - val jconstr = constructorToJava(symbol) - if (!jconstr.isAccessible) jconstr.setAccessible(true) - jconstr - } + lazy val jconstr = ensureAccessible(constructorToJava(symbol)) 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") + abort("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]] @@ -469,11 +486,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 +514,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 = { @@ -530,7 +544,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 @@ -574,7 +588,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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 { @@ -618,13 +632,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 @@ -644,14 +667,14 @@ 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) + val flags = jclazz.scalaFlags clazz setFlag (flags | JAVA) if (module != NoSymbol) { module setFlag (flags & PRIVATE | JAVA) module.moduleClass setFlag (flags & PRIVATE | JAVA) } - relatedSymbols foreach (importPrivateWithinFromJavaFlags(_, jclazz.getModifiers)) + propagatePackageBoundary(jclazz, relatedSymbols: _*) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? @@ -673,7 +696,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val parents = try { parentsLevel += 1 val jsuperclazz = jclazz.getGenericSuperclass - val superclazz = if (jsuperclazz == null) AnyClass.tpe else typeToScala(jsuperclazz) + val superclazz = if (jsuperclazz == null) AnyTpe else typeToScala(jsuperclazz) superclazz :: (jclazz.getGenericInterfaces.toList map typeToScala) } finally { parentsLevel -= 1 @@ -683,29 +706,21 @@ 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) = - (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym + def enter(sym: Symbol, mods: JavaAccFlags) = + ( if (mods.isStatic) module.moduleClass else clazz ).info.decls enter sym - for (jinner <- jclazz.getDeclaredClasses) { + for (jinner <- jclazz.getDeclaredClasses) jclassAsScala(jinner) // inner class is entered as a side-effect // 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 + 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)) + } if (parentsLevel == 0) { - while (!pendingLoadActions.isEmpty) { + while (pendingLoadActions.nonEmpty) { val item = pendingLoadActions.head pendingLoadActions = pendingLoadActions.tail item() @@ -724,8 +739,8 @@ 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) = - if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz + private def followStatic(clazz: Symbol, mods: JavaAccFlags) = + if (mods.isStatic) clazz.companionModule.moduleClass else clazz /** Methods which need to be treated with care * because they either are getSimpleName or call getSimpleName: @@ -761,7 +776,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (jclazz.isMemberClass) { val jEnclosingClass = jclazz.getEnclosingClass val sEnclosingClass = classToScala(jEnclosingClass) - followStatic(sEnclosingClass, jclazz.getModifiers) + followStatic(sEnclosingClass, jclazz.javaFlags) } else if (jclazz.isLocalClass0) { val jEnclosingMethod = jclazz.getEnclosingMethod if (jEnclosingMethod != null) { @@ -789,7 +804,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * 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) } /** @@ -805,9 +820,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 { @@ -829,7 +845,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 } @@ -843,26 +859,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) { @@ -965,8 +967,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 } @@ -983,7 +985,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 } } @@ -995,6 +997,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 @@ -1047,18 +1053,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 } /** @@ -1072,10 +1075,11 @@ 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) field } @@ -1095,16 +1099,19 @@ 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)) - meth + + if (jmeth.javaFlags.isVarargs) + meth modifyInfo arrayToRepeated + else + meth } /** @@ -1119,26 +1126,19 @@ 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) 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 @@ -1154,16 +1154,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 @@ -1183,11 +1185,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) @@ -1200,7 +1202,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 => @@ -1228,11 +1230,11 @@ 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 _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") + case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") } } @@ -1272,14 +1274,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni 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 => + syntheticCoreClasses get ((owner.fullName, name)) foreach { 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 } } info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index 1b69ca4e89..06a7db6289 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -1,8 +1,7 @@ -package scala.reflect +package scala +package reflect package runtime -import internal.{SomePhase, NoPhase, Phase, TreeGen} - /** An implementation of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders. * * Should not be instantiated directly, use [[scala.reflect.runtime.universe]] instead. @@ -11,18 +10,21 @@ import internal.{SomePhase, NoPhase, Phase, TreeGen} */ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.SymbolTable { self => - def picklerPhase = SomePhase - + override def inform(msg: String): Unit = log(msg) + def picklerPhase = internal.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 def newStrictTreeCopier: TreeCopier = new StrictTreeCopier def newLazyTreeCopier: TreeCopier = new LazyTreeCopier + // 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 internal.TreeInfo + init() } - diff --git a/src/reflect/scala/reflect/runtime/ReflectSetup.scala b/src/reflect/scala/reflect/runtime/ReflectSetup.scala index 6e28fc8520..84f159be00 100644 --- a/src/reflect/scala/reflect/runtime/ReflectSetup.scala +++ b/src/reflect/scala/reflect/runtime/ReflectSetup.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import internal.{SomePhase, NoPhase, Phase, TreeGen} diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index 33ad6d2430..2db9706007 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -3,10 +3,12 @@ * @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 /** A few java-reflection oriented utility functions useful during reflection bootstrapping. */ @@ -33,8 +35,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 => diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 0e0cf3fc40..a14eafff24 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,10 +33,10 @@ 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 Xprintpos = new BooleanSetting(false) - val Ynotnull = new BooleanSetting(false) val Yshowsymkinds = new BooleanSetting(false) + val Yposdebug = new BooleanSetting(false) + val Yrangepos = new BooleanSetting(false) val debug = new BooleanSetting(false) val deepCloning = new BooleanSetting(false) val explaintypes = new BooleanSetting(false) @@ -43,6 +44,7 @@ private[reflect] class Settings extends MutableSettings { val printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) + val breakCycles = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 61663f6181..815cc0c885 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import internal.Flags @@ -28,7 +29,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => debugInfo("completing "+sym+"/"+clazz.fullName) assert(sym == clazz || sym == module || sym == module.moduleClass) // try { - atPhaseNotLaterThan(picklerPhase) { + enteringPhaseNotLaterThan(picklerPhase) { val loadingMirror = mirrorThatLoaded(sym) val javaClass = loadingMirror.javaClass(clazz.javaClassName) loadingMirror.unpickleClass(clazz, module, javaClass) @@ -57,7 +58,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) @@ -67,7 +68,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) } @@ -75,7 +76,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. @@ -116,9 +117,9 @@ 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 diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 5c08e9a508..bcd4d16cde 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._ @@ -11,10 +12,10 @@ import scala.reflect.internal.Flags._ private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { 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. * diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 7b280e59b9..132470b2e7 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 diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 00f6952dc1..98cad45db1 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.reflect.io.AbstractFile @@ -83,9 +84,6 @@ 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 @@ -118,7 +116,8 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb 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 } + override def tpe_* : Type = synchronized { super.tpe_* } + override def tpeHK : Type = synchronized { super.tpeHK } } trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index a3e7c28ca4..f4b02c5bcd 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -1,4 +1,5 @@ -package scala.reflect +package scala +package reflect package runtime import scala.collection.mutable.WeakHashMap diff --git a/src/reflect/scala/reflect/runtime/TwoWayCache.scala b/src/reflect/scala/reflect/runtime/TwoWayCache.scala index 05debcba65..181ca6014a 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 diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala index b97913daf0..41c1310e17 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,7 +21,7 @@ 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 { |