diff options
Diffstat (limited to 'src/reflect')
16 files changed, 308 insertions, 110 deletions
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index fc7e184918..19460af27f 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -537,7 +537,8 @@ trait Definitions extends api.StandardDefinitions { lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature] lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature] - lazy val MethodHandle = getClassIfDefined("java.lang.invoke.MethodHandle") + lazy val MethodHandleClass = getClassIfDefined("java.lang.invoke.MethodHandle") + lazy val VarHandleClass = getClassIfDefined("java.lang.invoke.VarHandle") // Option classes lazy val OptionClass: ClassSymbol = requiredClass[Option[_]] @@ -1567,9 +1568,12 @@ trait Definitions extends api.StandardDefinitions { lazy val PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest) lazy val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass) + private lazy val PolymorphicSignatureClass = MethodHandleClass.companionModule.info.decl(TypeName("PolymorphicSignature")) - def isPolymorphicSignature(sym: Symbol) = PolySigMethods(sym) - private lazy val PolySigMethods: Set[Symbol] = Set[Symbol](MethodHandle.info.decl(sn.Invoke), MethodHandle.info.decl(sn.InvokeExact)).filter(_.exists) + def isPolymorphicSignature(sym: Symbol) = sym != null && sym.isJavaDefined && { + val owner = sym.safeOwner + (owner == MethodHandleClass || owner == VarHandleClass) && sym.hasAnnotation(PolymorphicSignatureClass) + } lazy val Scala_Java8_CompatPackage = rootMirror.getPackageIfDefined("scala.runtime.java8") } diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index a7bb127506..32ce02bdd6 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -282,6 +282,34 @@ trait Scopes extends api.Scopes { self: SymbolTable => } } + final def lookupSymbolEntry(sym: Symbol): ScopeEntry = { + var e = lookupEntry(sym.name) + while (e ne null) { + if (e.sym == sym) return e + e = lookupNextEntry(e) + } + null + } + + final def lookupCompanion(original: Symbol): Symbol = { + lookupSymbolEntry(original) match { + case null => + case entry => + var e = lookupEntry(original.name.companionName) + while (e != null) { + // 1) Must be owned by the same Scope, to ensure that in + // `{ class C; { ...; object C } }`, the class is not seen as a comaniopn of the object. + // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. + def isClassAndModule(sym1: Symbol, sym2: Symbol) = sym1.isClass && sym2.isModule + if ((e.owner eq entry.owner) && (isClassAndModule(original, e.sym) || isClassAndModule(e.sym, original))) { + return if (e.sym.isCoDefinedWith(original)) e.sym else NoSymbol + } + e = lookupNextEntry(e) + } + } + NoSymbol + } + /** lookup a symbol entry matching given name. * @note from Martin: I believe this is a hotspot or will be one * in future versions of the type system. I have reverted the previous diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 78f360409d..fc49de1cf6 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -78,4 +78,13 @@ trait StdAttachments { case object OuterArgCanBeElided extends PlainAttachment case object UseInvokeSpecial extends PlainAttachment + + /** An attachment carrying information between uncurry and erasure */ + case class TypeParamVarargsAttachment(val typeParamRef: Type) + + /** Attached to a class symbol to indicate that its children have been observed + * via knownDirectSubclasses. Children added subsequently will trigger an + * error to indicate that the earlier observation was incomplete. + */ + case object KnownDirectSubclassesCalled extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 56b6dc078d..e664b5ad08 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -34,9 +34,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => def recursionTable = _recursionTable def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable = value + @deprecated("Global existential IDs no longer used", "2.12.1") private var existentialIds = 0 + @deprecated("Global existential IDs no longer used", "2.12.1") protected def nextExistentialId() = { existentialIds += 1; existentialIds } - protected def freshExistentialName(suffix: String) = newTypeName("_" + nextExistentialId() + suffix) + @deprecated("Use overload that accepts an id", "2.12.1") + protected def freshExistentialName(suffix: String): TypeName = freshExistentialName(suffix, nextExistentialId()) + protected def freshExistentialName(suffix: String, id: Int): TypeName = newTypeName("_" + id + suffix) // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { @@ -117,6 +121,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => def knownDirectSubclasses = { // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize + + enclosingPackage.info.decls.foreach { sym => + if(sourceFile == sym.sourceFile) { + sym.rawInfo.forceDirectSuperclasses + } + } + + if(!isPastTyper) + updateAttachment(KnownDirectSubclassesCalled) + children } @@ -440,8 +454,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem = newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | GADT_SKOLEM_FLAGS) setInfo info + @deprecated("Use overload that accepts an id", "2.12.1") final def freshExistential(suffix: String): TypeSymbol = newExistential(freshExistentialName(suffix), pos) + final def freshExistential(suffix: String, id: Int): TypeSymbol = + newExistential(freshExistentialName(suffix, id), pos) /** Type skolems are type parameters ''seen from the inside'' * Assuming a polymorphic method m[T], its type is a PolyType which has a TypeParameter @@ -488,7 +505,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * often to the point of never. */ def newStubSymbol(name: Name, missingMessage: String, isPackage: Boolean = false): Symbol = name match { - case n: TypeName => if (isPackage) new StubPackageClassSymbol(this, n, missingMessage) else new StubClassSymbol(this, n, missingMessage) + case n: TypeName => new StubClassSymbol(this, n, missingMessage) case _ => new StubTermSymbol(this, name.toTermName, missingMessage) } @@ -3291,7 +3308,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var childSet: Set[Symbol] = Set() override def children = childSet - override def addChild(sym: Symbol) { childSet = childSet + sym } + override def addChild(sym: Symbol) { + if(!isPastTyper && hasAttachment[KnownDirectSubclassesCalled.type] && !childSet.contains(sym)) + globalError(s"knownDirectSubclasses of ${this.name} observed before subclass ${sym.name} registered") + + childSet = childSet + sym + } def anonOrRefinementString = { if (hasCompleteInfo) { @@ -3423,7 +3445,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def companionSymbol = fail(NoSymbol) } class StubClassSymbol(owner0: Symbol, name0: TypeName, val missingMessage: String) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol - class StubPackageClassSymbol(owner0: Symbol, name0: TypeName, val missingMessage: String) extends PackageClassSymbol(owner0, owner0.pos, name0) with StubSymbol class StubTermSymbol(owner0: Symbol, name0: TermName, val missingMessage: String) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol trait FreeSymbol extends Symbol { diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 61937958dd..1aef30819a 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -480,7 +480,8 @@ abstract class TreeInfo { } map { dd => val DefDef(dmods, dname, _, _, _, drhs) = dd // get access flags from DefDef - val vdMods = (vmods &~ Flags.AccessFlags) | (dmods & Flags.AccessFlags).flags + val defDefMask = Flags.AccessFlags | OVERRIDE | IMPLICIT | DEFERRED + val vdMods = (vmods &~ defDefMask) | (dmods & defDefMask).flags // for most cases lazy body should be taken from accessor DefDef val vdRhs = if (vmods.isLazy) lazyValDefRhs(drhs) else vrhs copyValDef(vd)(mods = vdMods, name = dname, rhs = vdRhs) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f8679616d1..ad7e3ffe8f 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -91,7 +91,6 @@ trait Types private var explainSwitch = false private final val emptySymbolSet = immutable.Set.empty[Symbol] - private final val traceTypeVars = sys.props contains "scalac.debug.tvar" private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn on type parameter bounds being used * to seed type constraints. @@ -99,8 +98,6 @@ trait Types private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" private final val sharperSkolems = sys.props contains "scalac.experimental.sharper-skolems" - protected val enableTypeVarExperimentals = settings.Xexperimental.value - /** Caching the most recent map has a 75-90% hit rate. */ private object substTypeMapCache { private[this] var cached: SubstTypeMap = new SubstTypeMap(Nil, Nil) @@ -315,6 +312,11 @@ trait Types /** If this is a lazy type, assign a new type to `sym`. */ def complete(sym: Symbol) {} + /** If this is a lazy type corresponding to a subclass add it to its + * parents children + */ + def forceDirectSuperclasses: Unit = () + /** The term symbol associated with the type * Note that the symbol of the normalized type is returned (@see normalize) */ @@ -2823,13 +2825,13 @@ trait Types // now, pattern-matching returns the most recent constr object TypeVar { @inline final def trace[T](action: String, msg: => String)(value: T): T = { - if (traceTypeVars) { - val s = msg match { - case "" => "" - case str => "( " + str + " )" - } - Console.err.println("[%10s] %-25s%s".format(action, value, s)) - } + // Uncomment the following for a compiler that has some diagnostics about type inference + // I doubt this is ever useful in the wild, so a recompile will be needed +// val s = msg match { +// case "" => "" +// case str => "( " + str + " )" +// } +// Console.err.println("[%10s] %-25s%s".format(action, value, s)) value } @@ -2850,7 +2852,9 @@ trait Types val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType) if (exclude) new TypeConstraint - else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds)) + else TypeVar.trace("constraint", "For " + tparam.fullLocationString)( + new TypeConstraint(bounds) + ) } else new TypeConstraint } @@ -2879,7 +2883,9 @@ trait Types else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) - trace("create", "In " + tv.originLocation)(tv) + trace("create", "In " + tv.originLocation)( + tv + ) } private def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar = createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable) @@ -2983,7 +2989,9 @@ trait Types else if (newArgs.size == params.size) { val tv = TypeVar(origin, constr, newArgs, params) tv.linkSuspended(this) - TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) + TypeVar.trace("applyArgs", s"In $originLocation, apply args ${newArgs.mkString(", ")} to $originName")( + tv + ) } else TypeVar(typeSymbol).setInst(ErrorType) @@ -3002,31 +3010,20 @@ trait Types // only one of them is in the set of tvars that need to be solved, but // they share the same TypeConstraint instance - // When comparing to types containing skolems, remember the highest level - // of skolemization. If that highest level is higher than our initial - // skolemizationLevel, we can't re-use those skolems as the solution of this - // typevar, which means we'll need to repack our inst into a fresh existential. - // were we compared to skolems at a higher skolemizationLevel? - // EXPERIMENTAL: value will not be considered unless enableTypeVarExperimentals is true - // see SI-5729 for why this is still experimental - private var encounteredHigherLevel = false - private def shouldRepackType = enableTypeVarExperimentals && encounteredHigherLevel - // <region name="constraint mutators + undoLog"> // invariant: before mutating constr, save old state in undoLog // (undoLog is used to reset constraints to avoid piling up unrelated ones) - def setInst(tp: Type): this.type = { - if (tp eq this) { + def setInst(tp: Type): this.type = + if (tp ne this) { + undoLog record this + constr.inst = TypeVar.trace("setInst", s"In $originLocation, $originName=$tp")( + tp + ) + this + } else { log(s"TypeVar cycle: called setInst passing $this to itself.") - return this + this } - undoLog record this - // if we were compared against later typeskolems, repack the existential, - // because skolems are only compatible if they were created at the same level - val res = if (shouldRepackType) repackExistential(tp) else tp - constr.inst = TypeVar.trace("setInst", "In " + originLocation + ", " + originName + "=" + res)(res) - this - } def addLoBound(tp: Type, isNumericBound: Boolean = false) { assert(tp != this, tp) // implies there is a cycle somewhere (?) @@ -3251,19 +3248,13 @@ trait Types case ts: TypeSkolem => ts.level > level case _ => false } - // side-effects encounteredHigherLevel - private def containsSkolemAboveLevel(tp: Type) = - (tp exists isSkolemAboveLevel) && { encounteredHigherLevel = true ; true } - /** Can this variable be related in a constraint to type `tp`? + + /** Can this variable be related in a constraint to type `tp`? * This is not the case if `tp` contains type skolems whose * skolemization level is higher than the level of this variable. */ - def isRelatable(tp: Type) = ( - shouldRepackType // short circuit if we already know we've seen higher levels - || !containsSkolemAboveLevel(tp) // side-effects tracking boolean - || enableTypeVarExperimentals // -Xexperimental: always say we're relatable, track consequences - ) + def isRelatable(tp: Type) = !(tp exists isSkolemAboveLevel) override def normalize: Type = ( if (instValid) inst @@ -3311,7 +3302,7 @@ trait Types // to never be resumed with the current implementation assert(!suspended, this) TypeVar.trace("clone", originLocation)( - TypeVar(origin, constr.cloneInternal, typeArgs, params) // @M TODO: clone args/params? + TypeVar(origin, constr.cloneInternal, typeArgs, params) ) } } @@ -4487,6 +4478,7 @@ trait Types debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss") NoType case Some(argsst) => + var capturedParamIds = 0 val args = map2(sym.typeParams, argsst) { (tparam, as0) => val as = as0.distinct if (as.size == 1) as.head @@ -4508,8 +4500,10 @@ trait Types 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 + capturedParamIds += 1 + val capturedParamId = capturedParamIds - val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) + val qvar = commonOwner(as).freshExistential("", capturedParamId) setInfo TypeBounds(g, l) capturedParams += qvar qvar.tpe } diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 6dea184826..fe1de91662 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -216,19 +216,6 @@ abstract class UnPickler { } adjust(decl) } - def nestedObjectSymbol: Symbol = { - // If the owner is overloaded (i.e. a method), it's not possible to select the - // right member, so return NoSymbol. This can only happen when unpickling a tree. - // the "case Apply" in readTree() takes care of selecting the correct alternative - // after parsing the arguments. - if (owner.isOverloaded) - return NoSymbol - - if (tag == EXTMODCLASSref) { - owner.info.decl(nme.moduleVarName(name.toTermName)) - } - NoSymbol - } def moduleAdvice(missing: String): String = { val module = @@ -255,20 +242,18 @@ abstract class UnPickler { // symbols are read from outside: for instance when checking the children // of a class. See #1722. fromName(nme.expandedName(name.toTermName, owner)) orElse { - // (3) Try as a nested object symbol. - nestedObjectSymbol orElse { - // (4) Call the mirror's "missing" hook. - adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { - // (5) Create a stub symbol to defer hard failure a little longer. - val advice = moduleAdvice(s"${owner.fullName}.$name") - val missingMessage = - s"""|missing or invalid dependency detected while loading class file '$filename'. - |Could not access ${name.longString} in ${owner.kindString} ${owner.fullName}, - |because it (or its dependencies) are missing. Check your build definition for - |missing or conflicting dependencies. (Re-run with `-Ylog-classpath` to see the problematic classpath.) - |A full rebuild may help if '$filename' was compiled against an incompatible version of ${owner.fullName}.$advice""".stripMargin - owner.newStubSymbol(name, missingMessage) - } + // (3) Call the mirror's "missing" hook. + adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { + // (4) Create a stub symbol to defer hard failure a little longer. + val advice = moduleAdvice(s"${owner.fullName}.$name") + val missingMessage = + s"""|missing or invalid dependency detected while loading class file '$filename'. + |Could not access ${name.longString} in ${owner.kindString} ${owner.fullName}, + |because it (or its dependencies) are missing. Check your build definition for + |missing or conflicting dependencies. (Re-run with `-Ylog-classpath` to see the problematic classpath.) + |A full rebuild may help if '$filename' was compiled against an incompatible version of ${owner.fullName}.$advice""".stripMargin + val stubName = if (tag == EXTref) name else name.toTypeName + owner.newStubSymbol(stubName, missingMessage) } } } @@ -392,9 +377,7 @@ abstract class UnPickler { def readThisType(): Type = { val sym = readSymbolRef() match { - case stub: StubSymbol if !stub.isClass => - // SI-8502 This allows us to create a stub for a unpickled reference to `missingPackage.Foo`. - stub.owner.newStubSymbol(stub.name.toTypeName, stub.missingMessage, isPackage = true) + case stub: StubSymbol => stub.setFlag(PACKAGE) case sym => sym } ThisType(sym) diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 5a2c802476..ab933ae617 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -54,6 +54,7 @@ abstract class MutableSettings extends AbsSettings { def uniqid: BooleanSetting def verbose: BooleanSetting def YpartialUnification: BooleanSetting + def Yvirtpatmat: BooleanSetting def Yrecursion: IntSetting def maxClassfileName: IntSetting diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index ba4f2bec4b..08219c0634 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -512,6 +512,8 @@ private[internal] trait TypeMaps { && isBaseClassOfEnclosingClass(sym.owner) ) + private var capturedThisIds= 0 + private def nextCapturedThisId() = { capturedThisIds += 1; capturedThisIds } /** Creates an existential representing a type parameter which appears * in the prefix of a ThisType. */ @@ -519,7 +521,7 @@ private[internal] trait TypeMaps { capturedParams find (_.owner == clazz) match { case Some(p) => p.tpe case _ => - val qvar = clazz freshExistential nme.SINGLETON_SUFFIX setInfo singletonBounds(pre) + val qvar = clazz.freshExistential(nme.SINGLETON_SUFFIX, nextCapturedThisId()) setInfo singletonBounds(pre) _capturedParams ::= qvar debuglog(s"Captured This(${clazz.fullNameString}) seen from $seenFromPrefix: ${qvar.defString}") qvar.tpe diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index a50084f40d..3918723b5c 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -4,6 +4,7 @@ package internal package transform import Flags._ +import scala.collection.mutable trait UnCurry { @@ -11,6 +12,12 @@ trait UnCurry { import global._ import definitions._ + /** + * The synthetic Java vararg method symbol corresponding to a Scala vararg method + * annotated with @varargs. + */ + case class VarargsSymbolAttachment(varargMethod: Symbol) + /** 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) @@ -65,18 +72,74 @@ trait UnCurry { def apply(tp0: Type): Type = { val tp = expandAlias(tp0) tp match { - case ClassInfoType(parents, decls, clazz) => + case ClassInfoType(parents, decls, clazz) if !clazz.isJavaDefined => val parents1 = parents mapConserve uncurry - if (parents1 eq parents) tp - else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls?? + val varargOverloads = mutable.ListBuffer.empty[Symbol] + + // Not using `hasAnnotation` here because of dreaded cyclic reference errors: + // it may happen that VarargsClass has not been initialized yet and we get here + // while processing one of its superclasses (such as java.lang.Object). Since we + // don't need the more precise `matches` semantics, we only check the symbol, which + // is anyway faster and safer + for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) { + if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) { + varargOverloads += varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info)) + } + } + if ((parents1 eq parents) && varargOverloads.isEmpty) tp + else { + val newDecls = decls.cloneScope + varargOverloads.foreach(newDecls.enter) + ClassInfoType(parents1, newDecls, clazz) + } // @MAT normalize in decls?? + case PolyType(_, _) => mapOver(tp) + case _ => tp } } } + private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = { + val forwSym = origSym.cloneSymbol(currentClass, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED, origSym.name.toTermName).withoutAnnotations + + // we are using `origSym.info`, which contains the type *before* the transformation + // so we still see repeated parameter types (uncurry replaces them with Seq) + val isRepeated = origSym.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)) + val oldPs = newInfo.paramss.head + def toArrayType(tp: Type, newParam: Symbol): Type = { + val arg = elementType(SeqClass, tp) + val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) { + // To prevent generation of an `Object` parameter from `Array[T]` parameter later + // as this would crash the Java compiler which expects an `Object[]` array for varargs + // e.g. def foo[T](a: Int, b: T*) + // becomes def foo[T](a: Int, b: Array[Object]) + // instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object) + // + // In order for the forwarder method to type check we need to insert a cast: + // def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']]) + // The target element type for that cast (T') is stored in the TypeParamVarargsAttachment +// val originalArg = arg.substSym(oldTps, tps) + // Store the type parameter that was replaced by Object to emit the correct generic signature + newParam.updateAttachment(new TypeParamVarargsAttachment(arg)) + ObjectTpe + } else + arg + arrayType(elem) + } + + foreach2(forwSym.paramss.flatten, isRepeated)((p, isRep) => + if (isRep) { + p.setInfo(toArrayType(p.info, p)) + } + ) + + origSym.updateAttachment(VarargsSymbolAttachment(forwSym)) + forwSym + } + /** - return symbol's transformed type, * - if symbol is a def parameter with transformed type T, return () => T * diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala index b5030460b8..3cede1b3c5 100644 --- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -12,6 +12,20 @@ import java.security.cert.Certificate import java.security.{ ProtectionDomain, CodeSource } import java.util.{ Collections => JCollections, Enumeration => JEnumeration } +object AbstractFileClassLoader { + // should be a method on AbstractFile, but adding in `internal.util._` for now as we're in a minor release + private[scala] final def lookupPath(base: AbstractFile)(pathParts: Seq[String], directory: Boolean): AbstractFile = { + var file: AbstractFile = base + for (dirPart <- pathParts.init) { + file = file.lookupName(dirPart, directory = true) + if (file == null) + return null + } + + file.lookupName(pathParts.last, directory = directory) + } +} + /** A class loader that loads files from a [[scala.reflect.io.AbstractFile]]. * * @author Lex Spoon @@ -25,19 +39,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) else s"${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 - } + AbstractFileClassLoader.lookupPath(root)(name split '/', directory = false) } protected def dirNameToPath(name: String): String = diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index a2642628a4..64b6972298 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -154,18 +154,23 @@ class BatchSourceFile(val file : AbstractFile, content0: Array[Char]) extends So case _ => false } - def calculateLineIndices(cs: Array[Char]) = { - val buf = new ArrayBuffer[Int] - buf += 0 - for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1 - buf += cs.length // sentinel, so that findLine below works smoother - buf.toArray + private lazy val lineIndices: Array[Int] = { + def calculateLineIndices(cs: Array[Char]) = { + val buf = new ArrayBuffer[Int] + buf += 0 + for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1 + buf += cs.length // sentinel, so that findLine below works smoother + buf.toArray + } + calculateLineIndices(content) } - private lazy val lineIndices: Array[Int] = calculateLineIndices(content) - def lineToOffset(index : Int): Int = lineIndices(index) + def lineToOffset(index: Int): Int = { + val offset = lineIndices(index) + if (offset < length) offset else throw new IndexOutOfBoundsException(index.toString) + } - private var lastLine = 0 + private[this] var lastLine = 0 /** Convert offset to line in this source file. * Lines are numbered from 0. diff --git a/src/reflect/scala/reflect/io/PlainFile.scala b/src/reflect/scala/reflect/io/PlainFile.scala index eb0940e703..989081ebe0 100644 --- a/src/reflect/scala/reflect/io/PlainFile.scala +++ b/src/reflect/scala/reflect/io/PlainFile.scala @@ -40,7 +40,6 @@ class PlainFile(val givenPath: Path) extends AbstractFile { override def output = givenPath.toFile.outputStream() override def sizeOption = Some(givenPath.length.toInt) - override def toString = path override def hashCode(): Int = fpath.hashCode() override def equals(that: Any): Boolean = that match { case x: PlainFile => fpath == x.fpath @@ -91,3 +90,82 @@ class PlainFile(val givenPath: Path) extends AbstractFile { def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = new PlainFile(givenPath / name) } + +private[scala] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractFile { + import java.nio.file._ + + assert(nioPath ne null) + + /** Returns the underlying File if any and null otherwise. */ + override def file: java.io.File = try { + nioPath.toFile + } catch { + case _: UnsupportedOperationException => null + } + + override def underlyingSource = Some(this) + + private val fpath = nioPath.toAbsolutePath.toString + + /** Returns the name of this abstract file. */ + def name = nioPath.getFileName.toString + + /** Returns the path of this abstract file. */ + def path = nioPath.toString + + /** The absolute file. */ + def absolute = new PlainNioFile(nioPath.toAbsolutePath) + + override def container: AbstractFile = new PlainNioFile(nioPath.getParent) + override def input = Files.newInputStream(nioPath) + override def output = Files.newOutputStream(nioPath) + override def sizeOption = Some(Files.size(nioPath).toInt) + override def hashCode(): Int = fpath.hashCode() + override def equals(that: Any): Boolean = that match { + case x: PlainNioFile => fpath == x.fpath + case _ => false + } + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = Files.isDirectory(nioPath) + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long = Files.getLastModifiedTime(nioPath).toMillis + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] = { + try { + import scala.collection.JavaConverters._ + val it = Files.newDirectoryStream(nioPath).iterator() + it.asScala.map(new PlainNioFile(_)) + } catch { + case _: NotDirectoryException => Iterator.empty + } + } + + /** + * 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. + */ + def lookupName(name: String, directory: Boolean): AbstractFile = { + val child = nioPath.resolve(name) + if ((Files.isDirectory(child) && directory) || (Files.isRegularFile(child) && !directory)) new PlainNioFile(child) + else null + } + + /** Does this abstract file denote an existing file? */ + def create(): Unit = if (!exists) Files.createFile(nioPath) + + /** Delete the underlying file or directory (recursively). */ + def delete(): Unit = + if (Files.isRegularFile(nioPath)) Files.deleteIfExists(nioPath) + else if (Files.isDirectory(nioPath)) new Directory(nioPath.toFile).deleteRecursively() + + /** Returns a plain file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = + new PlainNioFile(nioPath.resolve(name)) +} diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index b74ccb9177..95d6662d14 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -47,6 +47,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.InlineCallsiteAttachment this.OuterArgCanBeElided this.UseInvokeSpecial + this.TypeParamVarargsAttachment + this.KnownDirectSubclassesCalled this.noPrint this.typeDebug this.Range @@ -321,7 +323,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.QuasiquoteClass_api_unapply definitions.ScalaSignatureAnnotation definitions.ScalaLongSignatureAnnotation - definitions.MethodHandle + definitions.MethodHandleClass + definitions.VarHandleClass definitions.OptionClass definitions.OptionModule definitions.SomeClass @@ -458,6 +461,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ScalaValueClassesNoUnit definitions.ScalaValueClasses + uncurry.VarargsSymbolAttachment uncurry.DesugaredParameterType erasure.GenericArray erasure.scalaErasure diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 3b33f089e1..2d8bacd3b2 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -48,6 +48,7 @@ private[reflect] class Settings extends MutableSettings { val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) val YpartialUnification = new BooleanSetting(false) + val Yvirtpatmat = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 237afa082b..4e7ddda54e 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -10,7 +10,9 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb private lazy val atomicIds = new java.util.concurrent.atomic.AtomicInteger(0) override protected def nextId() = atomicIds.incrementAndGet() + @deprecated("Global existential IDs no longer used", "2.12.1") private lazy val atomicExistentialIds = new java.util.concurrent.atomic.AtomicInteger(0) + @deprecated("Global existential IDs no longer used", "2.12.1") override protected def nextExistentialId() = atomicExistentialIds.incrementAndGet() private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int]) |