diff options
Diffstat (limited to 'src/compiler')
99 files changed, 6234 insertions, 3809 deletions
diff --git a/src/compiler/rootdoc.txt b/src/compiler/rootdoc.txt new file mode 100644 index 0000000000..173f604098 --- /dev/null +++ b/src/compiler/rootdoc.txt @@ -0,0 +1,6 @@ +The Scala compiler API. + +The following resources are useful for Scala plugin/compiler development: + - [[http://www.scala-lang.org/node/215 Scala development tutorials]] on [[http://www.scala-lang.org www.scala-lang.org]] + - [[https://wiki.scala-lang.org/display/SIW/ Scala Internals wiki]] + - [[http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ Scala compiler corner]], maintained by Miguel diff --git a/src/compiler/scala/reflect/internal/AnnotationCheckers.scala b/src/compiler/scala/reflect/internal/AnnotationCheckers.scala index 666c1d74cb..449b0ca0bc 100644 --- a/src/compiler/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/compiler/scala/reflect/internal/AnnotationCheckers.scala @@ -49,6 +49,7 @@ trait AnnotationCheckers { def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree } + // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. /** The list of annotation checkers that have been registered */ private var annotationCheckers: List[AnnotationChecker] = Nil diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index 255e69c3c6..9a7c79d856 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -116,6 +116,11 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => // Classfile annot: args empty. Scala annot: assocs empty. assert(args.isEmpty || assocs.isEmpty, atp) + // @xeno.by: necessary for reification, see Reifiers.scala for more info + private var orig: Tree = EmptyTree + def original = orig + def setOriginal(t: Tree): this.type = { orig = t; this } + override def toString = ( atp + (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + @@ -130,7 +135,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => private var forced = false private lazy val forcedInfo = try { - val result = lazyInfo + val result = lazyInfo if (result.pos == NoPosition) result setPos pos result } finally forced = true @@ -138,10 +143,12 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def atp: Type = forcedInfo.atp def args: List[Tree] = forcedInfo.args def assocs: List[(Name, ClassfileAnnotArg)] = forcedInfo.assocs + def original: Tree = forcedInfo.original + def setOriginal(t: Tree): this.type = { forcedInfo.setOriginal(t); this } // We should always be able to print things without forcing them. override def toString = if (forced) forcedInfo.toString else "@<?>" - + override def pos: Position = if (forced) forcedInfo.pos else NoPosition } @@ -166,10 +173,16 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => def args: List[Tree] def assocs: List[(Name, ClassfileAnnotArg)] + // @xeno.by: necessary for reification, see Reifiers.scala for more info + def original: Tree + def setOriginal(t: Tree): this.type + /** Hand rolling Product. */ def _1 = atp def _2 = args def _3 = assocs + // @xeno.by: original hasn't become a product member for backward compatibility purposes + // def _4 = original def canEqual(other: Any) = other.isInstanceOf[AnnotationInfo] override def productPrefix = "AnnotationInfo" @@ -178,7 +191,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => private var rawpos: Position = NoPosition def pos = rawpos - def setPos(pos: Position): this.type = { + def setPos(pos: Position): this.type = { // Syncnote: Setpos inaccessible to reflection, so no sync in rawpos necessary. rawpos = pos this } diff --git a/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala b/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala index 38277b5a09..9e5c93753f 100644 --- a/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/compiler/scala/reflect/internal/BaseTypeSeqs.scala @@ -29,7 +29,14 @@ trait BaseTypeSeqs { this: SymbolTable => import definitions._ - class BaseTypeSeq(parents: List[Type], elems: Array[Type]) { + protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + new BaseTypeSeq(parents, elems) + + /** Note: constructor is protected to force everyone to use the factory method newBaseTypeSeq instead. + * This is necessary because when run from reflection every base type sequence needs to have a + * SynchronizedBaseTypeSeq as mixin. + */ + class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => incCounter(baseTypeSeqCount) incCounter(baseTypeSeqLenTotal, elems.length) @@ -41,7 +48,7 @@ trait BaseTypeSeqs { // (while NoType is in there to indicate a cycle in this BTS, during the execution of // the mergePrefixAndArgs below, the elems get copied without the pending map, // so that NoType's are seen instead of the original type --> spurious compile error) - val pending = new mutable.BitSet(length) + private val pending = new mutable.BitSet(length) /** The type at i'th position in this sequence; lazy types are returned evaluated. */ def apply(i: Int): Type = @@ -89,11 +96,11 @@ trait BaseTypeSeqs { /** Return all evaluated types in this sequence as a list */ def toList: List[Type] = elems.toList - protected def copy(head: Type, offset: Int): BaseTypeSeq = { + def copy(head: Type, offset: Int): BaseTypeSeq = { val arr = new Array[Type](elems.length + offset) compat.Platform.arraycopy(elems, 0, arr, offset, elems.length) arr(0) = head - new BaseTypeSeq(parents, arr) + newBaseTypeSeq(parents, arr) } /** Compute new base type sequence with `tp` prepended to this sequence */ @@ -113,21 +120,10 @@ trait BaseTypeSeqs { arr(i) = f(elems(i)) i += 1 } - new BaseTypeSeq(parents, arr) + newBaseTypeSeq(parents, arr) } - def lateMap(f: Type => Type): BaseTypeSeq = new BaseTypeSeq(parents map f, elems) { - override def apply(i: Int) = f(self.apply(i)) - override def rawElem(i: Int) = f(self.rawElem(i)) - override def typeSymbol(i: Int) = self.typeSymbol(i) - override def toList = self.toList map f - override protected def copy(head: Type, offset: Int) = (self map f).copy(head, offset) - override def map(g: Type => Type) = lateMap(g) - override def lateMap(g: Type => Type) = self.lateMap(x => g(f(x))) - override def exists(p: Type => Boolean) = elems exists (x => p(f(x))) - override protected def maxDepthOfElems: Int = elems map (x => maxDpth(f(x))) max - override def toString = elems.mkString("MBTS(", ",", ")") - } + def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) def exists(p: Type => Boolean): Boolean = elems exists p @@ -177,10 +173,10 @@ trait BaseTypeSeqs { /** A merker object for a base type sequence that's no yet computed. * used to catch inheritance cycles */ - val undetBaseTypeSeq: BaseTypeSeq = new BaseTypeSeq(List(), Array()) + val undetBaseTypeSeq: BaseTypeSeq = newBaseTypeSeq(List(), Array()) /** Create a base type sequence consisting of a single type */ - def baseTypeSingletonSeq(tp: Type): BaseTypeSeq = new BaseTypeSeq(List(), Array(tp)) + def baseTypeSingletonSeq(tp: Type): BaseTypeSeq = newBaseTypeSeq(List(), Array(tp)) /** Create the base type sequence of a compound type wuth given tp.parents */ def compoundBaseTypeSeq(tp: Type): BaseTypeSeq = { @@ -244,8 +240,21 @@ trait BaseTypeSeqs { val elems = new Array[Type](btsSize) buf.copyToArray(elems, 0) // Console.println("computed baseTypeSeq of " + tsym.tpe + " " + parents + ": "+elems.toString)//DEBUG - new BaseTypeSeq(parents, elems) + newBaseTypeSeq(parents, elems) } - + + class MappedBaseTypeSeq(orig: BaseTypeSeq, f: Type => Type) extends BaseTypeSeq(orig.parents map f, orig.elems) { + override def apply(i: Int) = f(orig.apply(i)) + override def rawElem(i: Int) = f(orig.rawElem(i)) + override def typeSymbol(i: Int) = orig.typeSymbol(i) + override def toList = orig.toList map f + override def copy(head: Type, offset: Int) = (orig map f).copy(head, offset) + override def map(g: Type => Type) = lateMap(g) + override def lateMap(g: Type => Type) = orig.lateMap(x => g(f(x))) + override def exists(p: Type => Boolean) = elems exists (x => p(f(x))) + override protected def maxDepthOfElems: Int = elems map (x => maxDpth(f(x))) max + override def toString = elems.mkString("MBTS(", ",", ")") + } + val CyclicInheritance = new Throwable } diff --git a/src/compiler/scala/reflect/internal/ClassfileConstants.scala b/src/compiler/scala/reflect/internal/ClassfileConstants.scala index f1bf41ede9..1c4c007de0 100644 --- a/src/compiler/scala/reflect/internal/ClassfileConstants.scala +++ b/src/compiler/scala/reflect/internal/ClassfileConstants.scala @@ -88,6 +88,7 @@ object ClassfileConstants { final val ARRAY_TAG = '[' final val VOID_TAG = 'V' final val TVAR_TAG = 'T' + final val OBJECT_TAG = 'L' final val ANNOTATION_TAG = '@' final val SCALA_NOTHING = "scala.runtime.Nothing$" final val SCALA_NULL = "scala.runtime.Null$" diff --git a/src/compiler/scala/reflect/internal/Constants.scala b/src/compiler/scala/reflect/internal/Constants.scala index 9c4b2b2245..c328cc49cb 100644 --- a/src/compiler/scala/reflect/internal/Constants.scala +++ b/src/compiler/scala/reflect/internal/Constants.scala @@ -45,7 +45,7 @@ trait Constants extends api.Constants { case x: Char => CharTag case x: Type => ClassTag case x: Symbol => EnumTag - case _ => throw new Error("bad constant value: " + value) + case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass) } def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 02d6737bdb..b4b0a7335d 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -14,9 +14,17 @@ import PartialFunction._ trait Definitions extends reflect.api.StandardDefinitions { self: SymbolTable => + /** Since both the value parameter types and the result type may + * require access to the type parameter symbols, we model polymorphic + * creation as a function from those symbols to (formal types, result type). + * The Option is to distinguish between nullary methods and empty-param-list + * methods. + */ + private type PolyMethodCreator = List[Symbol] => (Option[List[Type]], Type) + private def newClass(owner: Symbol, name: TypeName, parents: List[Type], flags: Long = 0L): Symbol = { val clazz = owner.newClassSymbol(name, NoPosition, flags) - clazz setInfoAndEnter ClassInfoType(parents, new Scope, clazz) + clazz setInfoAndEnter ClassInfoType(parents, newScope, clazz) } private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): Symbol = { val msym = owner.newMethod(name.encode, NoPosition, flags) @@ -63,7 +71,7 @@ trait Definitions extends reflect.api.StandardDefinitions { tpnme.Double -> DOUBLE_TAG, tpnme.Boolean -> BOOL_TAG, tpnme.Unit -> VOID_TAG, - tpnme.Object -> TVAR_TAG + tpnme.Object -> OBJECT_TAG ) private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) @@ -120,6 +128,7 @@ trait Definitions extends reflect.api.StandardDefinitions { FloatClass, DoubleClass ) + def ScalaValueClassCompanions: List[Symbol] = ScalaValueClasses map (_.companionSymbol) } object definitions extends AbsDefinitions with ValueClassDefinitions { @@ -201,8 +210,12 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val AnyClass = newClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectClass.typeConstructor) lazy val ObjectClass = getClass(sn.Object) - lazy val AnyCompanionClass = getRequiredClass("scala.AnyCompanion") initFlags (SEALED | ABSTRACT | TRAIT) - lazy val AnyValCompanionClass = getRequiredClass("scala.AnyValCompanion") initFlags (SEALED | ABSTRACT | TRAIT) + + // Note: this is not the type alias AnyRef, it's a companion-like + // object used by the @specialize annotation. + def AnyRefModule = getMember(ScalaPackageClass, nme.AnyRef) + @deprecated("Use AnyRefModule", "2.10.0") + def Predef_AnyRef = AnyRefModule // bottom types lazy val RuntimeNothingClass = getClass(fulltpnme.RuntimeNothing) @@ -211,7 +224,7 @@ trait Definitions extends reflect.api.StandardDefinitions { sealed abstract class BottomClassSymbol(name: TypeName, parent: Symbol) extends ClassSymbol(ScalaPackageClass, NoPosition, name) { locally { this initFlags ABSTRACT | TRAIT | FINAL - this setInfoAndEnter ClassInfoType(List(parent.tpe), new Scope, this) + this setInfoAndEnter ClassInfoType(List(parent.tpe), newScope, this) } final override def isBottomClass = true } @@ -257,9 +270,7 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val PredefModule: Symbol = getRequiredModule("scala.Predef") lazy val PredefModuleClass = PredefModule.moduleClass - // Note: this is not the type alias AnyRef, it's a val defined in Predef - // used by the @specialize annotation. - def Predef_AnyRef = getMember(PredefModule, nme.AnyRef) + def Predef_classOf = getMember(PredefModule, nme.classOf) def Predef_identity = getMember(PredefModule, nme.identity) def Predef_conforms = getMember(PredefModule, nme.conforms) @@ -273,6 +284,11 @@ trait Definitions extends reflect.api.StandardDefinitions { def isPredefMemberNamed(sym: Symbol, name: Name) = ( (sym.name == name) && (sym.owner == PredefModule.moduleClass) ) + + /** Specialization. + */ + lazy val SpecializableModule = getRequiredModule("scala.Specializable") + lazy val GroupOfSpecializable = SpecializableModule.info.member(newTypeName("Group")) lazy val ConsoleModule: Symbol = getRequiredModule("scala.Console") lazy val ScalaRunTimeModule: Symbol = getRequiredModule("scala.runtime.ScalaRunTime") @@ -311,17 +327,10 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val RemoteInterfaceClass = getRequiredClass("java.rmi.Remote") lazy val RemoteExceptionClass = getRequiredClass("java.rmi.RemoteException") - lazy val RepeatedParamClass = newCovariantPolyClass( - ScalaPackageClass, - tpnme.REPEATED_PARAM_CLASS_NAME, - tparam => seqType(tparam.typeConstructor) - ) - - lazy val JavaRepeatedParamClass = newCovariantPolyClass( - ScalaPackageClass, - tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, - tparam => arrayType(tparam.typeConstructor) - ) + lazy val ByNameParamClass = specialPolyClass(tpnme.BYNAME_PARAM_CLASS_NAME, COVARIANT)(_ => AnyClass.typeConstructor) + lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN_NAME, 0L)(_ => AnyClass.typeConstructor) + lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.typeConstructor)) + lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.typeConstructor)) def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass @@ -350,15 +359,6 @@ trait Definitions extends reflect.api.StandardDefinitions { case _ => false } - lazy val ByNameParamClass = newCovariantPolyClass( - ScalaPackageClass, - tpnme.BYNAME_PARAM_CLASS_NAME, - tparam => AnyClass.typeConstructor - ) - lazy val EqualsPatternClass = { - val clazz = newClass(ScalaPackageClass, tpnme.EQUALS_PATTERN_NAME, Nil) - clazz setInfo polyType(List(newTypeParam(clazz, 0)), ClassInfoType(anyparam, new Scope, clazz)) - } lazy val MatchingStrategyClass = getRequiredClass("scala.MatchingStrategy") // collections classes @@ -410,9 +410,6 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val FullManifestModule = getRequiredModule("scala.reflect.Manifest") lazy val OptManifestClass = getRequiredClass("scala.reflect.OptManifest") lazy val NoManifest = getRequiredModule("scala.reflect.NoManifest") - lazy val CodeClass = getClass(sn.Code) - lazy val CodeModule = getModule(sn.Code) - lazy val Code_lift = getMember(CodeModule, nme.lift_) lazy val ScalaSignatureAnnotation = getRequiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnotation = getRequiredClass("scala.reflect.ScalaLongSignature") @@ -429,24 +426,27 @@ trait Definitions extends reflect.api.StandardDefinitions { * information into the toString method. */ def manifestToType(m: OptManifest[_]): Type = m match { - case x: AnyValManifest[_] => - getClassIfDefined("scala." + x).tpe case m: ClassManifest[_] => - val name = m.erasure.getName - if (name endsWith nme.MODULE_SUFFIX_STRING) - getModuleIfDefined(name stripSuffix nme.MODULE_SUFFIX_STRING).tpe - else { - val sym = getClassIfDefined(name) - val args = m.typeArguments - - if (sym eq NoSymbol) NoType - else if (args.isEmpty) sym.tpe - else appliedType(sym.typeConstructor, args map manifestToType) - } + val sym = manifestToSymbol(m) + val args = m.typeArguments + + if ((sym eq NoSymbol) || args.isEmpty) sym.tpe + else appliedType(sym.typeConstructor, args map manifestToType) case _ => NoType } + def manifestToSymbol(m: ClassManifest[_]): Symbol = m match { + case x: scala.reflect.AnyValManifest[_] => + getMember(ScalaPackageClass, newTypeName("" + x)) + case _ => + val name = m.erasure.getName + if (name endsWith nme.MODULE_SUFFIX_STRING) + getModuleIfDefined(name stripSuffix nme.MODULE_SUFFIX_STRING) + else + getClassIfDefined(name) + } + // 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 @@ -605,6 +605,29 @@ trait Definitions extends reflect.api.StandardDefinitions { def ClassType(arg: Type) = if (phase.erasedTypes || forMSIL) ClassClass.tpe else appliedType(ClassClass.typeConstructor, List(arg)) + + def vmClassType(arg: Type): Type = ClassType(arg) + def vmSignature(sym: Symbol, info: Type): String = signature(info) // !!! + + /** Given a class symbol C with type parameters T1, T2, ... Tn + * which have upper/lower bounds LB1/UB1, LB1/UB2, ..., LBn/UBn, + * returns an existential type of the form + * + * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. + */ + def classExistentialType(clazz: Symbol): Type = + newExistentialType(clazz.typeParams, clazz.tpe) + + /** Given type U, creates a Type representing Class[_ <: U]. + */ + def boundedClassType(upperBound: Type) = + appliedTypeAsUpperBounds(ClassClass.typeConstructor, List(upperBound)) + + /** To avoid unchecked warnings on polymorphic classes, translate + * a Foo[T] into a Foo[_] for use in the pattern matcher. + */ + @deprecated("Use classExistentialType", "2.10.0") + def typeCaseType(clazz: Symbol): Type = classExistentialType(clazz) // // .NET backend @@ -615,7 +638,7 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val ValueTypeClass: Symbol = getClass(sn.ValueType) // System.MulticastDelegate lazy val DelegateClass: Symbol = getClass(sn.Delegate) - var Delegate_scalaCallers: List[Symbol] = List() + 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() @@ -635,31 +658,40 @@ trait Definitions extends reflect.api.StandardDefinitions { case _ => false }) } - + // members of class scala.Any - var Any_== : Symbol = _ - var Any_!= : Symbol = _ - var Any_equals : Symbol = _ - var Any_hashCode : Symbol = _ - var Any_toString : Symbol = _ - var Any_getClass : Symbol = _ - var Any_isInstanceOf: Symbol = _ - var Any_asInstanceOf: Symbol = _ - var Any_## : Symbol = _ - - // members of class java.lang.{Object, String} - var Object_eq : Symbol = _ - var Object_ne : Symbol = _ - var Object_== : Symbol = _ - var Object_!= : Symbol = _ - var Object_## : Symbol = _ - var Object_synchronized: Symbol = _ - lazy val Object_isInstanceOf = newPolyMethod( - ObjectClass, newTermName("$isInstanceOf"), - tparam => MethodType(List(), booltype)) setFlag (FINAL | SYNTHETIC) - lazy val Object_asInstanceOf = newPolyMethod( - ObjectClass, newTermName("$asInstanceOf"), - tparam => MethodType(List(), tparam.typeConstructor)) setFlag (FINAL | SYNTHETIC) + lazy val Any_== = newMethod(AnyClass, nme.EQ, anyparam, booltype, FINAL) + lazy val Any_!= = newMethod(AnyClass, nme.NE, anyparam, booltype, FINAL) + lazy val Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype) + lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, Nil, inttype) + lazy val Any_toString = newMethod(AnyClass, nme.toString_, Nil, stringtype) + lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype, FINAL) + + // Any_getClass requires special handling. The return type is determined on + // a per-call-site basis as if the function being called were actually: + // + // // Assuming `target.getClass()` + // def getClass[T](target: T): Class[_ <: T] + // + // Since getClass is not actually a polymorphic method, this requires compiler + // participation. At the "Any" level, the return type is Class[_] as it is in + // java.lang.Object. Java also special cases the return type. + lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, Nil, getMember(ObjectClass, nme.getClass_).tpe.resultType, DEFERRED) + lazy val Any_isInstanceOf = newT1NullaryMethod(AnyClass, nme.isInstanceOf_, FINAL)(_ => booltype) + lazy val Any_asInstanceOf = newT1NullaryMethod(AnyClass, nme.asInstanceOf_, FINAL)(_.typeConstructor) + + // members of class java.lang.{ Object, String } + lazy val Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) + lazy val Object_== = newMethod(ObjectClass, nme.EQ, anyrefparam, booltype, FINAL) + lazy val Object_!= = newMethod(ObjectClass, nme.NE, anyrefparam, booltype, FINAL) + lazy val Object_eq = newMethod(ObjectClass, nme.eq, anyrefparam, booltype, FINAL) + lazy val Object_ne = newMethod(ObjectClass, nme.ne, anyrefparam, booltype, FINAL) + lazy val Object_isInstanceOf = newT1NoParamsMethod(ObjectClass, nme.isInstanceOf_Ob, FINAL | SYNTHETIC)(_ => booltype) + lazy val Object_asInstanceOf = newT1NoParamsMethod(ObjectClass, nme.asInstanceOf_Ob, FINAL | SYNTHETIC)(_.typeConstructor) + lazy val Object_synchronized = newPolyMethod(1, ObjectClass, nme.synchronized_, FINAL)(tps => + (Some(List(tps.head.typeConstructor)), tps.head.typeConstructor) + ) + lazy val String_+ = newMethod(StringClass, nme.raw.PLUS, anyparam, stringtype, FINAL) def Object_getClass = getMember(ObjectClass, nme.getClass_) def Object_clone = getMember(ObjectClass, nme.clone_) @@ -670,8 +702,6 @@ trait Definitions extends reflect.api.StandardDefinitions { def Object_hashCode = getMember(ObjectClass, nme.hashCode_) def Object_toString = getMember(ObjectClass, nme.toString_) - var String_+ : Symbol = _ - // boxed classes lazy val ObjectRefClass = getRequiredClass("scala.runtime.ObjectRef") lazy val VolatileObjectRefClass = getRequiredClass("scala.runtime.VolatileObjectRef") @@ -816,39 +846,36 @@ trait Definitions extends reflect.api.StandardDefinitions { */ private def getModuleOrClass(path: Name): Symbol = getModuleOrClass(path, path.length) - private def newCovariantPolyClass(owner: Symbol, name: TypeName, parent: Symbol => Type): Symbol = { - val clazz = newClass(owner, name, List()) - val tparam = newTypeParam(clazz, 0) setFlag COVARIANT - val p = parent(tparam) -/* p.typeSymbol.initialize - println(p.typeSymbol + " flags: " + Flags.flagsToString(p.typeSymbol.flags)) - val parents = /*if (p.typeSymbol.isTrait) - List(definitions.AnyRefClass.tpe, p) - else*/ List(p) - println("creating " + name + " with parents " + parents) */ - clazz.setInfo( - polyType( - List(tparam), - ClassInfoType(List(AnyRefClass.tpe, p), new Scope, clazz))) - } - private def newAlias(owner: Symbol, name: TypeName, alias: Type): Symbol = owner.newAliasType(name) setInfoAndEnter alias - - /** tcon receives the type parameter symbol as argument */ - private def newPolyMethod(owner: Symbol, name: TermName, tcon: Symbol => Type): Symbol = - newPolyMethodCon(owner, name, tparam => msym => tcon(tparam)) - - /** tcon receives the type parameter symbol and the method symbol as arguments */ - private def newPolyMethodCon(owner: Symbol, name: TermName, tcon: Symbol => Symbol => Type): Symbol = { - val msym = owner.info.decls enter owner.newMethod(name.encode) - val tparam = newTypeParam(msym, 0) - - msym setInfo polyType(List(tparam), tcon(tparam)(msym)) + + private def specialPolyClass(name: TypeName, flags: Long)(parentFn: Symbol => Type): Symbol = { + val clazz = newClass(ScalaPackageClass, name, Nil) + val tparam = clazz.newSyntheticTypeParam("T0", flags) + val parents = List(AnyRefClass.tpe, parentFn(tparam)) + + clazz setInfo polyType(List(tparam), ClassInfoType(parents, newScope, clazz)) } + + def newPolyMethod(typeParamCount: Int, owner: Symbol, name: TermName, flags: Long)(createFn: PolyMethodCreator): Symbol = { + val msym = owner.newMethod(name.encode, NoPosition, flags) + val tparams = msym.newSyntheticTypeParams(typeParamCount) + val mtpe = createFn(tparams) match { + case (Some(formals), restpe) => MethodType(msym.newSyntheticValueParams(formals), restpe) + case (_, restpe) => NullaryMethodType(restpe) + } - private def newTypeParam(owner: Symbol, index: Int): Symbol = - owner.newTypeParameter(newTypeName("T" + index)) setInfo TypeBounds.empty + msym setInfoAndEnter polyType(tparams, mtpe) + } + + /** T1 means one type parameter. + */ + def newT1NullaryMethod(owner: Symbol, name: TermName, flags: Long)(createFn: Symbol => Type): Symbol = { + newPolyMethod(1, owner, name, flags)(tparams => (None, createFn(tparams.head))) + } + def newT1NoParamsMethod(owner: Symbol, name: TermName, flags: Long)(createFn: Symbol => Type): Symbol = { + newPolyMethod(1, owner, name, flags)(tparams => (Some(Nil), createFn(tparams.head))) + } lazy val boxedClassValues = boxedClass.values.toSet lazy val isUnbox = unboxMethod.values.toSet @@ -864,8 +891,9 @@ trait Definitions extends reflect.api.StandardDefinitions { private lazy val boxedValueClassesSet = boxedClass.values.toSet + BoxedUnitClass /** Is symbol a value class? */ - def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) - def isNonUnitValueClass(sym: Symbol) = (sym != UnitClass) && isValueClass(sym) + def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) + def isNonUnitValueClass(sym: Symbol) = isValueClass(sym) && (sym != UnitClass) + def isSpecializableClass(sym: Symbol) = isValueClass(sym) || (sym == AnyRefClass) def isScalaValueType(tp: Type) = scalaValueClassesSet(tp.typeSymbol) /** Is symbol a boxed value class, e.g. java.lang.Integer? */ @@ -946,43 +974,7 @@ trait Definitions extends reflect.api.StandardDefinitions { RootClass.info.decls enter EmptyPackage RootClass.info.decls enter RootPackage - - // members of class scala.Any - Any_== = newMethod(AnyClass, nme.EQ, anyparam, booltype, FINAL) - Any_!= = newMethod(AnyClass, nme.NE, anyparam, booltype, FINAL) - Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype) - Any_hashCode = newMethod(AnyClass, nme.hashCode_, Nil, inttype) - Any_toString = newMethod(AnyClass, nme.toString_, Nil, stringtype) - Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype, FINAL) - - // Any_getClass requires special handling. The return type is determined on - // a per-call-site basis as if the function being called were actually: - // - // // Assuming `target.getClass()` - // def getClass[T](target: T): Class[_ <: T] - // - // Since getClass is not actually a polymorphic method, this requires compiler - // participation. At the "Any" level, the return type is Class[_] as it is in - // java.lang.Object. Java also special cases the return type. - Any_getClass = - newMethod(AnyClass, nme.getClass_, Nil, getMember(ObjectClass, nme.getClass_).tpe.resultType, DEFERRED) - Any_isInstanceOf = newPolyMethod( - AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL - Any_asInstanceOf = newPolyMethod( - AnyClass, nme.asInstanceOf_, tparam => NullaryMethodType(tparam.typeConstructor)) setFlag FINAL - - // members of class java.lang.{ Object, String } - Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) - Object_== = newMethod(ObjectClass, nme.EQ, anyrefparam, booltype, FINAL) - Object_!= = newMethod(ObjectClass, nme.NE, anyrefparam, booltype, FINAL) - Object_eq = newMethod(ObjectClass, nme.eq, anyrefparam, booltype, FINAL) - Object_ne = newMethod(ObjectClass, nme.ne, anyrefparam, booltype, FINAL) - Object_synchronized = newPolyMethodCon( - ObjectClass, nme.synchronized_, - tparam => msym => MethodType(msym.newSyntheticValueParams(List(tparam.typeConstructor)), tparam.typeConstructor)) setFlag FINAL - - String_+ = newMethod(StringClass, nme.raw.PLUS, anyparam, stringtype, FINAL) - + val forced = List( // force initialization of every symbol that is entered as a side effect AnnotationDefaultAttr, // #2264 RepeatedParamClass, @@ -995,8 +987,24 @@ trait Definitions extends reflect.api.StandardDefinitions { NothingClass, SingletonClass, EqualsPatternClass, + Any_==, + Any_!=, + Any_equals, + Any_hashCode, + Any_toString, + Any_getClass, + Any_isInstanceOf, + Any_asInstanceOf, + Any_##, + Object_eq, + Object_ne, + Object_==, + Object_!=, + Object_##, + Object_synchronized, Object_isInstanceOf, - Object_asInstanceOf + Object_asInstanceOf, + String_+ ) /** Removing the anyref parent they acquire from having a source file. diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala index 9e4f0431c3..aa696bc6e8 100644 --- a/src/compiler/scala/reflect/internal/Flags.scala +++ b/src/compiler/scala/reflect/internal/Flags.scala @@ -13,7 +13,7 @@ import scala.collection.{ mutable, immutable } // Parsers/JavaParsers and therefore definitely appear on Modifiers; but the // absence of /M on the other flags does not imply they aren't. // -// Generated by mkFlagsTable() at Mon Oct 11 10:01:09 PDT 2010 +// Generated by mkFlagsTable() at Thu Feb 02 20:31:52 PST 2012 // // 0: PROTECTED/M // 1: OVERRIDE/M @@ -30,7 +30,7 @@ import scala.collection.{ mutable, immutable } // 12: MUTABLE/M // 13: PARAM/M // 14: PACKAGE -// 15: +// 15: MACRO/M // 16: BYNAMEPARAM/M CAPTURED COVARIANT/M // 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL // 18: ABSOVERRIDE/M @@ -59,13 +59,13 @@ import scala.collection.{ mutable, immutable } // 41: DEFAULTINIT/M // 42: VBRIDGE // 43: VARARGS -// 44: +// 44: TRIEDCOOKING // 45: // 46: // 47: // 48: -// 49: latePRIVATE (eliminated) -// 50: lateABSTRACT (eliminated) +// 49: +// 50: // 51: lateDEFERRED // 52: lateFINAL // 53: lateMETHOD @@ -74,10 +74,10 @@ import scala.collection.{ mutable, immutable } // 56: notPROTECTED // 57: notOVERRIDE // 58: notPRIVATE -// 59: notABSTRACT (eliminated) -// 60: notDEFERRED (eliminated) -// 61: notFINAL (eliminated) -// 62: notMETHOD (eliminated) +// 59: +// 60: +// 61: +// 62: // 63: /** Flags set on Modifiers instances in the parsing stage. @@ -337,7 +337,7 @@ class Flags extends ModifierFlags { // ------ displaying flags -------------------------------------------------------- - // Generated by mkFlagToStringMethod() at Mon Oct 11 10:12:36 PDT 2010 + // Generated by mkFlagToStringMethod() at Thu Feb 02 20:31:52 PST 2012 @annotation.switch override def flagToString(flag: Long): String = flag match { case PROTECTED => "protected" // (1L << 0) case OVERRIDE => "override" // (1L << 1) @@ -354,7 +354,7 @@ class Flags extends ModifierFlags { case MUTABLE => "<mutable>" // (1L << 12) case PARAM => "<param>" // (1L << 13) case PACKAGE => "<package>" // (1L << 14) - case MACRO => "macro" // (1L << 15) + case MACRO => "<macro>" // (1L << 15) case BYNAMEPARAM => "<bynameparam/captured/covariant>" // (1L << 16) case CONTRAVARIANT => "<contravariant/inconstructor/label>" // (1L << 17) case ABSOVERRIDE => "absoverride" // (1L << 18) @@ -383,13 +383,13 @@ class Flags extends ModifierFlags { case DEFAULTINIT => "<defaultinit>" // (1L << 41) case VBRIDGE => "<vbridge>" // (1L << 42) case VARARGS => "<varargs>" // (1L << 43) - case 0x100000000000L => "" // (1L << 44) + case TRIEDCOOKING => "<triedcooking>" // (1L << 44) case 0x200000000000L => "" // (1L << 45) case 0x400000000000L => "" // (1L << 46) case 0x800000000000L => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) - // case `latePRIVATE` => "<lateprivate>" // (1L << 49) - // case `lateABSTRACT` => "<lateabstract>" // (1L << 50) + case 0x2000000000000L => "" // (1L << 49) + case 0x4000000000000L => "" // (1L << 50) case `lateDEFERRED` => "<latedeferred>" // (1L << 51) case `lateFINAL` => "<latefinal>" // (1L << 52) case `lateMETHOD` => "<latemethod>" // (1L << 53) @@ -398,10 +398,10 @@ class Flags extends ModifierFlags { case `notPROTECTED` => "<notprotected>" // (1L << 56) case `notOVERRIDE` => "<notoverride>" // (1L << 57) case `notPRIVATE` => "<notprivate>" // (1L << 58) - // case `notABSTRACT` => "<notabstract>" // (1L << 59) - // case `notDEFERRED` => "<notdeferred>" // (1L << 60) - // case `notFINAL` => "<notfinal>" // (1L << 61) - // case `notMETHOD` => "<notmethod>" // (1L << 62) + case 0x800000000000000L => "" // (1L << 59) + case 0x1000000000000000L => "" // (1L << 60) + case 0x2000000000000000L => "" // (1L << 61) + case 0x4000000000000000L => "" // (1L << 62) case 0x8000000000000000L => "" // (1L << 63) case _ => "" } @@ -466,7 +466,7 @@ class Flags extends ModifierFlags { } protected final val rawFlagPickledOrder: Array[Long] = pickledListOrder.toArray - def flagOfModifier(mod: Modifier.Value): Long = mod match { + def flagOfModifier(mod: Modifier): Long = mod match { case Modifier.`protected` => PROTECTED case Modifier.`private` => PRIVATE case Modifier.`override` => OVERRIDE @@ -496,13 +496,13 @@ class Flags extends ModifierFlags { case Modifier.bynameParameter => BYNAMEPARAM } - def flagsOfModifiers(mods: List[Modifier.Value]): Long = + def flagsOfModifiers(mods: List[Modifier]): Long = (mods :\ 0L) { (mod, curr) => curr | flagOfModifier(mod) } - def modifierOfFlag(flag: Long): Option[Modifier.Value] = + def modifierOfFlag(flag: Long): Option[Modifier] = Modifier.values find { mod => flagOfModifier(mod) == flag } - def modifiersOfFlags(flags: Long): List[Modifier.Value] = + def modifiersOfFlags(flags: Long): List[Modifier] = pickledListOrder map (mask => modifierOfFlag(flags & mask)) flatMap { mod => mod } } diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index 53380952c0..1ae4f755ed 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -9,16 +9,34 @@ trait Importers { self: SymbolTable => val from: SymbolTable lazy val symMap: WeakHashMap[from.Symbol, Symbol] = new WeakHashMap + lazy val tpeMap: WeakHashMap[from.Type, Type] = new WeakHashMap + + // fixups and maps prevent stackoverflows in importer + var pendingSyms = 0 + var pendingTpes = 0 + lazy val fixups = collection.mutable.MutableList[Function0[Unit]]() + def addFixup(fixup: => Unit): Unit = fixups += (() => fixup) + def tryFixup(): Unit = { + if (pendingSyms == 0 && pendingTpes == 0) { + val fixups = this.fixups.toList + this.fixups.clear() + fixups foreach { _() } + } + } object reverse extends from.Importer { val from: self.type = self for ((fromsym, mysym) <- Importer.this.symMap) symMap += ((mysym, fromsym)) + for ((fromtpe, mytpe) <- Importer.this.tpeMap) tpeMap += ((mytpe, fromtpe)) } def importPosition(pos: from.Position): Position = NoPosition - def importSymbol(sym: from.Symbol): Symbol = { + def importSymbol(sym0: from.Symbol): Symbol = { def doImport(sym: from.Symbol): Symbol = { + if (symMap.contains(sym)) + return symMap(sym) + val myowner = importSymbol(sym.owner) val mypos = importPosition(sym.pos) val myname = importName(sym.name).toTermName @@ -32,7 +50,7 @@ trait Importers { self: SymbolTable => case x: from.MethodSymbol => linkReferenced(myowner.newMethod(myname, mypos, myflags), x, importSymbol) case x: from.ModuleSymbol => - linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, doImport) + linkReferenced(myowner.newModuleSymbol(myname, mypos, myflags), x, importSymbol) case x: from.FreeVar => newFreeVar(importName(x.name).toTermName, importType(x.tpe), x.value, myflags) case x: from.TermSymbol => @@ -44,14 +62,14 @@ trait Importers { self: SymbolTable => case y: from.Symbol => importSymbol(y) } myowner.newTypeSkolemSymbol(myname.toTypeName, origin, mypos, myflags) - /* - case x: from.ModuleClassSymbol => - val mysym = new ModuleClassSymbol(myowner, mypos, myname.toTypeName) - mysym.sourceModule = importSymbol(x.sourceModule) - mysym -*/ + case x: from.ModuleClassSymbol => + val mysym = myowner.newModuleClassSymbol(myname.toTypeName, mypos, myflags) + symMap(x) = mysym + mysym.sourceModule = importSymbol(x.sourceModule) + mysym case x: from.ClassSymbol => val mysym = myowner.newClassSymbol(myname.toTypeName, mypos, myflags) + symMap(x) = mysym if (sym.thisSym != sym) { mysym.typeOfThis = importType(sym.typeOfThis) mysym.thisSym.name = importName(sym.thisSym.name) @@ -63,7 +81,7 @@ trait Importers { self: SymbolTable => symMap(sym) = mysym mysym setFlag Flags.LOCKED mysym setInfo { - val mytypeParams = sym.typeParams map doImport + val mytypeParams = sym.typeParams map importSymbol new LazyPolyType(mytypeParams) { override def complete(s: Symbol) { val result = sym.info match { @@ -78,7 +96,8 @@ trait Importers { self: SymbolTable => mysym resetFlag Flags.LOCKED } // end doImport - def importOrRelink: Symbol = + def importOrRelink: Symbol = { + val sym = sym0 // makes sym visible in the debugger if (sym == null) null else if (sym == from.NoSymbol) @@ -86,116 +105,160 @@ trait Importers { self: SymbolTable => else if (sym.isRoot) definitions.RootClass else { - val myowner = importSymbol(sym.owner) - val myname = importName(sym.name) - if (sym.isModuleClass) { - assert(sym.sourceModule != NoSymbol, sym) - val mymodule = importSymbol(sym.sourceModule) - assert(mymodule != NoSymbol, sym) - assert(mymodule.moduleClass != NoSymbol, mymodule) - mymodule.moduleClass - } else if (myowner.isClass && !myowner.isRefinementClass && !(myowner hasFlag Flags.LOCKED) && sym.owner.info.decl(sym.name).exists) { - // symbol is in class scope, try to find equivalent one in local scope - if (sym.isOverloaded) - myowner.newOverloaded(myowner.thisType, sym.alternatives map importSymbol) - else { - var existing: Symbol = myowner.info.decl(myname) - if (existing.isOverloaded) { - existing = - if (sym.isMethod) { - val localCopy = doImport(sym) - existing filter (_.tpe matches localCopy.tpe) - } else { - existing filter (!_.isMethod) - } - assert(!existing.isOverloaded, - "import failure: cannot determine unique overloaded method alternative from\n "+ - (existing.alternatives map (_.defString) mkString "\n")+"\n that matches "+sym+":"+sym.tpe) + 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.isPackageClass || 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.isPackageClass || 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) } - if (existing != NoSymbol) existing - else { + 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) - assert(myowner.info.decls.lookup(myname) == NoSymbol, myname+" "+myowner.info.decl(myname)+" "+existing) - myowner.info.decls enter mysym + + if (myscope != NoType) { + assert(myowner.info.decls.lookup(myname) == NoSymbol, myname+" "+myowner.info.decl(myname)+" "+myexisting) + myowner.info.decls enter mysym + } + mysym } } - } 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 - doImport(sym) + } + + mysym } - symMap getOrElseUpdate (sym, importOrRelink) + } // end importOrRelink + + val sym = sym0 + if (symMap contains sym) { + symMap(sym) + } else { + pendingSyms += 1 + + try { + symMap getOrElseUpdate (sym, importOrRelink) + } finally { + pendingSyms -= 1 + tryFixup() + } + } } - def importType(tpe: from.Type): Type = 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(from.Constant(value)) => - ConstantType(Constant(value)) - 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 polyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope - decls foreach importSymbol // will enter itself into myclazz - myclazzTpe - case from.RefinedType(parents, decls) => - RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol)) - case from.ExistentialType(tparams, restpe) => - newExistentialType(tparams map importSymbol, importType(restpe)) - case from.OverloadedType(pre, alts) => - OverloadedType(importType(pre), alts map importSymbol) - case from.AntiPolyType(pre, targs) => - AntiPolyType(importType(pre), targs map importType) - case x: from.TypeVar => - TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) - case from.NotNullType(tpe) => - NotNullType(importType(tpe)) - case from.AnnotatedType(annots, tpe, selfsym) => - AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym)) - case from.ErrorType => - ErrorType - case from.WildcardType => - WildcardType - case from.NoType => - NoType - case from.NoPrefix => - NoPrefix - case null => - null + 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 polyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope + decls foreach importSymbol // will enter itself into myclazz + myclazzTpe + case from.RefinedType(parents, decls) => + RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol)) + case from.ExistentialType(tparams, restpe) => + newExistentialType(tparams map importSymbol, importType(restpe)) + case from.OverloadedType(pre, alts) => + OverloadedType(importType(pre), alts map importSymbol) + case from.AntiPolyType(pre, targs) => + AntiPolyType(importType(pre), targs map importType) + case x: from.TypeVar => + TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) + case from.NotNullType(tpe) => + NotNullType(importType(tpe)) + case from.AnnotatedType(annots, tpe, selfsym) => + AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym)) + case from.ErrorType => + ErrorType + case from.WildcardType => + WildcardType + case from.NoType => + NoType + case from.NoPrefix => + NoPrefix + case null => + null + } // end doImport + + def importOrRelink: Type = + doImport(tpe) + + if (tpeMap contains tpe) { + tpeMap(tpe) + } else { + pendingTpes += 1 + + try { + tpeMap getOrElseUpdate (tpe, importOrRelink) + } finally { + pendingTpes -= 1 + tryFixup() + } + } } def importTypeBounds(bounds: from.TypeBounds) = importType(bounds).asInstanceOf[TypeBounds] - def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = - AnnotationInfo(importType(ann.atp), ann.args map importTree, ann.assocs map { - case (name, arg) => (importName(name), importAnnotArg(arg)) - }) + def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = { + val atp1 = importType(ann.atp) + val args1 = ann.args map importTree + val assocs1 = ann.assocs map { case (name, arg) => (importName(name), importAnnotArg(arg)) } + val original1 = importTree(ann.original) + AnnotationInfo(atp1, args1, assocs1) setOriginal original1 + } def importAnnotArg(arg: from.ClassfileAnnotArg): ClassfileAnnotArg = arg match { - case from.LiteralAnnotArg(from.Constant(value)) => - LiteralAnnotArg(Constant(value)) + case from.LiteralAnnotArg(constant @ from.Constant(_)) => + LiteralAnnotArg(importConstant(constant)) case from.ArrayAnnotArg(args) => ArrayAnnotArg(args map importAnnotArg) case from.ScalaSigBytes(bytes) => @@ -210,9 +273,9 @@ trait Importers { self: SymbolTable => result } - // !!! todo: override to vcater for PackageScopes + // !!! todo: override to cater for PackageScopes def importScope(decls: from.Scope): Scope = - new Scope(decls.toList map importSymbol) + newScopeWith(decls.toList map importSymbol: _*) def importName(name: from.Name): Name = if (name.isTypeName) newTypeName(name.toString) else newTermName(name.toString) @@ -223,7 +286,7 @@ trait Importers { self: SymbolTable => new Modifiers(mods.flags, importName(mods.privateWithin), mods.annotations map importTree) def importImportSelector(sel: from.ImportSelector): ImportSelector = - new ImportSelector(importName(sel.name), sel.namePos, importName(sel.rename), sel.renamePos) + 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 { @@ -265,6 +328,8 @@ trait Importers { self: SymbolTable => 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) => @@ -303,8 +368,8 @@ trait Importers { self: SymbolTable => case _ => new Ident(importName(name)) } - case from.Literal(from.Constant(value)) => - new Literal(Constant(value)) + case from.Literal(constant @ from.Constant(_)) => + new Literal(importConstant(constant)) case from.TypeTree() => new TypeTree() case from.Annotated(annot, arg) => @@ -326,10 +391,24 @@ trait Importers { self: SymbolTable => case null => null } - if (mytree != null) { - if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol) - mytree.tpe = importType(tree.tpe) - } + 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 } @@ -339,5 +418,10 @@ trait Importers { self: SymbolTable => def importRefTree(tree: from.RefTree): RefTree = importTree(tree).asInstanceOf[RefTree] def importIdent(tree: from.Ident): Ident = importTree(tree).asInstanceOf[Ident] def importCaseDef(tree: from.CaseDef): CaseDef = importTree(tree).asInstanceOf[CaseDef] + def importConstant(constant: from.Constant): Constant = new Constant(constant.tag match { + case ClassTag => importType(constant.value.asInstanceOf[from.Type]) + case EnumTag => importSymbol(constant.value.asInstanceOf[from.Symbol]) + case _ => constant.value + }) } -} +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/InfoTransformers.scala b/src/compiler/scala/reflect/internal/InfoTransformers.scala index 9c54b1b4cd..96d9d8f076 100644 --- a/src/compiler/scala/reflect/internal/InfoTransformers.scala +++ b/src/compiler/scala/reflect/internal/InfoTransformers.scala @@ -9,6 +9,8 @@ package internal trait InfoTransformers { self: SymbolTable => + /* Syncnote: This should not need to be protected, as reflection does not run in multiple phases. + */ abstract class InfoTransformer { var prev: InfoTransformer = this var next: InfoTransformer = this diff --git a/src/compiler/scala/reflect/internal/Names.scala b/src/compiler/scala/reflect/internal/Names.scala index 907b564d4c..e6ca4c49ba 100644 --- a/src/compiler/scala/reflect/internal/Names.scala +++ b/src/compiler/scala/reflect/internal/Names.scala @@ -387,6 +387,13 @@ trait Names extends api.Names { * decode returns a String. */ + /** !!! Duplicative but consistently named. + */ + def decoded: String = decode + def encoded: String = "" + encode + // def decodedName: ThisNameType = newName(decoded) + def encodedName: ThisNameType = encode + /** Replace operator symbols by corresponding $op_name. */ def encode: ThisNameType = { val str = toString diff --git a/src/compiler/scala/reflect/internal/Scopes.scala b/src/compiler/scala/reflect/internal/Scopes.scala index fb3012adff..37464ebf29 100644 --- a/src/compiler/scala/reflect/internal/Scopes.scala +++ b/src/compiler/scala/reflect/internal/Scopes.scala @@ -37,9 +37,18 @@ trait Scopes extends api.Scopes { self: SymbolTable => def unapplySeq(decls: Scope): Some[Seq[Symbol]] = Some(decls.toList) } - class Scope(initElems: ScopeEntry) extends Iterable[Symbol] { + /** Note: constructor is protected to force everyone to use the factory methods newScope or newNestedScope instead. + * This is necessary because when run from reflection every scope needs to have a + * SynchronizedScope as mixin. + */ + class Scope protected[Scopes] (initElems: ScopeEntry = null) extends Iterable[Symbol] { + + protected[Scopes] def this(base: Scope) = { + this(base.elems) + nestinglevel = base.nestinglevel + 1 + } - var elems: ScopeEntry = initElems + private[scala] var elems: ScopeEntry = initElems /** The number of times this scope is nested in another */ @@ -65,20 +74,8 @@ trait Scopes extends api.Scopes { self: SymbolTable => if (size >= MIN_HASH) createHash() - def this() = this(null: ScopeEntry) - - def this(base: Scope) = { - this(base.elems) - nestinglevel = base.nestinglevel + 1 - } - - def this(decls: List[Symbol]) = { - this() - decls foreach enter - } - /** Returns a new scope with the same content as this one. */ - def cloneScope: Scope = new Scope(this.toList) + def cloneScope: Scope = newScopeWith(this.toList: _*) /** is the scope empty? */ override def isEmpty: Boolean = elems eq null @@ -123,7 +120,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => * @param sym ... */ def enterUnique(sym: Symbol) { - assert(lookup(sym.name) == NoSymbol) + assert(lookup(sym.name) == NoSymbol, (sym.fullLocationString, lookup(sym.name).fullLocationString)) enter(sym) } @@ -311,7 +308,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => override def foreach[U](p: Symbol => U): Unit = toList foreach p override def filter(p: Symbol => Boolean): Scope = - if (!(toList forall p)) new Scope(toList filter p) else this + if (!(toList forall p)) newScopeWith(toList filter p: _*) else this override def mkString(start: String, sep: String, end: String) = toList.map(_.defString).mkString(start, sep, end) @@ -321,21 +318,26 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** Create a new scope */ - def newScope: Scope = new Scope + def newScope: Scope = new Scope() + + /** Create a new scope nested in another one with which it shares its elements */ + def newNestedScope(outer: Scope): Scope = new Scope(outer) + + /** Create a new scope with given initial elements */ + def newScopeWith(elems: Symbol*): Scope = { + val scope = newScope + elems foreach scope.enter + scope + } /** Create new scope for the members of package `pkg` */ - def newPackageScope(pkgClass: Symbol): Scope = new Scope + def newPackageScope(pkgClass: Symbol): Scope = newScope /** Transform scope of members of `owner` using operation `op` * This is overridden by the reflective compiler to avoid creating new scopes for packages */ def scopeTransform(owner: Symbol)(op: => Scope): Scope = op - def newScopeWith(elems: Symbol*): Scope = { - val scope = newScope - elems foreach scope.enter - scope - } /** The empty scope (immutable). */ @@ -347,7 +349,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** The error scope. */ - class ErrorScope(owner: Symbol) extends Scope(null: ScopeEntry) + class ErrorScope(owner: Symbol) extends Scope private final val maxRecursions = 1000 diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index aba00088f9..1f67bbc0ac 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -153,6 +153,9 @@ trait StdNames extends NameManglers { self: SymbolTable => final val ClassfileAnnotation: NameType = "ClassfileAnnotation" final val Enum: NameType = "Enum" + final val Tree: NameType = "Tree" + final val TypeTree: NameType = "TypeTree" + // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" final val BooleanBeanPropertyAnnot: NameType = "BooleanBeanProperty" @@ -272,6 +275,8 @@ trait StdNames extends NameManglers { self: SymbolTable => // val productElementName: NameType = "productElementName" val Ident: NameType = "Ident" val StringContext: NameType = "StringContext" + val This: NameType = "This" + val Tree : NameType = "Tree" val TYPE_ : NameType = "TYPE" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" @@ -291,6 +296,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val array_update : NameType = "array_update" val arraycopy: NameType = "arraycopy" val asInstanceOf_ : NameType = "asInstanceOf" + val asInstanceOf_Ob : NameType = "$asInstanceOf" val asTypeConstructor: NameType = "asTypeConstructor" val assert_ : NameType = "assert" val assume_ : NameType = "assume" @@ -301,8 +307,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val classOf: NameType = "classOf" val clone_ : NameType = if (forMSIL) "MemberwiseClone" else "clone" // sn.OClone causes checkinit failure val conforms: NameType = "conforms" - val context : NameType = "_context" - val contextImplicit : NameType = "$context" val copy: NameType = "copy" val delayedInit: NameType = "delayedInit" val delayedInitArg: NameType = "delayedInit$body" @@ -336,12 +340,15 @@ trait StdNames extends NameManglers { self: SymbolTable => val isDefinedAt: NameType = "isDefinedAt" val isEmpty: NameType = "isEmpty" val isInstanceOf_ : NameType = "isInstanceOf" + val isInstanceOf_Ob : NameType = "$isInstanceOf" val java: NameType = "java" val lang: NameType = "lang" val length: NameType = "length" val lengthCompare: NameType = "lengthCompare" val lift_ : NameType = "lift" val macro_ : NameType = "macro" + val macroThis : NameType = "_this" + val macroContext : NameType = "_context" val main: NameType = "main" val map: NameType = "map" val mirror : NameType = "mirror" @@ -366,7 +373,9 @@ trait StdNames extends NameManglers { self: SymbolTable => val self: NameType = "self" val setAccessible: NameType = "setAccessible" val setAnnotations: NameType = "setAnnotations" - val setTypeSig: NameType = "setTypeSig" + val setSymbol: NameType = "setSymbol" + val setType: NameType = "setType" + val setTypeSignature: NameType = "setTypeSignature" val synchronized_ : NameType = "synchronized" val tail: NameType = "tail" val thisModuleType: NameType = "thisModuleType" @@ -424,7 +433,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val toInteger: NameType = "toInteger" } - object tpnme extends TypeNames /*with LibraryTypeNames*/ with TypeNameMangling { + object tpnme extends AbsTypeNames with TypeNames /*with LibraryTypeNames*/ with TypeNameMangling { type NameType = TypeName protected implicit def createNameType(name: String): TypeName = newTypeNameCached(name) @@ -461,7 +470,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val javanme = nme.javaKeywords - object nme extends TermNames /*with LibraryTermNames*/ with TermNameMangling { + object nme extends AbsTermNames with TermNames /*with LibraryTermNames*/ with TermNameMangling { type NameType = TermName protected implicit def createNameType(name: String): TermName = newTermNameCached(name) @@ -708,7 +717,6 @@ trait StdNames extends NameManglers { self: SymbolTable => val BoxedCharacter : TypeName val BoxedNumber : TypeName val Class : TypeName - val Code : TypeName val Delegate : TypeName val IOOBException : TypeName // IndexOutOfBoundsException val InvTargetException : TypeName // InvocationTargetException @@ -843,7 +851,6 @@ trait StdNames extends NameManglers { self: SymbolTable => final val BoxedCharacter: TypeName = "System.IConvertible" final val BoxedNumber: TypeName = "System.IConvertible" final val Class: TypeName = "System.Type" - final val Code: TypeName = tpnme.NO_NAME final val Delegate: TypeName = "System.MulticastDelegate" final val IOOBException: TypeName = "System.IndexOutOfRangeException" final val InvTargetException: TypeName = "System.Reflection.TargetInvocationException" @@ -877,7 +884,6 @@ trait StdNames extends NameManglers { self: SymbolTable => private class J2SENames extends JavaNames { final val BeanProperty: TypeName = "scala.beans.BeanProperty" final val BooleanBeanProperty: TypeName = "scala.beans.BooleanBeanProperty" - final val Code: TypeName = "scala.reflect.Code" final val JavaSerializable: TypeName = "java.io.Serializable" } diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index ace4d55b90..1973a97279 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -33,12 +33,17 @@ abstract class SymbolTable extends api.Universe { def rootLoader: LazyType def log(msg: => AnyRef): Unit - def abort(msg: String): Nothing = throw new FatalError(msg) + def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) + + @deprecated("2.10.0", "Give us a reason") def abort(): Nothing = abort("unknown error") /** Override with final implementation for inlining. */ def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg) def debugwarn(msg: => String): Unit = if (settings.debug.value) Console.err.println(msg) + + /** Overridden when we know more about what was happening during a failure. */ + def supplementErrorMessage(msg: String): String = msg private[scala] def printResult[T](msg: String)(result: T) = { Console.err.println(msg + ": " + result) @@ -115,9 +120,11 @@ abstract class SymbolTable extends api.Universe try op finally phase = current } - - @inline final def afterPhase[T](ph: Phase)(op: => T): T = - atPhase(ph.next)(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 atPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (target != NoPhase && phase.id > target.id) atPhase(target)(op) else op @@ -271,4 +278,9 @@ abstract class SymbolTable extends api.Universe /** The phase which has given index as identifier. */ val phaseWithId: Array[Phase] + + /** Is this symbol table part of reflexive mirror? In this case + * operations need to be made thread safe. + */ + def inReflexiveMirror = false } diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index a943b6fe24..77ed2f6a1b 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -16,10 +16,13 @@ import api.Modifier trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ - private var ids = 0 + protected var ids = 0 + + val emptySymbolArray = new Array[Symbol](0) + def symbolCount = ids // statistics - val emptySymbolArray = new Array[Symbol](0) + protected def nextId() = { ids += 1; ids } /** Used for deciding in the IDE whether we can interrupt the compiler */ //protected var activeLocks = 0 @@ -31,7 +34,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private var recursionTable = immutable.Map.empty[Symbol, Int] private var nextexid = 0 - private def freshExistentialName(suffix: String) = { + protected def freshExistentialName(suffix: String) = { nextexid += 1 newTypeName("_" + nextexid + suffix) } @@ -42,6 +45,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => m setModuleClass moduleClass m } + /** Create a new free variable. Its owner is NoSymbol. */ def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = @@ -57,18 +61,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => case n: TermName => newTermSymbol(n, pos, newFlags) case n: TypeName => newTypeSymbol(n, pos, newFlags) } - def typeSig: Type = info - def typeSigIn(site: Type): Type = site.memberInfo(this) + def enclosingClass: Symbol = enclClass + def enclosingMethod: Symbol = enclMethod + def thisPrefix: Type = thisType + def selfType: Type = typeOfThis + def typeSignature: Type = info + def typeSignatureIn(site: Type): Type = site memberInfo this + def asType: Type = tpe def asTypeIn(site: Type): Type = site.memberType(this) def asTypeConstructor: Type = typeConstructor def setInternalFlags(flag: Long): this.type = { setFlag(flag); this } - def setTypeSig(tpe: Type): this.type = { setInfo(tpe); this } + def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this } def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } } /** The class for all symbols */ - abstract class Symbol(initOwner: Symbol, initPos: Position, initName: Name) + abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name) extends AbsSymbolImpl with HasFlags with Annotatable[Symbol] { @@ -77,24 +86,39 @@ trait Symbols extends api.Symbols { self: SymbolTable => type AccessBoundaryType = Symbol type AnnotationType = AnnotationInfo - var rawowner = initOwner - var rawname = initName - var rawflags = 0L - + private[this] var _rawowner = initOwner // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api + private[this] var _rawname = initName + private[this] var _rawflags = 0L + + def rawowner = _rawowner + def rawname = _rawname + def rawflags = _rawflags + + protected def rawflags_=(x: FlagsType) { _rawflags = x } + private var rawpos = initPos - val id = { ids += 1; ids } // identity displayed when -uniqid + + val id = nextId() // identity displayed when -uniqid - var validTo: Period = NoPeriod + private[this] var _validTo: Period = NoPeriod + + def validTo = _validTo + def validTo_=(x: Period) { _validTo = x} def pos = rawpos def setPos(pos: Position): this.type = { this.rawpos = pos; this } - override def hasModifier(mod: Modifier.Value) = + /** !!! The logic after "hasFlag" is far too opaque to be unexplained. + * I'm guessing it's attempting to compensate for flag overloading, + * and embedding such logic in an undocumented island like this is a + * notarized guarantee of future breakage. + */ + override def hasModifier(mod: Modifier) = hasFlag(flagOfModifier(mod)) && (!(mod == Modifier.bynameParameter) || isTerm) && (!(mod == Modifier.covariant) || isType) - override def allModifiers: Set[Modifier.Value] = + override def modifiers: Set[Modifier] = Modifier.values filter hasModifier // ------ creators ------------------------------------------------------------------- @@ -237,6 +261,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } mmap(argtypess)(tp => newValueParameter(freshName(), focusPos(owner.pos), SYNTHETIC) setInfo tp) } + + def newSyntheticTypeParam(): Symbol = newSyntheticTypeParam("T0", 0L) + def newSyntheticTypeParam(name: String, newFlags: Long): Symbol = newTypeParameter(newTypeName(name), NoPosition, newFlags) setInfo TypeBounds.empty + def newSyntheticTypeParams(num: Int): List[Symbol] = (0 until num).toList map (n => newSyntheticTypeParam("T" + n, 0L)) /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. @@ -336,7 +364,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // True if the symbol is unlocked. // True if the symbol is locked but still below the allowed recursion depth. // False otherwise - def lockOK: Boolean = { + private[scala] def lockOK: Boolean = { ((rawflags & LOCKED) == 0L) || ((settings.Yrecursion.value != 0) && (recursionTable get this match { @@ -345,33 +373,37 @@ trait Symbols extends api.Symbols { self: SymbolTable => } // Lock a symbol, using the handler if the recursion depth becomes too great. - def lock(handler: => Unit) = { + private[scala] def lock(handler: => Unit): Boolean = { if ((rawflags & LOCKED) != 0L) { if (settings.Yrecursion.value != 0) { recursionTable get this match { case Some(n) => if (n > settings.Yrecursion.value) { handler + false } else { recursionTable += (this -> (n + 1)) + true } case None => recursionTable += (this -> 1) + true } - } else { handler } + } else { handler; false } } else { rawflags |= LOCKED + true // activeLocks += 1 // lockedSyms += this } } // Unlock a symbol - def unlock() = { + private[scala] def unlock() = { if ((rawflags & LOCKED) != 0L) { // activeLocks -= 1 // lockedSyms -= this - rawflags = rawflags & ~LOCKED + _rawflags = rawflags & ~LOCKED if (settings.Yrecursion.value != 0) recursionTable -= this } @@ -736,7 +768,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ owner attribute -------------------------------------------------------------- def owner: Symbol = rawowner - final def owner_=(owner: Symbol) { + 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). @@ -744,8 +776,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (originalOwner contains this) () else originalOwner(this) = rawowner } - - rawowner = owner + assert(!inReflexiveMirror, "owner_= is not thread-safe; cannot be run in reflexive code") + _rawowner = owner } def ownerChain: List[Symbol] = this :: owner.ownerChain @@ -778,7 +810,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def name: Name = rawname - final def name_=(name: Name) { + def name_=(name: Name) { if (name != rawname) { if (owner.isClass) { var ifs = owner.infos @@ -787,7 +819,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => ifs = ifs.prev } } - rawname = name + _rawname = name } } @@ -855,20 +887,20 @@ trait Symbols extends api.Symbols { self: SymbolTable => val fs = rawflags & phase.flagMask (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) } - final def flags_=(fs: Long) = rawflags = fs + def flags_=(fs: Long) = _rawflags = fs /** Set the symbol's flags to the given value, asserting * that the previous value was 0. */ def initFlags(mask: Long): this.type = { assert(rawflags == 0L, this) - rawflags = mask + _rawflags = mask this } - def setFlag(mask: Long): this.type = { rawflags = rawflags | mask ; this } - def resetFlag(mask: Long): this.type = { rawflags = rawflags & ~mask ; this } + def setFlag(mask: Long): this.type = { _rawflags = rawflags | mask ; this } + def resetFlag(mask: Long): this.type = { _rawflags = rawflags & ~mask ; this } final def getFlag(mask: Long): Long = flags & mask - final def resetFlags() { rawflags = rawflags & TopLevelCreationFlags } + final def resetFlags() { _rawflags = rawflags & TopLevelCreationFlags } /** Does symbol have ANY flag in `mask` set? */ final def hasFlag(mask: Long): Boolean = (flags & mask) != 0L @@ -954,7 +986,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => throw CyclicReference(this, tp) } } else { - rawflags |= LOCKED + _rawflags |= LOCKED // activeLocks += 1 // lockedSyms += this } @@ -963,7 +995,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => phase = phaseOf(infos.validFrom) tp.complete(this) } finally { - // if (id == 431) println("completer ran "+tp.getClass+" for "+fullName) unlock() phase = current } @@ -984,7 +1015,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(info ne null) infos = TypeHistory(currentPeriod, info, null) unlock() - validTo = if (info.isComplete) currentPeriod else NoPeriod + _validTo = if (info.isComplete) currentPeriod else NoPeriod } /** Set initial info. */ @@ -1003,11 +1034,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** Set new info valid from start of this phase. */ - final def updateInfo(info: Type): Symbol = { + def updateInfo(info: Type): Symbol = { assert(phaseId(infos.validFrom) <= phase.id) if (phaseId(infos.validFrom) == phase.id) infos = infos.prev infos = TypeHistory(currentPeriod, info, infos) - validTo = if (info.isComplete) currentPeriod else NoPeriod + _validTo = if (info.isComplete) currentPeriod else NoPeriod this } @@ -1045,11 +1076,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => infos = TypeHistory(currentPeriod + 1, info1, infos) this.infos = infos } - validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform + _validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform itr = itr.next } - validTo = if (itr.pid == NoPhase.id) curPeriod - else period(currentRunId, itr.pid) + _validTo = if (itr.pid == NoPhase.id) curPeriod + else period(currentRunId, itr.pid) } } finally { phase = current @@ -1060,7 +1091,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => } // adapt to new run in fsc. - private def adaptInfos(infos: TypeHistory): TypeHistory = + private def adaptInfos(infos: TypeHistory): TypeHistory = { + assert(!inReflexiveMirror) if (infos == null || runId(infos.validFrom) == currentRunId) { infos } else { @@ -1069,7 +1101,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else { val pid = phaseId(infos.validFrom) - validTo = period(currentRunId, pid) + _validTo = period(currentRunId, pid) phase = phaseWithId(pid) val info1 = ( @@ -1085,6 +1117,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } } + } /** Initialize the symbol */ final def initialize: this.type = { @@ -1094,6 +1127,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Was symbol's type updated during given phase? */ final def isUpdatedAt(pid: Phase#Id): Boolean = { + assert(!inReflexiveMirror) var infos = this.infos while ((infos ne null) && phaseId(infos.validFrom) != pid + 1) infos = infos.prev infos ne null @@ -1101,6 +1135,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Was symbol's type updated during given phase? */ final def hasTypeAt(pid: Phase#Id): Boolean = { + assert(!inReflexiveMirror) var infos = this.infos while ((infos ne null) && phaseId(infos.validFrom) > pid) infos = infos.prev infos ne null @@ -1212,7 +1247,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def reset(completer: Type) { resetFlags() infos = null - validTo = NoPeriod + _validTo = NoPeriod //limit = NoPhase.id setInfo(completer) } @@ -1239,14 +1274,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ----- annotations ------------------------------------------------------------ // null is a marker that they still need to be obtained. - private var _annotations: List[AnnotationInfo] = Nil + private[this] var _annotations: List[AnnotationInfo] = Nil def annotationsString = if (annotations.isEmpty) "" else annotations.mkString("(", ", ", ")") /** After the typer phase (before, look at the definition's Modifiers), contains * the annotations attached to member a definition (class, method, type, field). */ - def annotations: List[AnnotationInfo] = _annotations + def annotations: List[AnnotationInfo] = { + // Necessary for reflection, see SI-5423 + if (inReflexiveMirror) + initialize + + _annotations + } + def setAnnotations(annots: List[AnnotationInfo]): this.type = { _annotations = annots this @@ -1536,11 +1578,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isMethod || isClass) this else owner.logicallyEnclosingMember + /** Kept for source compatibility with 2.9. Scala IDE for Eclipse relies on this. */ + @deprecated("Use enclosingTopLevelClass") + def toplevelClass: Symbol = enclosingTopLevelClass + /** The top-level class containing this symbol. */ - def toplevelClass: Symbol = + def enclosingTopLevelClass: Symbol = if (owner.isPackageClass) { if (isClass) this else moduleClass - } else owner.toplevelClass + } else owner.enclosingTopLevelClass /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ def isCoDefinedWith(that: Symbol) = ( @@ -1658,6 +1704,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * (which is always the interface, by convention) * - before erasure, it looks up the interface name in the scope of the owner of the class. * This only works for implementation classes owned by other classes or traits. + * !!! Why? */ final def toInterface: Symbol = if (isImplClass) { @@ -1807,6 +1854,18 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } + /** Remove any access boundary and clear flags PROTECTED | PRIVATE. + */ + def makePublic = this setPrivateWithin NoSymbol resetFlag AccessFlags + + /** The first parameter to the first argument list of this method, + * or NoSymbol if inapplicable. + */ + def firstParam = info.params match { + case p :: _ => p + case _ => NoSymbol + } + /** change name by appending $$<fully-qualified-name-of-class `base`> * Do the same for any accessed symbols or setters/getters */ @@ -1832,7 +1891,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def sourceFile: AbstractFileType = if (isModule) moduleClass.sourceFile - else toplevelClass.sourceFile + else enclosingTopLevelClass.sourceFile def sourceFile_=(f: AbstractFileType) { abort("sourceFile_= inapplicable for " + this) @@ -1877,36 +1936,43 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isTerm && (!isParameter || isParamAccessor)) "val" else "" + private case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) + private def symbolKind: SymbolKind = { + val kind = + if (isInstanceOf[FreeVar]) ("free variable", "free variable", "FV") + else if (isPackage) ("package", "package", "PK") + else if (isPackageClass) ("package class", "package", "PKC") + else if (isPackageObject) ("package object", "package", "PKO") + else if (isPackageObjectClass) ("package object class", "package", "PKOC") + else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") + else if (isRefinementClass) ("refinement class", "", "RC") + else if (isModule) ("module", "object", "MOD") + else if (isModuleClass) ("module class", "object", "MODC") + else if (isGetter) ("getter", if (isSourceMethod) "method" else "value", "GET") + else if (isSetter) ("setter", if (isSourceMethod) "method" else "value", "SET") + else if (isTerm && isLazy) ("lazy value", "lazy value", "LAZ") + else if (isVariable) ("field", "variable", "VAR") + else if (isTrait) ("trait", "trait", "TRT") + else if (isClass) ("class", "class", "CLS") + else if (isType) ("type", "type", "TPE") + else if (isClassConstructor) ("constructor", "constructor", "CTOR") + else if (isSourceMethod) ("method", "method", "METH") + else if (isTerm) ("value", "value", "VAL") + else ("", "", "???") + SymbolKind(kind._1, kind._2, kind._3) + } + /** Accurate string representation of symbols' kind, suitable for developers. */ final def accurateKindString: String = - if (isPackage) "package" - else if (isPackageClass) "package class" - else if (isPackageObject) "package object" - else if (isPackageObjectClass) "package object class" - else if (isRefinementClass) "refinement class" - else if (isModule) "module" - else if (isModuleClass) "module class" - else if (isGetter) "getter" - else if (isSetter) "setter" - else if (isVariable) "field" - else sanitizedKindString + symbolKind.accurate /** String representation of symbol's kind, suitable for the masses. */ private def sanitizedKindString: String = - if (isPackage || isPackageClass) "package" - else if (isModule || isModuleClass) "object" - else if (isAnonymousClass) "anonymous class" - else if (isRefinementClass) "" - else if (isTrait) "trait" - else if (isClass) "class" - else if (isType) "type" - else if (isInstanceOf[FreeVar]) "free variable" - else if (isTerm && isLazy) "lazy value" - else if (isVariable) "variable" - else if (isClassConstructor) "constructor" - else if (isSourceMethod) "method" - else if (isTerm) "value" - else "" + symbolKind.sanitized + + /** String representation of symbol's kind, suitable for the masses. */ + protected[scala] def abbreviatedKindString: String = + symbolKind.abbreviation final def kindString: String = if (settings.debug.value) accurateKindString @@ -1929,12 +1995,25 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If !settings.debug translates expansions of operators back to operator symbol. * E.g. $eq => =. * If settings.uniqid, adds id. + * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ def nameString: String = ( - if (settings.uniqid.value) decodedName + "#" + id - else "" + decodedName + if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + decodedName + else if (settings.uniqid.value && !settings.Yshowsymkinds.value) decodedName + "#" + id + else if (!settings.uniqid.value && settings.Yshowsymkinds.value) decodedName + "#" + abbreviatedKindString + else decodedName + "#" + id + "#" + abbreviatedKindString ) + def fullNameString: String = { + def recur(sym: Symbol): String = { + if (sym.isRoot || sym.isRootPackage || sym == NoSymbol) sym.nameString + else if (sym.owner.isEffectiveRoot) sym.nameString + else recur(sym.effectiveOwner.enclClass) + "." + sym.nameString + } + + recur(this) + } + /** If settings.uniqid is set, the symbol's id, else "" */ final def idString = if (settings.uniqid.value) "#"+id else "" @@ -2002,6 +2081,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def infosString = infos.toString() + def debugLocationString = fullLocationString + " " + debugFlagString + def debugFlagString = hasFlagsToString(-1L) def hasFlagsToString(mask: Long): String = flagsToString( flags & mask, if (hasAccessBoundary) privateWithin.toString else "" @@ -2049,15 +2130,18 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** A class for term symbols */ - class TermSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + class TermSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) extends Symbol(initOwner, initPos, initName) { final override def isTerm = true override def name: TermName = rawname.toTermName privateWithin = NoSymbol - var referenced: Symbol = NoSymbol - + private[this] var _referenced: Symbol = NoSymbol + + def referenced: Symbol = _referenced + def referenced_=(x: Symbol) { _referenced = x } + def existentialBound = singletonBounds(this.tpe) def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = @@ -2097,7 +2181,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def setLazyAccessor(sym: Symbol): TermSymbol = { - assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, hasFlagsToString(-1L), referenced, sym)) + assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, debugFlagString, referenced, sym)) referenced = sym this } @@ -2139,7 +2223,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** A class for module symbols */ - class ModuleSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + class ModuleSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) extends TermSymbol(initOwner, initPos, initName) { private var flatname: TermName = null @@ -2162,7 +2246,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** A class for method symbols */ - class MethodSymbol(initOwner: Symbol, initPos: Position, initName: TermName) + class MethodSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TermName) extends TermSymbol(initOwner, initPos, initName) { private var mtpePeriod = NoPeriod private var mtpePre: Type = _ @@ -2188,7 +2272,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } - class AliasTypeSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) + class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { // Temporary programmatic help tracking down who might do such a thing override def setFlag(mask: Long): this.type = { @@ -2229,7 +2313,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** A class of type symbols. Alias and abstract types are direct instances * of this class. Classes are instances of a subclass. */ - sealed abstract class TypeSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) extends Symbol(initOwner, initPos, initName) { + abstract class TypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends Symbol(initOwner, initPos, initName) { privateWithin = NoSymbol private var tyconCache: Type = null private var tyconRunId = NoRunId @@ -2238,7 +2322,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Overridden in subclasses for which it makes sense. */ - def existentialBound: Type = abort("unexpected type: "+this.getClass+ " "+this.fullLocationString+ " " + hasFlagsToString(-1L)) + def existentialBound: Type = abort("unexpected type: "+this.getClass+ " "+debugLocationString) override def name: TypeName = super.name.asInstanceOf[TypeName] final override def isType = true @@ -2246,7 +2330,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isAbstractType = { if (settings.debug.value) { if (isDeferred) { - println("TypeSymbol claims to be abstract type: " + this.getClass + " " + hasFlagsToString(-1L) + " at ") + println("TypeSymbol claims to be abstract type: " + this.getClass + " " + debugFlagString + " at ") (new Throwable).printStackTrace } } @@ -2367,7 +2451,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * * origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) */ - class TypeSkolem(initOwner: Symbol, initPos: Position, initName: TypeName, origin: AnyRef) + class TypeSkolem protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName, origin: AnyRef) extends TypeSymbol(initOwner, initPos, initName) { /** The skolemization level in place when the skolem was constructed */ @@ -2396,11 +2480,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** A class for class symbols */ - class ClassSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) + class ClassSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { - private var flatname: TypeName = null - private var source: AbstractFileType = null - private var thissym: Symbol = this + private[this] var flatname: TypeName = null + private[this] var source: AbstractFileType = null + private[this] var thissym: Symbol = this final override def isClass = true final override def isNonClassType = false @@ -2462,7 +2546,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } typeOfThisCache } - else thissym.tpe + else thisSym.tpe } /** Sets the self type of the class */ @@ -2482,7 +2566,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def sourceModule = if (isModuleClass) companionModule else NoSymbol - private var childSet: Set[Symbol] = Set() + private[this] var childSet: Set[Symbol] = Set() override def children = childSet override def addChild(sym: Symbol) { childSet = childSet + sym } @@ -2493,7 +2577,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * Note: Not all module classes are of this type; when unpickled, we get * plain class symbols! */ - class ModuleClassSymbol(owner: Symbol, pos: Position, name: TypeName) + class ModuleClassSymbol protected[Symbols] (owner: Symbol, pos: Position, name: TypeName) extends ClassSymbol(owner, pos, name) { private var module: Symbol = null private var implicitMembersCacheValue: List[Symbol] = List() @@ -2518,7 +2602,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } class FreeVar(name0: TermName, val value: Any) extends TermSymbol(NoSymbol, NoPosition, name0) { - override def hashCode = value.hashCode + override def hashCode = if (value == null) 0 else value.hashCode override def equals(other: Any): Boolean = other match { case that: FreeVar => this.value.asInstanceOf[AnyRef] eq that.value.asInstanceOf[AnyRef] case _ => false @@ -2526,10 +2610,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** An object representing a missing symbol */ - object NoSymbol extends Symbol(null, NoPosition, nme.NO_NAME) { - setInfo(NoType) - privateWithin = this - override def info_=(info: Type) { + class NoSymbol protected[Symbols]() extends Symbol(null, NoPosition, nme.NO_NAME) { + synchronized { + setInfo(NoType) + privateWithin = this + } + override def info_=(info: Type) = { infos = TypeHistory(1, NoType, null) unlock() validTo = currentPeriod @@ -2539,7 +2625,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def defString: String = toString override def locationString: String = "" override def enclClass: Symbol = this - override def toplevelClass: Symbol = this + override def enclosingTopLevelClass: Symbol = this override def enclMethod: Symbol = this override def sourceFile: AbstractFileType = null override def ownerChain: List[Symbol] = List() @@ -2560,6 +2646,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") } + protected def makeNoSymbol = new NoSymbol + + lazy val NoSymbol = makeNoSymbol + /** Derives a new list of symbols from the given list by mapping the given * list across the given function. Then fixes the info of all the new symbols * by substituting the new symbols for the original symbols. diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index dcc395ddd2..2b1d833c73 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -24,13 +24,32 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } def quotedName(name: Name): String = quotedName(name, false) + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + val sym = tree.symbol + 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 + tree.symbol.decodedName + suffix + } else { + quotedName(name, decoded) + } + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + /** Turns a path into a String, introducing backquotes * as necessary. */ - def backquotedPath(t: Tree): String = t match { - case Select(qual, name) => "%s.%s".format(backquotedPath(qual), quotedName(name)) - case Ident(name) => quotedName(name) - case _ => t.toString + def backquotedPath(t: Tree): String = { + t match { + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) + case Ident(name) => symName(t, name) + case _ => t.toString + } } class TreePrinter(out: PrintWriter) extends super.TreePrinter { @@ -118,18 +137,6 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) - private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - def nameFn(sym: Symbol) = { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - val suffix = if (uniqueIds) "#"+sym.id else "" - prefix + tree.symbol.decodedName + suffix - } - symFn(tree, nameFn, quotedName(name, decoded)) - } - - def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) - def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) - def printOpt(prefix: String, tree: Tree) { if (!tree.isEmpty) { print(prefix, tree) } } @@ -292,6 +299,9 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => case Assign(lhs, rhs) => print(lhs, " = ", rhs) + case AssignOrNamedArg(lhs, rhs) => + print(lhs, " = ", rhs) + case If(cond, thenp, elsep) => print("if (", cond, ")"); indent; println() print(thenp); undent @@ -397,7 +407,6 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => // case SelectFromArray(qualifier, name, _) => // print(qualifier); print(".<arr>"); print(symName(tree, name)) - case tree => xprintTree(this, tree) } @@ -413,7 +422,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => case name: Name => print(quotedName(name)) case arg => - out.print(arg.toString) + out.print(if (arg == null) "null" else arg.toString) } } diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index a2c55a89d6..958d04732b 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -72,9 +72,9 @@ trait Trees extends api.Trees { self: SymbolTable => def withPosition(flag: Long, position: Position) = copy() setPositions positions + (flag -> position) - override def hasModifier(mod: Modifier.Value) = + override def hasModifier(mod: Modifier) = hasFlag(flagOfModifier(mod)) - override def allModifiers: Set[Modifier.Value] = + override def modifiers: Set[Modifier] = Modifier.values filter hasModifier override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = Modifiers(flags, privateWithin, f(annotations)) setPositions positions @@ -85,7 +85,7 @@ trait Trees extends api.Trees { self: SymbolTable => def Modifiers(flags: Long, privateWithin: Name): Modifiers = Modifiers(flags, privateWithin, List()) def Modifiers(flags: Long): Modifiers = Modifiers(flags, tpnme.EMPTY) - def Modifiers(mods: Set[Modifier.Value], + def Modifiers(mods: Set[Modifier], privateWithin: Name, annotations: List[Tree]): Modifiers = { val flagSet = mods map flagOfModifier @@ -129,6 +129,9 @@ trait Trees extends api.Trees { self: SymbolTable => } def shallowDuplicate: Tree = new ShallowDuplicator(tree) transform tree def shortClass: String = tree.getClass.getName split "[.$]" last + + def isErrorTyped = (tree.tpe ne null) && tree.tpe.isError + /** When you want to know a little more than the class, but a lot * less than the whole tree. */ @@ -186,7 +189,7 @@ trait Trees extends api.Trees { self: SymbolTable => def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = atPos(sym.pos) { assert(sym != NoSymbol) - DefDef(Modifiers(sym.flags), + DefDef(mods, sym.name.toTermName, sym.typeParams map TypeDef, vparamss, @@ -228,15 +231,6 @@ trait Trees extends api.Trees { self: SymbolTable => def Bind(sym: Symbol, body: Tree): Bind = Bind(sym.name, body) setSymbol sym - - /** Factory method for object creation `new tpt(args_1)...(args_n)` - * A `New(t, as)` is expanded to: `(new t).<init>(as)` - */ - def New(tpt: Tree, argss: List[List[Tree]]): Tree = { - assert(!argss.isEmpty) - val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR) - (superRef /: argss) (Apply) - } /** 0-1 argument list new, based on a symbol. */ def New(sym: Symbol, args: Tree*): Tree = @@ -248,8 +242,6 @@ trait Trees extends api.Trees { self: SymbolTable => def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix) - def This(sym: Symbol): Tree = This(sym.name.toTypeName) setSymbol sym - /** Block factory that flattens directly nested blocks. */ def Block(stats: Tree*): Block = { diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 1df60f32d9..adf9df185a 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -109,15 +109,19 @@ trait Types extends api.Types { self: SymbolTable => /** A log of type variable with their original constraints. Used in order * to undo constraints in the case of isSubType/isSameType failure. */ - object undoLog { - private type UndoLog = List[(TypeVar, TypeConstraint)] - private[scala] var log: UndoLog = List() - + lazy val undoLog = newUndoLog + + protected def newUndoLog = new UndoLog + + class UndoLog { + private type UndoPairs = List[(TypeVar, TypeConstraint)] + private var log: UndoPairs = List() + // register with the auto-clearing cache manager perRunCaches.recordCache(this) /** Undo all changes to constraints to type variables upto `limit`. */ - private def undoTo(limit: UndoLog) { + private def undoTo(limit: UndoPairs) { while ((log ne limit) && log.nonEmpty) { val (tv, constr) = log.head tv.constr = constr @@ -125,9 +129,14 @@ trait Types extends api.Types { self: SymbolTable => } } - private[Types] def record(tv: TypeVar) = { + /** 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)) } + private[scala] def clear() { if (settings.debug.value) self.log("Clearing " + log.size + " entries from the undoLog.") @@ -249,10 +258,10 @@ trait Types extends api.Types { self: SymbolTable => abstract class AbsTypeImpl extends AbsType { this: Type => def declaration(name: Name): Symbol = decl(name) def nonPrivateDeclaration(name: Name): Symbol = nonPrivateDecl(name) - def allDeclarations = decls - def allMembers = members + def declarations = decls def typeArguments = typeArgs def erasedType = transformedType(this) + def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) } /** The base class for all types */ @@ -873,16 +882,7 @@ trait Types extends api.Types { self: SymbolTable => * after `maxTostringRecursions` recursion levels. Uses `safeToString` * to produce a string on each level. */ - override def toString: String = - if (tostringRecursions >= maxTostringRecursions) - "..." - else - try { - tostringRecursions += 1 - safeToString - } finally { - tostringRecursions -= 1 - } + override def toString: String = typeToString(this) /** Method to be implemented in subclasses. * Converts this type to a string in calling toString for its parts. @@ -992,7 +992,9 @@ trait Types extends api.Types { self: SymbolTable => if (membertpe eq null) membertpe = self.memberType(member) (membertpe matches self.memberType(sym)) })) { - members = new Scope(List(member, sym)) + members = newScope + members enter member + members enter sym } } else { var prevEntry = members.lookupEntry(sym.name) @@ -1105,7 +1107,7 @@ trait Types extends api.Types { self: SymbolTable => /** A base class for types that represent a single value * (single-types and this-types). */ - abstract class SingletonType extends SubType with SimpleTypeProxy with AbsSingletonType { + abstract class SingletonType extends SubType with SimpleTypeProxy { def supertype = underlying override def isTrivial = false override def isStable = true @@ -1204,7 +1206,7 @@ trait Types extends api.Types { self: SymbolTable => if (settings.debug.value) sym.nameString + ".this." else if (sym.isAnonOrRefinementClass) "this." else if (sym.isOmittablePrefix) "" - else if (sym.isModuleClass) sym.fullName + "." + else if (sym.isModuleClass) sym.fullNameString + "." else sym.nameString + ".this." override def safeToString: String = if (sym.isRoot) "<root>" @@ -1231,18 +1233,15 @@ trait Types extends api.Types { self: SymbolTable => override val isTrivial: Boolean = pre.isTrivial // override def isNullable = underlying.isNullable override def isNotNull = underlying.isNotNull - private var underlyingCache: Type = NoType - private var underlyingPeriod = NoPeriod + private[reflect] var underlyingCache: Type = NoType + private[reflect] var underlyingPeriod = NoPeriod override def underlying: Type = { - val period = underlyingPeriod - if (period != currentPeriod) { - underlyingPeriod = currentPeriod - if (!isValid(period)) { - underlyingCache = pre.memberType(sym).resultType; - assert(underlyingCache ne this, this) - } + val cache = underlyingCache + if (underlyingPeriod == currentPeriod && cache != null) cache + else { + defineUnderlyingOfSingleType(this) + underlyingCache } - underlyingCache } // more precise conceptually, but causes cyclic errors: (paramss exists (_ contains sym)) @@ -1281,6 +1280,17 @@ trait Types extends api.Types { self: SymbolTable => unique(new UniqueSingleType(pre, sym)) } } + + protected def defineUnderlyingOfSingleType(tpe: SingleType) = { + val period = tpe.underlyingPeriod + if (period != currentPeriod) { + tpe.underlyingPeriod = currentPeriod + if (!isValid(period)) { + tpe.underlyingCache = tpe.pre.memberType(tpe.sym).resultType; + assert(tpe.underlyingCache ne tpe, tpe) + } + } + } abstract case class SuperType(thistpe: Type, supertpe: Type) extends SingletonType { override val isTrivial: Boolean = thistpe.isTrivial && supertpe.isTrivial @@ -1333,105 +1343,36 @@ trait Types extends api.Types { self: SymbolTable => */ abstract class CompoundType extends Type { - var baseTypeSeqCache: BaseTypeSeq = _ - private var baseTypeSeqPeriod = NoPeriod - private var baseClassesCache: List[Symbol] = _ - private var baseClassesPeriod = NoPeriod + private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ + private[reflect] var baseTypeSeqPeriod = NoPeriod + private[reflect] var baseClassesCache: List[Symbol] = _ + private[reflect] var baseClassesPeriod = NoPeriod override def baseTypeSeq: BaseTypeSeq = { - val period = baseTypeSeqPeriod; - if (period != currentPeriod) { // no caching in IDE - baseTypeSeqPeriod = currentPeriod - if (!isValidForBaseClasses(period)) { - if (parents.exists(_.exists(_.isInstanceOf[TypeVar]))) { - // rename type vars to fresh type params, take base type sequence of - // resulting type, and rename back all the entries in that sequence - var tvs = Set[TypeVar]() - for (p <- parents) - for (t <- p) t match { - case tv: TypeVar => tvs += tv - case _ => - } - val varToParamMap: Map[Type, Symbol] = tvs map (tv => tv -> tv.origin.typeSymbol.cloneSymbol) toMap - val paramToVarMap = varToParamMap map (_.swap) - val varToParam = new TypeMap { - def apply(tp: Type) = varToParamMap get tp match { - case Some(sym) => sym.tpe - case _ => mapOver(tp) - } - } - val paramToVar = new TypeMap { - def apply(tp: Type) = tp match { - case TypeRef(_, tsym, _) if paramToVarMap.isDefinedAt(tsym) => paramToVarMap(tsym) - case _ => mapOver(tp) - } - } - val bts = copyRefinedType(this.asInstanceOf[RefinedType], parents map varToParam, varToParam mapOver decls).baseTypeSeq - baseTypeSeqCache = bts lateMap paramToVar - } else { - incCounter(compoundBaseTypeSeqCount) - baseTypeSeqCache = undetBaseTypeSeq - baseTypeSeqCache = if (typeSymbol.isRefinementClass) - memo(compoundBaseTypeSeq(this))(_.baseTypeSeq updateHead typeSymbol.tpe) - else - compoundBaseTypeSeq(this) - // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors - // when compiling with - // scalac scala.collection.IterableViewLike.scala scala.collection.IterableLike.scala - // I have not yet figured out precisely why this is the case. - // My current assumption is that taking memos forces baseTypeSeqs to be computed - // at stale types (i.e. the underlying typeSymbol has already another type). - // I do not yet see precisely why this would cause a problem, but it looks - // fishy in any case. - } - } - //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + val cached = baseTypeSeqCache + if (baseTypeSeqPeriod == currentPeriod && cached != null && cached != undetBaseTypeSeq) + cached + else { + defineBaseTypeSeqOfCompoundType(this) + if (baseTypeSeqCache eq undetBaseTypeSeq) + throw new RecoverableCyclicReference(typeSymbol) + + baseTypeSeqCache } - if (baseTypeSeqCache eq undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + typeSymbol) - baseTypeSeqCache } override def baseTypeSeqDepth: Int = baseTypeSeq.maxDepth override def baseClasses: List[Symbol] = { - def computeBaseClasses: List[Symbol] = - if (parents.isEmpty) List(typeSymbol) - else { - //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG - // optimized, since this seems to be performance critical - val superclazz = parents.head - var mixins = parents.tail - val sbcs = superclazz.baseClasses - var bcs = sbcs - def isNew(clazz: Symbol): Boolean = ( - superclazz.baseTypeIndex(clazz) < 0 && - { var p = bcs; - while ((p ne sbcs) && (p.head != clazz)) p = p.tail; - p eq sbcs - } - ); - while (!mixins.isEmpty) { - def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = - if (mbcs.isEmpty) bcs - else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) - else addMixinBaseClasses(mbcs.tail); - bcs = addMixinBaseClasses(mixins.head.baseClasses) - mixins = mixins.tail - } - typeSymbol :: bcs - } - val period = baseClassesPeriod - if (period != currentPeriod) { - baseClassesPeriod = currentPeriod - if (!isValidForBaseClasses(period)) { - baseClassesCache = null - baseClassesCache = memo(computeBaseClasses)(typeSymbol :: _.baseClasses.tail) - } + val cached = baseClassesCache + if (baseClassesPeriod == currentPeriod && cached != null) cached + else { + defineBaseClassesOfCompoundType(this) + if (baseClassesCache eq null) + throw new RecoverableCyclicReference(typeSymbol) + + baseClassesCache } - if (baseClassesCache eq null) - throw new TypeError("illegal cyclic reference involving " + typeSymbol) - baseClassesCache } /** The slightly less idiomatic use of Options is due to @@ -1469,12 +1410,103 @@ trait Types extends api.Types { self: SymbolTable => // override def isNullable: Boolean = // parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType); - + override def safeToString: String = parents.mkString(" with ") + (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) decls.mkString("{", "; ", "}") else "") } + + protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = { + val period = tpe.baseTypeSeqPeriod; + if (period != currentPeriod) { + tpe.baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + if (tpe.parents.exists(_.exists(_.isInstanceOf[TypeVar]))) { + // rename type vars to fresh type params, take base type sequence of + // resulting type, and rename back all the entries in that sequence + var tvs = Set[TypeVar]() + for (p <- tpe.parents) + for (t <- p) t match { + case tv: TypeVar => tvs += tv + case _ => + } + val varToParamMap: Map[Type, Symbol] = tvs map (tv => tv -> tv.origin.typeSymbol.cloneSymbol) toMap + val paramToVarMap = varToParamMap map (_.swap) + val varToParam = new TypeMap { + def apply(tp: Type) = varToParamMap get tp match { + case Some(sym) => sym.tpe + case _ => mapOver(tp) + } + } + val paramToVar = new TypeMap { + def apply(tp: Type) = tp match { + case TypeRef(_, tsym, _) if paramToVarMap.isDefinedAt(tsym) => paramToVarMap(tsym) + case _ => mapOver(tp) + } + } + val bts = copyRefinedType(tpe.asInstanceOf[RefinedType], tpe.parents map varToParam, varToParam mapOver tpe.decls).baseTypeSeq + tpe.baseTypeSeqCache = bts lateMap paramToVar + } else { + incCounter(compoundBaseTypeSeqCount) + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) + tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) + else + compoundBaseTypeSeq(tpe) + // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors + // when compiling with + // scalac scala.collection.IterableViewLike.scala scala.collection.IterableLike.scala + // I have not yet figured out precisely why this is the case. + // My current assumption is that taking memos forces baseTypeSeqs to be computed + // at stale types (i.e. the underlying typeSymbol has already another type). + // I do not yet see precisely why this would cause a problem, but it looks + // fishy in any case. + } + } + } + //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + if (tpe.baseTypeSeqCache eq undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) + } + + protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = { + def computeBaseClasses: List[Symbol] = + if (tpe.parents.isEmpty) List(tpe.typeSymbol) + else { + //Console.println("computing base classes of " + typeSymbol + " at phase " + phase);//DEBUG + // optimized, since this seems to be performance critical + val superclazz = tpe.parents.head + var mixins = tpe.parents.tail + val sbcs = superclazz.baseClasses + var bcs = sbcs + def isNew(clazz: Symbol): Boolean = + superclazz.baseTypeIndex(clazz) < 0 && + { var p = bcs; + while ((p ne sbcs) && (p.head != clazz)) p = p.tail; + p eq sbcs + } + while (!mixins.isEmpty) { + def addMixinBaseClasses(mbcs: List[Symbol]): List[Symbol] = + if (mbcs.isEmpty) bcs + else if (isNew(mbcs.head)) mbcs.head :: addMixinBaseClasses(mbcs.tail) + else addMixinBaseClasses(mbcs.tail) + bcs = addMixinBaseClasses(mixins.head.baseClasses) + mixins = mixins.tail + } + tpe.typeSymbol :: bcs + } + val period = tpe.baseClassesPeriod + if (period != currentPeriod) { + tpe.baseClassesPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + tpe.baseClassesCache = null + tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) + } + } + if (tpe.baseClassesCache eq null) + throw new TypeError("illegal cyclic reference involving " + tpe.typeSymbol) + } /** A class representing intersection types with refinements of the form * `<parents_0> with ... with <parents_n> { decls }` @@ -1583,7 +1615,7 @@ trait Types extends api.Types { self: SymbolTable => * by a path which contains at least one expansive reference. * @See Kennedy, Pierce: On Decidability of Nominal Subtyping with Variance */ - def expansiveRefs(tparam: Symbol) = { + private[scala] def expansiveRefs(tparam: Symbol) = { if (state == UnInitialized) { computeRefs() while (state != Initialized) propagate() @@ -1597,10 +1629,16 @@ trait Types extends api.Types { self: SymbolTable => /** The type parameters which are referenced type parameters of this class. * Two entries: refs(0): Non-expansive references * refs(1): Expansive references + * Syncnote: This var need not be protected with synchronized, because + * it is accessed only from expansiveRefs, which is called only from + * Typer. */ private var refs: Array[RefMap] = _ /** The initialization state of the class: UnInialized --> Initializing --> Initialized + * Syncnote: This var need not be protected with synchronized, because + * it is accessed only from expansiveRefs, which is called only from + * Typer. */ private var state = UnInitialized @@ -1713,6 +1751,19 @@ trait Types extends api.Types { self: SymbolTable => // override def isNonNull: Boolean = symbol == NonNullClass || super.isNonNull; override def kind = "ClassInfoType" + + override def safeToString = + if (settings.debug.value || decls.size > 1) + formattedToString + else + super.safeToString + + /** A nicely formatted string with newlines and such. + */ + def formattedToString: String = + parents.mkString("\n with ") + + (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + decls.mkString(" {\n ", "\n ", "\n}") else "") } object ClassInfoType extends ClassInfoTypeExtractor @@ -1750,6 +1801,10 @@ trait Types extends api.Types { self: SymbolTable => } } + /* Syncnote: The `volatile` var and `pendingVolatiles` mutable set need not be protected + * with synchronized, because they are accessed only from isVolatile, which is called only from + * Typer. + */ private var volatileRecursions: Int = 0 private val pendingVolatiles = new mutable.HashSet[Symbol] @@ -1831,13 +1886,18 @@ trait Types extends api.Types { self: SymbolTable => if (sym == clazz) this else transform(sym.info.baseType(clazz)) } + trait NonClassTypeRef extends TypeRef { require(sym.isNonClassType, sym) + /* Syncnote: These are pure caches for performance; no problem to evaluate these + * several times. Hence, no need to protected with synchronzied in a mutli-threaded + * usage scenario. + */ private var relativeInfoCache: Type = _ private var memberInfoCache: Type = _ - private def relativeInfo = { + private[Types] def relativeInfo = { val memberInfo = pre.memberInfo(sym) if (relativeInfoCache == null || (memberInfo ne memberInfoCache)) { memberInfoCache = memberInfo @@ -1846,25 +1906,27 @@ trait Types extends api.Types { self: SymbolTable => relativeInfoCache } - override def baseType(clazz: Symbol): Type = ( - if (sym == clazz) this else try { - basetypeRecursions += 1 - if (basetypeRecursions < LogPendingBaseTypesThreshold) - relativeInfo.baseType(clazz) - else if (pendingBaseTypes contains this) - if (clazz == AnyClass) clazz.tpe else NoType - else - try { - pendingBaseTypes += this - relativeInfo.baseType(clazz) - } finally { - pendingBaseTypes -= this - } + override def baseType(clazz: Symbol): Type = + if (sym == clazz) this else baseTypeOfNonClassTypeRef(this, clazz) + } + + protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = try { + basetypeRecursions += 1 + if (basetypeRecursions < LogPendingBaseTypesThreshold) + tpe.relativeInfo.baseType(clazz) + else if (pendingBaseTypes contains tpe) + if (clazz == AnyClass) clazz.tpe else NoType + else + try { + pendingBaseTypes += tpe + tpe.relativeInfo.baseType(clazz) } finally { - basetypeRecursions -= 1 + pendingBaseTypes -= tpe } - ) + } finally { + basetypeRecursions -= 1 } + trait AliasTypeRef extends NonClassTypeRef { require(sym.isAliasType, sym) @@ -1912,6 +1974,8 @@ trait Types extends api.Types { self: SymbolTable => trait AbstractTypeRef extends NonClassTypeRef { require(sym.isAbstractType, sym) + /** Syncnote: Pure performance caches; no need to synchronize in multi-threaded environment + */ private var symInfoCache: Type = _ private var thisInfoCache: Type = _ @@ -1938,6 +2002,7 @@ trait Types extends api.Types { self: SymbolTable => volatileRecursions -= 1 } } + override def thisInfo = { val symInfo = sym.info if (thisInfoCache == null || (symInfo ne symInfoCache)) { @@ -1946,7 +2011,7 @@ trait Types extends api.Types { self: SymbolTable => // If a subtyping cycle is not detected here, we'll likely enter an infinite // loop before a sensible error can be issued. SI-5093 is one example. case x: SubType if x.supertype eq this => - throw new TypeError("illegal cyclic reference involving " + sym) + throw new RecoverableCyclicReference(sym) case tp => tp } } @@ -1955,7 +2020,7 @@ 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 def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this + override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this } /** A class for named types of the form @@ -1966,11 +2031,11 @@ trait Types extends api.Types { self: SymbolTable => * @M: a higher-kinded type is represented as a TypeRef with sym.typeParams.nonEmpty, but args.isEmpty */ abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends Type { - private var parentsCache: List[Type] = _ - private var parentsPeriod = NoPeriod - private var baseTypeSeqCache: BaseTypeSeq = _ - private var baseTypeSeqPeriod = NoPeriod - private var normalized: Type = _ + private[reflect] var parentsCache: List[Type] = _ + private[reflect] var parentsPeriod = NoPeriod + private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ + private[reflect] var baseTypeSeqPeriod = NoPeriod + private var normalized: Type = _ // @M: propagate actual type params (args) to `tp`, by replacing // formal type parameters with actual ones. If tp is higher kinded, @@ -2030,16 +2095,12 @@ trait Types extends api.Types { self: SymbolTable => sym.isModuleClass || sym == NothingClass || isValueClass(sym) || super.isNotNull override def parents: List[Type] = { - val period = parentsPeriod - if (period != currentPeriod) { - parentsPeriod = currentPeriod - if (!isValidForBaseClasses(period)) { - parentsCache = thisInfo.parents map transform - } else if (parentsCache == null) { // seems this can happen if things are corrupted enough, see #2641 - parentsCache = List(AnyClass.tpe) - } + val cache = parentsCache + if (parentsPeriod == currentPeriod && cache != null) cache + else { + defineParentsOfTypeRef(this) + parentsCache } - parentsCache } override def decls: Scope = { @@ -2051,21 +2112,19 @@ trait Types extends api.Types { self: SymbolTable => thisInfo.decls } - protected def baseTypeSeqImpl: BaseTypeSeq = sym.info.baseTypeSeq map transform + protected[Types] def baseTypeSeqImpl: BaseTypeSeq = sym.info.baseTypeSeq map transform override def baseTypeSeq: BaseTypeSeq = { - val period = baseTypeSeqPeriod - if (period != currentPeriod) { - baseTypeSeqPeriod = currentPeriod - if (!isValidForBaseClasses(period)) { - incCounter(typerefBaseTypeSeqCount) - baseTypeSeqCache = undetBaseTypeSeq - baseTypeSeqCache = baseTypeSeqImpl - } + val cache = baseTypeSeqCache + if (baseTypeSeqPeriod == currentPeriod && cache != null && cache != undetBaseTypeSeq) + cache + else { + defineBaseTypeSeqOfTypeRef(this) + if (baseTypeSeqCache == undetBaseTypeSeq) + throw new RecoverableCyclicReference(sym) + + baseTypeSeqCache } - if (baseTypeSeqCache == undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + sym) - baseTypeSeqCache } private def preString = ( @@ -2074,11 +2133,11 @@ trait Types extends api.Types { self: SymbolTable => else pre.prefixString ) private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") - private def refinementString = ( + def refinementString = ( if (sym.isStructuralRefinement) ( decls filter (sym => sym.isPossibleInRefinement && sym.isPublic) map (_.defString) - mkString(" {", "; ", "}") + mkString("{", "; ", "}") ) else "" ) @@ -2151,6 +2210,32 @@ trait Types extends api.Types { self: SymbolTable => } }) } + + protected def defineParentsOfTypeRef(tpe: TypeRef) = { + val period = tpe.parentsPeriod + if (period != currentPeriod) { + tpe.parentsPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + tpe.parentsCache = tpe.thisInfo.parents map tpe.transform + } else if (tpe.parentsCache == null) { // seems this can happen if things are corrupted enough, see #2641 + tpe.parentsCache = List(AnyClass.tpe) + } + } + } + + protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = { + val period = tpe.baseTypeSeqPeriod + if (period != currentPeriod) { + tpe.baseTypeSeqPeriod = currentPeriod + if (!isValidForBaseClasses(period)) { + incCounter(typerefBaseTypeSeqCount) + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl + } + } + if (tpe.baseTypeSeqCache == undetBaseTypeSeq) + throw new TypeError("illegal cyclic inheritance involving " + tpe.sym) + } /** A class representing a method type with parameters. * Note that a parameterless method is represented by a NullaryMethodType: @@ -2348,25 +2433,37 @@ trait Types extends api.Types { self: SymbolTable => case _ => List() } - + /** An existential can only be printed with wildcards if: + * - the underlying type is a typeref + * - where there is a 1-to-1 correspondence between underlying's typeargs and quantified + * - and none of the existential parameters is referenced from anywhere else in the type + * - and none of the existential parameters are singleton types + */ + private def isRepresentableWithWildcards = !settings.debug.value && { + val qset = quantified.toSet + !qset.exists(_.isSingletonExistential) && (underlying match { + case TypeRef(_, sym, args) => + sameLength(args, quantified) && { + args forall { arg => + qset(arg.typeSymbol) && !qset.exists(arg.typeSymbol.info.bounds contains _) + } + } + case _ => false + }) + } override def safeToString: String = { - if (!(quantified exists (_.isSingletonExistential)) && !settings.debug.value) - // try to represent with wildcards first - underlying match { - case TypeRef(pre, sym, args) if args.nonEmpty => - val wargs = wildcardArgsString(quantified.toSet, args) - if (sameLength(wargs, args)) - return TypeRef(pre, sym, List()) + wargs.mkString("[", ", ", "]") - case _ => - } - var ustr = underlying.toString + def clauses = { + val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") + if (settings.explaintypes.value) "(" + str + ")" else str + } underlying match { - case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => ustr = "("+ustr+")" + case TypeRef(pre, sym, args) if isRepresentableWithWildcards => + "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => + "(" + underlying + ")" + clauses case _ => + "" + underlying + clauses } - val str = - ustr+(quantified map (_.existentialToString) mkString(" forSome { ", "; ", " }")) - if (settings.explaintypes.value) "("+str+")" else str } override def cloneInfo(owner: Symbol) = @@ -2408,7 +2505,7 @@ 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) => @@ -2498,7 +2595,7 @@ trait Types extends api.Types { self: SymbolTable => if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr) else if (args.size == params.size) new AppliedTypeVar(origin, constr, params zip args) else if (args.isEmpty) new HKTypeVar(origin, constr, params) - else throw new TypeError("Invalid TypeVar construction: " + ((origin, constr, args, params))) + else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) trace("create", "In " + tv.originLocation)(tv) @@ -2577,7 +2674,12 @@ trait Types extends api.Types { self: SymbolTable => override def typeArgs: List[Type] = Nil override def isHigherKinded = false - /** The constraint associated with the variable */ + /** The constraint associated with the variable + * Syncnote: Type variables are assumed to be used from only one + * thread. They are not exposed in api.Types and are used only locally + * in operations that are exposed from types. Hence, no syncing of `constr` + * or `encounteredHigherLevel` or `suspended` accesses should be necessary. + */ var constr = constr0 def instValid = constr.instValid @@ -2599,7 +2701,7 @@ trait Types extends api.Types { self: SymbolTable => TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) } else - throw new TypeError("Invalid type application in TypeVar: " + params + ", " + newArgs) + throw new Error("Invalid type application in TypeVar: " + params + ", " + newArgs) ) // newArgs.length may differ from args.length (could've been empty before) // @@ -3048,7 +3150,7 @@ trait Types extends api.Types { self: SymbolTable => * @return ... */ def refinedType(parents: List[Type], owner: Symbol): Type = - refinedType(parents, owner, new Scope, owner.pos) + refinedType(parents, owner, newScope, owner.pos) def copyRefinedType(original: RefinedType, parents: List[Type], decls: Scope) = if ((parents eq original.parents) && (decls eq original.decls)) original @@ -3079,7 +3181,7 @@ trait Types extends api.Types { self: SymbolTable => // don't expand cyclical type alias // we require that object is initialized, thus info.typeParams instead of typeParams. if (sym1.isAliasType && sameLength(sym1.info.typeParams, args) && !sym1.lockOK) - throw new TypeError("illegal cyclic reference involving " + sym1) + throw new RecoverableCyclicReference(sym1) val pre1 = pre match { case x: SuperType if sym1.isEffectivelyFinal || sym1.isDeferred => @@ -3101,7 +3203,7 @@ trait Types extends api.Types { self: SymbolTable => def copyTypeRef(tp: Type, pre: Type, sym: Symbol, args: List[Type]): Type = tp match { case TypeRef(pre0, sym0, _) if pre == pre0 && sym0.name == sym.name => if (sym.isAliasType && sameLength(sym.info.typeParams, args) && !sym.lockOK) - throw new TypeError("illegal cyclic reference involving " + sym) + throw new RecoverableCyclicReference(sym) TypeRef(pre, sym, args) case _ => @@ -3170,6 +3272,25 @@ trait Types extends api.Types { self: SymbolTable => case WildcardType => tycon // needed for neg/t0226 case _ => abort(debugString(tycon)) } + + /** 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 _) + (eparams, bounds).zipped foreach (_ setInfo _) + + newExistentialType(eparams, typeRef(pre, sym, eparams map (_.tpe))) + case _ => + appliedType(tycon, args) + } + } /** A creator for type parameterizations that strips empty type parameter lists. * Use this factory method to indicate the type has kind * (it's a polymorphic value) @@ -3352,7 +3473,7 @@ trait Types extends api.Types { self: SymbolTable => private var uniques: util.HashSet[Type] = _ private var uniqueRunId = NoRunId - private def unique[T <: Type](tp: T): T = { + protected def unique[T <: Type](tp: T): T = { incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) @@ -3376,6 +3497,12 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -3439,14 +3566,14 @@ trait Types extends api.Types { self: SymbolTable => } override def toString = { - val boundsStr = ( - if (loBounds.isEmpty && hiBounds.isEmpty) "[]" - else { - val lostr = if (loBounds.isEmpty) "" else loBounds map (_.safeToString) mkString("_>:(", ", ", ")") - val histr = if (hiBounds.isEmpty) "" else hiBounds map (_.safeToString) mkString("_<:(", ", ", ")") - List(lostr, histr) filterNot (_ == "") mkString ("[", " | ", "]") - } - ) + val boundsStr = { + val lo = loBounds filterNot (_.typeSymbolDirect eq NothingClass) + val hi = hiBounds filterNot (_.typeSymbolDirect eq AnyClass) + val lostr = if (lo.isEmpty) Nil else List(lo.mkString(" >: (", ", ", ")")) + val histr = if (hi.isEmpty) Nil else List(hi.mkString(" <: (", ", ", ")")) + + lostr ++ histr mkString ("[", " | ", "]") + } if (inst eq NoType) boundsStr else boundsStr + " _= " + inst.safeToString } @@ -3648,7 +3775,7 @@ trait Types extends api.Types { self: SymbolTable => val elems = scope.toList val elems1 = mapOver(elems) if (elems1 eq elems) scope - else new Scope(elems1) + else newScopeWith(elems1: _*) } /** Map this function over given list of symbols */ @@ -3713,6 +3840,11 @@ trait Types extends api.Types { self: SymbolTable => 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 = _ @@ -3744,6 +3876,8 @@ trait Types extends api.Types { self: SymbolTable => eparams map (_ substInfo (tparams, eparams)) } + def typeParamsToExistentials(clazz: Symbol): List[Symbol] = + typeParamsToExistentials(clazz, clazz.typeParams) // note: it's important to write the two tests in this order, // as only typeParams forces the classfile to be read. See #400 @@ -3767,7 +3901,7 @@ trait Types extends api.Types { self: SymbolTable => * the conversion of raw types to existential types might not have taken place * in ClassFileparser.sigToType (where it is usually done). */ - object rawToExistential extends TypeMap { + def rawToExistential = new TypeMap { private var expanded = immutable.Set[Symbol]() private var generated = immutable.Set[Type]() def apply(tp: Type): Type = tp match { @@ -3775,7 +3909,7 @@ trait Types extends api.Types { self: SymbolTable => if (expanded contains sym) AnyRefClass.tpe else try { expanded += sym - val eparams = mapOver(typeParamsToExistentials(sym, sym.typeParams)) + val eparams = mapOver(typeParamsToExistentials(sym)) existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) } finally { expanded -= sym @@ -3985,15 +4119,17 @@ trait Types extends api.Types { self: SymbolTable => 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)) { + if (sameLength(basesym.typeParams, baseargs)) instParam(basesym.typeParams, baseargs) - } else { - throw new TypeError( - "something is wrong (wrong class file?): "+basesym+ - " with type parameters "+ - basesym.typeParams.map(_.name).mkString("[",",","]")+ - " gets applied to arguments "+baseargs.mkString("[",",","]")+", phase = "+phase) - } + else + if (symclazz.tpe.parents.exists(_.isErroneous)) + ErrorType // don't be to overzealous with throwing exceptions, see #2641 + else + throw new Error( + "something is wrong (wrong class file?): "+basesym+ + " with type parameters "+ + basesym.typeParams.map(_.name).mkString("[",",","]")+ + " gets applied to arguments "+baseargs.mkString("[",",","]")+", phase = "+phase) case ExistentialType(tparams, qtpe) => capturedSkolems = capturedSkolems union tparams toInstance(qtpe, clazz) @@ -4379,15 +4515,20 @@ trait Types extends api.Types { self: SymbolTable => private def commonOwner(tps: List[Type]): Symbol = { if (tps.isEmpty) NoSymbol else { - commonOwnerMap.result = null + commonOwnerMap.clear() tps foreach (commonOwnerMap traverse _) val result = if (commonOwnerMap.result ne null) commonOwnerMap.result else NoSymbol debuglog(tps.mkString("commonOwner(", ", ", ") == " + result)) result } } - private object commonOwnerMap extends TypeTraverser { + + 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. @@ -4404,12 +4545,15 @@ trait Types extends api.Types { self: SymbolTable => 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 @@ -5349,8 +5493,8 @@ trait Types extends api.Types { self: SymbolTable => matchesType(tp1, res2, true) case MethodType(_, _) => false - case PolyType(tparams2, res2) => - tparams2.isEmpty && matchesType(tp1, res2, alwaysMatchSimple) + case PolyType(_, _) => + false case _ => alwaysMatchSimple || tp1 =:= tp2 } @@ -5367,6 +5511,8 @@ trait Types extends api.Types { self: SymbolTable => else matchesType(tp1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) + case TypeRef(_, sym, Nil) => + params1.isEmpty && sym.isModuleClass && matchesType(res1, tp2, alwaysMatchSimple) case _ => false } @@ -5378,6 +5524,8 @@ trait Types extends api.Types { self: SymbolTable => matchesType(res1, res2, alwaysMatchSimple) case ExistentialType(_, res2) => alwaysMatchSimple && matchesType(tp1, res2, true) + case TypeRef(_, sym, Nil) if sym.isModuleClass => + matchesType(res1, tp2, alwaysMatchSimple) case _ => matchesType(res1, tp2, alwaysMatchSimple) } @@ -5398,6 +5546,12 @@ trait Types extends api.Types { self: SymbolTable => if (alwaysMatchSimple) matchesType(res1, tp2, true) else lastTry } + case TypeRef(_, sym, Nil) if sym.isModuleClass => + tp2 match { + case MethodType(Nil, res2) => matchesType(tp1, res2, alwaysMatchSimple) + case NullaryMethodType(res2) => matchesType(tp1, res2, alwaysMatchSimple) + case _ => lastTry + } case _ => lastTry } @@ -5577,7 +5731,7 @@ trait Types extends api.Types { self: SymbolTable => val padded = sorted map (_._2.padTo(maxSeqLength, NoType)) val transposed = padded.transpose - val columns: List[Column[List[Type]]] = sorted.zipWithIndex map { + val columns: List[Column[List[Type]]] = mapWithIndex(sorted) { case ((k, v), idx) => Column(str(k), (xs: List[Type]) => str(xs(idx)), true) } @@ -5846,7 +6000,7 @@ trait Types extends api.Types { self: SymbolTable => } /** The least upper bound wrt <:< of a list of types */ - def lub(ts: List[Type], depth: Int): Type = { + 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 @@ -5997,7 +6151,7 @@ trait Types extends api.Types { self: SymbolTable => /** The greatest lower bound wrt <:< of a list of types, which have been normalized * wrt elimSuper */ - private def glbNorm(ts: List[Type], depth: Int): Type = { + 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 @@ -6278,6 +6432,12 @@ trait Types extends api.Types { self: SymbolTable => def this(msg: String) = this(NoPosition, msg) } + // TODO: RecoverableCyclicReference should be separated from TypeError, + // but that would be a big change. Left for further refactoring. + /** An exception for cyclic references from which we can recover */ + case class RecoverableCyclicReference(sym: Symbol) + extends TypeError("illegal cyclic reference involving " + sym) + class NoCommonType(tps: List[Type]) extends Throwable( "lub/glb of incompatible types: " + tps.mkString("", " and ", "")) with ControlThrowable @@ -6286,14 +6446,11 @@ trait Types extends api.Types { self: SymbolTable => def this(pre: Type, tp: String) = this("malformed type: " + pre + "#" + tp) } - /** An exception signalling a variance annotation/usage conflict */ - class VarianceError(msg: String) extends TypeError(msg) - /** The current indentation string for traces */ private var indent: String = "" /** Perform operation `p` on arguments `tp1`, `arg2` and print trace of computation. */ - private def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { + protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { Console.println(indent + tp1 + " " + op + " " + arg2 + "?" /* + "("+tp1.getClass+","+arg2.getClass+")"*/) indent = indent + " " val result = p(tp1, arg2) @@ -6338,4 +6495,16 @@ trait Types extends api.Types { self: SymbolTable => final val maxTostringRecursions = 50 private var tostringRecursions = 0 + + protected def typeToString(tpe: Type): String = + if (tostringRecursions >= maxTostringRecursions) + "..." + else + try { + tostringRecursions += 1 + tpe.safeToString + } finally { + tostringRecursions -= 1 + } + } diff --git a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala index 6980d28bfb..0092f73fe3 100644 --- a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala @@ -38,6 +38,7 @@ abstract class MutableSettings extends AbsSettings { def explaintypes: BooleanSetting def verbose: BooleanSetting def uniqid: BooleanSetting + def Yshowsymkinds: BooleanSetting def Xprintpos: BooleanSetting def Yrecursion: IntSetting def maxClassfileName: IntSetting diff --git a/src/compiler/scala/reflect/runtime/ConversionUtil.scala b/src/compiler/scala/reflect/runtime/ConversionUtil.scala index bd40200310..e75fd78590 100644 --- a/src/compiler/scala/reflect/runtime/ConversionUtil.scala +++ b/src/compiler/scala/reflect/runtime/ConversionUtil.scala @@ -17,36 +17,42 @@ trait ConversionUtil { self: SymbolTable => private val toScalaMap = new HashMap[J, S] private val toJavaMap = new HashMap[S, J] - def enter(j: J, s: S) = { + def enter(j: J, s: S) = synchronized { debugInfo("cached: "+j+"/"+s) toScalaMap(j) = s toJavaMap(s) = j } - def toScala(key: J)(body: => S): S = toScalaMap get key match { - case Some(v) => - v - case none => - val result = body - enter(key, result) - result + def toScala(key: J)(body: => S): S = synchronized { + toScalaMap get key match { + case Some(v) => + v + case none => + val result = body + enter(key, result) + result + } } - def toJava(key: S)(body: => J): J = toJavaMap get key match { - case Some(v) => - v - case none => - val result = body - enter(result, key) - result + def toJava(key: S)(body: => J): J = synchronized { + toJavaMap get key match { + case Some(v) => + v + case none => + val result = body + enter(result, key) + result + } } - def toJavaOption(key: S)(body: => Option[J]): Option[J] = toJavaMap get key match { - case None => - val result = body - for (value <- result) enter(value, key) - result - case some => some + def toJavaOption(key: S)(body: => Option[J]): Option[J] = synchronized { + toJavaMap get key match { + case None => + val result = body + for (value <- result) enter(value, key) + result + case some => some + } } } diff --git a/src/compiler/scala/reflect/runtime/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala index bc5d616ae3..4c49c0221f 100644 --- a/src/compiler/scala/reflect/runtime/JavaToScala.scala +++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala @@ -175,7 +175,7 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => load(sym) completeRest() } - def completeRest(): Unit = { + def completeRest(): Unit = self.synchronized { val tparams = clazz.rawInfo.typeParams val parents = try { @@ -241,16 +241,32 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => * The Scala owner of the Scala class corresponding to the Java class `jclazz` */ private def sOwner(jclazz: jClass[_]): Symbol = { - if (jclazz.isMemberClass) - followStatic(classToScala(jclazz.getEnclosingClass), jclazz.getModifiers) - else if (jclazz.isLocalClass) - methodToScala(jclazz.getEnclosingMethod) orElse constrToScala(jclazz.getEnclosingConstructor) - else if (jclazz.isPrimitive || jclazz.isArray) + if (jclazz.isMemberClass) { + val jEnclosingClass = jclazz.getEnclosingClass + val sEnclosingClass = classToScala(jEnclosingClass) + followStatic(sEnclosingClass, jclazz.getModifiers) + } else if (jclazz.isLocalClass) { + val jEnclosingMethod = jclazz.getEnclosingMethod + if (jEnclosingMethod != null) { + methodToScala(jEnclosingMethod) + } else { + val jEnclosingConstructor = jclazz.getEnclosingConstructor + constrToScala(jEnclosingConstructor) + } + } else if (jclazz.isPrimitive || jclazz.isArray) { ScalaPackageClass - else if (jclazz.getPackage != null) - packageToScala(jclazz.getPackage) - else + } else if (jclazz.getPackage != null) { + val jPackage = jclazz.getPackage + packageToScala(jPackage) + } else { + // @eb: a weird classloader might return a null package for something with a non-empty package name + // for example, http://groups.google.com/group/scala-internals/browse_thread/thread/7be09ff8f67a1e5c + // in that case we could invoke packageNameToScala(jPackageName) and, probably, be okay + // however, I think, it's better to blow up, since weirdness of the class loader might bite us elsewhere + val jPackageName = jclazz.getName.substring(0, Math.max(jclazz.getName.lastIndexOf("."), 0)) + assert(jPackageName == "") EmptyPackageClass + } } /** @@ -295,8 +311,10 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => * @return A Scala method object that corresponds to `jmeth`. */ def methodToScala(jmeth: jMethod): Symbol = methodCache.toScala(jmeth) { - val owner = followStatic(classToScala(jmeth.getDeclaringClass), jmeth.getModifiers) - lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth) + val jOwner = jmeth.getDeclaringClass + var sOwner = classToScala(jOwner) + sOwner = followStatic(sOwner, jmeth.getModifiers) + lookup(sOwner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth) } /** @@ -344,6 +362,18 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => pkg.moduleClass } + private def scalaSimpleName(jclazz: jClass[_]): TypeName = { + val owner = sOwner(jclazz) + val enclosingClass = jclazz.getEnclosingClass + var prefix = if (enclosingClass != null) enclosingClass.getName else "" + val isObject = owner.isModuleClass && !owner.isPackageClass + if (isObject && !prefix.endsWith(nme.MODULE_SUFFIX_STRING)) prefix += nme.MODULE_SUFFIX_STRING + assert(jclazz.getName.startsWith(prefix)) + var name = jclazz.getName.substring(prefix.length) + name = name.substring(name.lastIndexOf(".") + 1) + newTypeName(name) + } + /** * The Scala class that corresponds to a given Java class. * @param jclazz The Java class @@ -353,28 +383,54 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => */ def classToScala(jclazz: jClass[_]): Symbol = classCache.toScala(jclazz) { val jname = javaTypeName(jclazz) - def lookup = sOwner(jclazz).info.decl(newTypeName(jclazz.getSimpleName)) - - if (jclazz.isMemberClass && !nme.isImplClassName(jname)) { - val sym = lookup - assert(sym.isType, sym+"/"+jclazz+"/"+sOwner(jclazz)+"/"+jclazz.getSimpleName) - sym.asInstanceOf[ClassSymbol] - } - else if (jclazz.isLocalClass || invalidClassName(jname)) { - // local classes and implementation classes not preserved by unpickling - treat as Java - jclassAsScala(jclazz) - } - else if (jclazz.isArray) { - ArrayClass + val owner = sOwner(jclazz) + val simpleName = scalaSimpleName(jclazz) + + val sym = { + def lookup = { + def coreLookup(name: Name): Symbol = { + val sym = owner.info.decl(name) + sym orElse { + if (name.startsWith(nme.NAME_JOIN_STRING)) + coreLookup(name.subName(1, name.length)) + else + NoSymbol + } + } + + if (nme.isModuleName(simpleName)) { + val moduleName = nme.stripModuleSuffix(simpleName).toTermName + val sym = coreLookup(moduleName) + if (sym == NoSymbol) sym else sym.moduleClass + } else { + coreLookup(simpleName) + } + } + + if (jclazz.isMemberClass && !nme.isImplClassName(jname)) { + lookup + } else if (jclazz.isLocalClass || invalidClassName(jname)) { + // local classes and implementation classes not preserved by unpickling - treat as Java + jclassAsScala(jclazz) + } else if (jclazz.isArray) { + ArrayClass + } else javaTypeToValueClass(jclazz) orElse { + // jclazz is top-level - get signature + lookup + // val (clazz, module) = createClassModule( + // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _)) + // classCache enter (jclazz, clazz) + // clazz + } } - else javaTypeToValueClass(jclazz) orElse { - // jclazz is top-level - get signature - lookup - // val (clazz, module) = createClassModule( - // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _)) - // classCache enter (jclazz, clazz) - // clazz + + if (!sym.isType) { + def msgNoSym = "no symbol could be loaded from %s (scala equivalent is %s) by name %s".format(owner, jclazz, simpleName) + def msgIsNotType = "not a type: symbol %s loaded from %s (scala equivalent is %s) by name %s".format(sym, owner, jclazz, simpleName) + assert(false, if (sym == NoSymbol) msgNoSym else msgIsNotType) } + + sym.asInstanceOf[ClassSymbol] } /** @@ -453,7 +509,7 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable => private def jclassAsScala(jclazz: jClass[_]): Symbol = jclassAsScala(jclazz, sOwner(jclazz)) private def jclassAsScala(jclazz: jClass[_], owner: Symbol): Symbol = { - val name = newTypeName(jclazz.getSimpleName) + val name = scalaSimpleName(jclazz) val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, jclazz) val (clazz, module) = createClassModule(owner, name, completer) classCache enter (jclazz, clazz) diff --git a/src/compiler/scala/reflect/runtime/Loaders.scala b/src/compiler/scala/reflect/runtime/Loaders.scala index 7aca052fa9..4b35a5b37e 100644 --- a/src/compiler/scala/reflect/runtime/Loaders.scala +++ b/src/compiler/scala/reflect/runtime/Loaders.scala @@ -97,9 +97,9 @@ trait Loaders { self: SymbolTable => 0 < dp && dp < (name.length - 1) } - class PackageScope(pkgClass: Symbol) extends Scope { + class PackageScope(pkgClass: Symbol) extends Scope() with SynchronizedScope { assert(pkgClass.isType) - private var negatives = mutable.Set[Name]() + private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. override def lookupEntry(name: Name): ScopeEntry = { val e = super.lookupEntry(name) if (e != null) diff --git a/src/compiler/scala/reflect/runtime/Mirror.scala b/src/compiler/scala/reflect/runtime/Mirror.scala index 09a4bbe198..028a660a35 100644 --- a/src/compiler/scala/reflect/runtime/Mirror.scala +++ b/src/compiler/scala/reflect/runtime/Mirror.scala @@ -12,28 +12,28 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe import definitions._ - def classWithName(name: String): Symbol = { + def symbolForName(name: String): Symbol = { val clazz = javaClass(name, defaultReflectiveClassLoader()) classToScala(clazz) } - def getCompanionObject(clazz: Symbol): AnyRef = { + def companionInstance(clazz: Symbol): AnyRef = { val singleton = ReflectionUtils.singletonInstance(clazz.fullName, defaultReflectiveClassLoader()) singleton } - def getClass(obj: AnyRef): Symbol = classToScala(obj.getClass) - def getType(obj: AnyRef): Type = typeToScala(obj.getClass) + def symbolOfInstance(obj: Any): Symbol = classToScala(obj.getClass) + def typeOfInstance(obj: Any): Type = typeToScala(obj.getClass) // to do add getClass/getType for instances of primitive types, probably like this: // def getClass[T <: AnyVal : Manifest](x: T): Symbol = manifest[T].getClass - def getValue(receiver: AnyRef, field: Symbol): Any = { + def getValueOfField(receiver: AnyRef, field: Symbol): Any = { fieldToJava(field).get(receiver) } - def setValue(receiver: AnyRef, field: Symbol, value: Any): Unit = { + def setValueOfField(receiver: AnyRef, field: Symbol, value: Any): Unit = { fieldToJava(field).set(receiver, value) } - def invoke(receiver: AnyRef, meth: Symbol, args: Any*): Any = { + def invoke(receiver: AnyRef, meth: Symbol)(args: Any*): Any = { if (meth.owner == ArrayClass) { meth.name match { case nme.length => return Array.getLength(receiver) @@ -51,7 +51,8 @@ class Mirror extends Universe with RuntimeTypes with TreeBuildUtil with ToolBoxe override def typeToClass(tpe: Type): java.lang.Class[_] = typeToJavaClass(tpe) override def symbolToClass(sym: Symbol): java.lang.Class[_] = classToJava(sym) - + + override def inReflexiveMirror = true } object Mirror extends Mirror diff --git a/src/compiler/scala/reflect/runtime/Settings.scala b/src/compiler/scala/reflect/runtime/Settings.scala index 2a6cdea519..b4f0123114 100644 --- a/src/compiler/scala/reflect/runtime/Settings.scala +++ b/src/compiler/scala/reflect/runtime/Settings.scala @@ -28,6 +28,7 @@ class Settings extends internal.settings.MutableSettings { val explaintypes = new BooleanSetting(false) val verbose = new BooleanSetting(false) val uniqid = new BooleanSetting(false) + val Yshowsymkinds = new BooleanSetting(false) val Xprintpos = new BooleanSetting(false) val printtypes = new BooleanSetting(false) val Yrecursion = new IntSetting(0) diff --git a/src/compiler/scala/reflect/runtime/SymbolTable.scala b/src/compiler/scala/reflect/runtime/SymbolTable.scala index d1a806bcef..5331f0a53e 100644 --- a/src/compiler/scala/reflect/runtime/SymbolTable.scala +++ b/src/compiler/scala/reflect/runtime/SymbolTable.scala @@ -6,7 +6,7 @@ package runtime * It can be used either from the reflexive mirror itself (class Universe), or else from * a runtime compiler that uses reflection to get a class information (class scala.tools.nsc.ReflectGlobal) */ -trait SymbolTable extends internal.SymbolTable with JavaToScala with ScalaToJava with Loaders { +trait SymbolTable extends internal.SymbolTable with JavaToScala with ScalaToJava with Loaders with SynchronizedOps { /** If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package * <owner>.<name>, otherwise return NoSymbol. diff --git a/src/compiler/scala/reflect/runtime/SynchronizedOps.scala b/src/compiler/scala/reflect/runtime/SynchronizedOps.scala new file mode 100644 index 0000000000..72adbd4004 --- /dev/null +++ b/src/compiler/scala/reflect/runtime/SynchronizedOps.scala @@ -0,0 +1,51 @@ +package scala.reflect +package runtime + +trait SynchronizedOps extends internal.SymbolTable + with SynchronizedSymbols + with SynchronizedTypes { self: SymbolTable => + +// Names + + private lazy val nameLock = new Object + + override def newTermName(s: String): TermName = nameLock.synchronized { super.newTermName(s) } + override def newTypeName(s: String): TypeName = nameLock.synchronized { super.newTypeName(s) } + +// BaseTypeSeqs + + override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = + new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + + trait SynchronizedBaseTypeSeq extends BaseTypeSeq { + override def apply(i: Int): Type = synchronized { super.apply(i) } + override def rawElem(i: Int) = synchronized { super.rawElem(i) } + override def typeSymbol(i: Int): Symbol = synchronized { super.typeSymbol(i) } + override def toList: List[Type] = synchronized { super.toList } + override def copy(head: Type, offset: Int): BaseTypeSeq = synchronized { super.copy(head, offset) } + override def map(f: Type => Type): BaseTypeSeq = synchronized { super.map(f) } + override def exists(p: Type => Boolean): Boolean = synchronized { super.exists(p) } + override lazy val maxDepth = synchronized { maxDepthOfElems } + override def toString = synchronized { super.toString } + + override def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + } + +// Scopes + + override def newScope = new Scope() with SynchronizedScope + override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope + + trait SynchronizedScope extends Scope { + override def isEmpty: Boolean = synchronized { super.isEmpty } + override def size: Int = synchronized { super.size } + override def enter(sym: Symbol) = synchronized { super.enter(sym) } + override def rehash(sym: Symbol, newname: Name) = synchronized { super.rehash(sym, newname) } + override def unlink(e: ScopeEntry) = synchronized { super.unlink(e) } + override def unlink(sym: Symbol) = synchronized { super.unlink(sym) } + override def lookupAll(name: Name) = synchronized { super.lookupAll(name) } + override def lookupEntry(name: Name) = synchronized { super.lookupEntry(name) } + override def lookupNextEntry(entry: ScopeEntry) = synchronized { super.lookupNextEntry(entry) } + override def toList: List[Symbol] = synchronized { super.toList } + } +} diff --git a/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala new file mode 100644 index 0000000000..9baf94f71d --- /dev/null +++ b/src/compiler/scala/reflect/runtime/SynchronizedSymbols.scala @@ -0,0 +1,119 @@ +package scala.reflect +package runtime + +import internal.Flags.DEFERRED + +trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => + + override protected def nextId() = synchronized { super.nextId() } + + override protected def freshExistentialName(suffix: String) = + synchronized { super.freshExistentialName(suffix) } + + // Set the fields which point companions at one another. Returns the module. + override def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = + synchronized { super.connectModuleToClass(m, moduleClass) } + + override def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = + new FreeVar(name, value) with SynchronizedTermSymbol initFlags newFlags setInfo tpe + + override protected def makeNoSymbol = new NoSymbol with SynchronizedSymbol + + trait SynchronizedSymbol extends Symbol { + + override def rawowner = synchronized { super.rawowner } + override def rawname = synchronized { super.rawname } + override def rawflags = synchronized { super.rawflags } + + override def rawflags_=(x: FlagsType) = synchronized { super.rawflags_=(x) } + override def name_=(x: Name) = synchronized { super.name_=(x) } + override def owner_=(owner: Symbol) = synchronized { super.owner_=(owner) } + + override def validTo = synchronized { super.validTo } + override def validTo_=(x: Period) = synchronized { super.validTo_=(x) } + + override def pos = synchronized { super.pos } + override def setPos(pos: Position): this.type = { synchronized { super.setPos(pos) }; this } + + override def privateWithin = synchronized { super.privateWithin } + override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } + + override def info = synchronized { super.info } + override def info_=(info: Type) = synchronized { super.info_=(info) } + override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } + override def rawInfo: Type = synchronized { super.rawInfo } + + override def typeParams: List[Symbol] = synchronized { super.typeParams } + + override def reset(completer: Type) = synchronized { super.reset(completer) } + + override def infosString: String = synchronized { super.infosString } + + override def annotations: List[AnnotationInfo] = synchronized { super.annotations } + override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } + + +// ------ creators ------------------------------------------------------------------- + + override def newTermSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = + new TermSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags + + override def newAbstractTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AbstractTypeSymbol = + new AbstractTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags + + override def newAliasTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AliasTypeSymbol = + new AliasTypeSymbol(this, pos, name) with SynchronizedTypeSymbol initFlags newFlags + + override def newModuleSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = + new ModuleSymbol(this, pos, name) with SynchronizedTermSymbol initFlags newFlags + + override def newMethodSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): MethodSymbol = + new MethodSymbol(this, pos, name) with SynchronizedMethodSymbol initFlags newFlags + + override def newClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = + new ClassSymbol(this, pos, name) with SynchronizedClassSymbol initFlags newFlags + + override def newModuleClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = + new ModuleClassSymbol(this, pos, name) with SynchronizedModuleClassSymbol initFlags newFlags + + override def newTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position = NoPosition, newFlags: Long = 0L): TypeSkolem = + if ((newFlags & DEFERRED) == 0L) + new TypeSkolem(this, pos, name, origin) with SynchronizedTypeSymbol initFlags newFlags + else + new TypeSkolem(this, pos, name, origin) with AbstractTypeMixin with SynchronizedTypeSymbol initFlags newFlags + } + +// ------- subclasses --------------------------------------------------------------------- + + trait SynchronizedTermSymbol extends TermSymbol with SynchronizedSymbol { + override def referenced: Symbol = synchronized { super.referenced } + override def referenced_=(x: Symbol) = synchronized { super.referenced_=(x) } + } + + trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { + override def typeAsMemberOf(pre: Type): Type = synchronized { super.typeAsMemberOf(pre) } + } + + trait SynchronizedTypeSymbol extends TypeSymbol with SynchronizedSymbol { + override def typeConstructor: Type = synchronized { super.typeConstructor } + override def tpe: Type = synchronized { super.tpe } + } + + trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { + override def sourceFile = synchronized { super.sourceFile } + override def sourceFile_=(f: AbstractFileType) = synchronized { super.sourceFile_=(f) } + override def thisSym: Symbol = synchronized { super.thisSym } + override def thisType: Type = synchronized { super.thisType } + override def typeOfThis: Type = synchronized { super.typeOfThis } + override def typeOfThis_=(tp: Type) = synchronized { super.typeOfThis_=(tp) } + override def children = synchronized { super.children } + override def addChild(sym: Symbol) = synchronized { super.addChild(sym) } + } + + trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { + override def sourceModule = synchronized { super.sourceModule } + override def sourceModule_=(module: Symbol) = synchronized { super.sourceModule_=(module: Symbol) } + override def implicitMembers: List[Symbol] = synchronized { super.implicitMembers } + } +} + diff --git a/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala b/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala new file mode 100644 index 0000000000..c842d3dd01 --- /dev/null +++ b/src/compiler/scala/reflect/runtime/SynchronizedTypes.scala @@ -0,0 +1,87 @@ +package scala.reflect +package runtime + +/** This trait overrides methods in reflect.internal, bracketing + * them in synchronized { ... } to make them thread-safe + */ +trait SynchronizedTypes extends internal.Types { self: SymbolTable => + + // No sharing of map objects: + override protected def commonOwnerMap = new CommonOwnerMap + + private val uniqueLock = new Object + override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) } + + class SynchronizedUndoLog extends UndoLog { + + override def clear() = + synchronized { super.clear() } + + override def undo[T](block: => T): T = + synchronized { super.undo(block) } + + override def undoUnless(block: => Boolean): Boolean = + synchronized { super.undoUnless(block) } + } + + override protected def newUndoLog = new SynchronizedUndoLog + + override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = + synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } + + private val subsametypeLock = new Object + + override def isSameType(tp1: Type, tp2: Type): Boolean = + subsametypeLock.synchronized { super.isSameType(tp1, tp2) } + + override def isDifferentType(tp1: Type, tp2: Type): Boolean = + subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } + + override def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = + subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } + + private val lubglbLock = new Object + + override def glb(ts: List[Type]): Type = + lubglbLock.synchronized { super.glb(ts) } + + override def lub(ts: List[Type]): Type = + lubglbLock.synchronized { super.lub(ts) } + + private val indentLock = new Object + + override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { + indentLock.synchronized { super.explain(op, p, tp1, arg2) } + } + + private val toStringLock = new Object + + override protected def typeToString(tpe: Type): String = + toStringLock.synchronized(super.typeToString(tpe)) + + /* The idea of caches is as follows. + * When in reflexive mode, a cache is either null, or one sentinal + * value representing undefined or the final defined + * value. Hence, we can ask in non-synchronized ode whether the cache field + * is non null and different from the sentinel (if a sentinel exists). + * If that's true, the cache value is current. + * Otherwise we arrive in one of the defined... methods listed below + * which go through all steps in synchronized mode. + */ + + override protected def defineUnderlyingOfSingleType(tpe: SingleType) = + tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } + + override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = + tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } + + override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = + tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } + + override protected def defineParentsOfTypeRef(tpe: TypeRef) = + tpe.synchronized { super.defineParentsOfTypeRef(tpe) } + + override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = + tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } + +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala index 8afd6d2231..f52662ce6f 100644 --- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala +++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala @@ -44,31 +44,31 @@ trait ToolBoxes extends { self: Universe => // !!! Why is this is in the empty package? If it's only to make // it inaccessible then please put it somewhere designed for that // rather than polluting the empty package with synthetics. + trace("typing: ")(showAttributed(tree, true, true, settings.Yshowsymkinds.value)) val ownerClass = EmptyPackageClass.newClassWithInfo(newTypeName("<expression-owner>"), List(ObjectClass.tpe), newScope) val owner = ownerClass.newLocalDummy(tree.pos) - - typer.atOwner(tree, owner).typed(tree, analyzer.EXPRmode, pt) + val ttree = typer.atOwner(tree, owner).typed(tree, analyzer.EXPRmode, pt) + trace("typed: ")(showAttributed(ttree, true, true, settings.Yshowsymkinds.value)) + ttree } - + def defOwner(tree: Tree): Symbol = tree find (_.isDef) map (_.symbol) match { case Some(sym) if sym != null && sym != NoSymbol => sym.owner case _ => NoSymbol } - + def wrapInObject(expr: Tree, fvs: List[Symbol]): ModuleDef = { val obj = EmptyPackageClass.newModule(nextWrapperModuleName()) - val minfo = ClassInfoType(List(ObjectClass.tpe, ScalaObjectClass.tpe), new Scope, obj.moduleClass) + val minfo = ClassInfoType(List(ObjectClass.tpe, ScalaObjectClass.tpe), newScope, obj.moduleClass) obj.moduleClass setInfo minfo obj setInfo obj.moduleClass.tpe val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) def makeParam(fv: Symbol) = meth.newValueParameter(fv.name.toTermName) setInfo fv.tpe - meth setInfo MethodType(fvs map makeParam, expr.tpe) + meth setInfo MethodType(fvs map makeParam, AnyClass.tpe) minfo.decls enter meth trace("wrapping ")(defOwner(expr) -> meth) val methdef = DefDef(meth, expr changeOwner (defOwner(expr) -> meth)) - trace("wrapped: ")(showAttributed(methdef)) - resetAllAttrs( - ModuleDef( + val moduledef = ModuleDef( obj, Template( List(TypeTree(ObjectClass.tpe)), @@ -77,7 +77,11 @@ trait ToolBoxes extends { self: Universe => List(), List(List()), List(methdef), - NoPosition))) + NoPosition)) + trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) + val cleanedUp = resetLocalAttrs(moduledef) + trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) + cleanedUp } def wrapInPackage(clazz: Tree): PackageDef = @@ -90,8 +94,22 @@ trait ToolBoxes extends { self: Universe => } def compileExpr(expr: Tree, fvs: List[Symbol]): String = { + // Previously toolboxes used to typecheck their inputs before compiling. + // Actually, the initial demo by Martin first typechecked the reified tree, + // then ran it, which typechecked it again, and only then launched the + // reflective compiler. + // + // However, as observed in https://issues.scala-lang.org/browse/SI-5464 + // current implementation typechecking is not always idempotent. + // That's why we cannot allow inputs of toolboxes to be typechecked, + // at least not until the aforementioned issue is closed. + val typed = expr filter (t => t.tpe != null && t.tpe != NoType && !t.isInstanceOf[TypeTree]) + if (!typed.isEmpty) { + throw new Error("cannot compile trees that are already typed") + } + val mdef = wrapInObject(expr, fvs) - val pdef = trace("wrapped: ")(wrapInPackage(mdef)) + val pdef = wrapInPackage(mdef) val unit = wrapInCompilationUnit(pdef) val run = new Run run.compileUnits(List(unit), run.namerPhase) @@ -102,36 +120,44 @@ trait ToolBoxes extends { self: Universe => jclazz.getDeclaredMethods.find(_.getName == name).get def runExpr(expr: Tree): Any = { - val etpe = expr.tpe val fvs = (expr filter isFree map (_.symbol)).distinct - + reporter.reset() val className = compileExpr(expr, fvs) if (reporter.hasErrors) { throw new Error("reflective compilation has failed") } - + if (settings.debug.value) println("generated: "+className) val jclazz = jClass.forName(moduleFileName(className), true, classLoader) val jmeth = jclazz.getDeclaredMethods.find(_.getName == wrapperMethodName).get val jfield = jclazz.getDeclaredFields.find(_.getName == NameTransformer.MODULE_INSTANCE_NAME).get val singleton = jfield.get(null) - val result = jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*) - if (etpe.typeSymbol != FunctionClass(0)) result - else { - val applyMeth = result.getClass.getMethod("apply") - applyMeth.invoke(result) - } + // @odersky writes: Not sure we will be able to drop this. I forgot the reason why we dereference () functions, + // but there must have been one. So I propose to leave old version in comments to be resurrected if the problem resurfaces. +// val result = jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*) +// if (etpe.typeSymbol != FunctionClass(0)) result +// else { +// val applyMeth = result.getClass.getMethod("apply") +// applyMeth.invoke(result) +// } + jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*) } - - def showAttributed(tree: Tree): String = { - val saved = settings.printtypes.value + + def showAttributed(tree: Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = { + val saved1 = settings.printtypes.value + val saved2 = settings.uniqid.value + val saved3 = settings.Yshowsymkinds.value try { - settings.printtypes.value = true - //settings.uniqid.value = true + settings.printtypes.value = printTypes + settings.uniqid.value = printIds + settings.Yshowsymkinds.value = printKinds tree.toString - } finally - compiler.settings.printtypes.value = saved + } finally { + settings.printtypes.value = saved1 + settings.uniqid.value = saved2 + settings.Yshowsymkinds.value = saved3 + } } } @@ -151,7 +177,13 @@ trait ToolBoxes extends { self: Universe => } command.settings.outputDirs setSingleOutput virtualDirectory - new ToolBoxGlobal(command.settings, reporter) + val instance = new ToolBoxGlobal(command.settings, reporter) + + // need to establish a run an phase because otherwise we run into an assertion in TypeHistory + // that states that the period must be different from NoPeriod + val run = new instance.Run + instance.phase = run.refchecksPhase + instance } lazy val importer = new compiler.Importer { @@ -161,36 +193,26 @@ trait ToolBoxes extends { self: Universe => lazy val exporter = importer.reverse lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, defaultReflectiveClassLoader) - - private def importAndTypeCheck(tree: rm.Tree, expectedType: rm.Type): compiler.Tree = { - // need to establish a run an phase because otherwise we run into an assertion in TypeHistory - // that states that the period must be different from NoPeriod - val run = new compiler.Run - compiler.phase = run.refchecksPhase - val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) - val pt: compiler.Type = importer.importType(expectedType.asInstanceOf[Type]) -// val typer = compiler.typer.atOwner(ctree, if (owner.isModule) cowner.moduleClass else cowner) - val ttree: compiler.Tree = compiler.typedTopLevelExpr(ctree, pt) - ttree - } def typeCheck(tree: rm.Tree, expectedType: rm.Type): rm.Tree = { if (compiler.settings.verbose.value) println("typing "+tree+", pt = "+expectedType) - val ttree = importAndTypeCheck(tree, expectedType) - exporter.importTree(ttree).asInstanceOf[rm.Tree] + val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) + val pt: compiler.Type = importer.importType(expectedType.asInstanceOf[Type]) + val ttree: compiler.Tree = compiler.typedTopLevelExpr(ctree, pt) + val rmttree = exporter.importTree(ttree).asInstanceOf[rm.Tree] + rmttree } def typeCheck(tree: rm.Tree): rm.Tree = typeCheck(tree, WildcardType.asInstanceOf[rm.Type]) - def showAttributed(tree: rm.Tree): String = - compiler.showAttributed(importer.importTree(tree.asInstanceOf[Tree])) + def showAttributed(tree: rm.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = + compiler.showAttributed(importer.importTree(tree.asInstanceOf[Tree]), printTypes, printIds, printKinds) - def runExpr(tree: rm.Tree, expectedType: rm.Type): Any = { - val ttree = importAndTypeCheck(tree, expectedType) - compiler.runExpr(ttree) + def runExpr(tree: rm.Tree): Any = { + if (compiler.settings.verbose.value) println("running "+tree) + val ctree: compiler.Tree = importer.importTree(tree.asInstanceOf[Tree]) + compiler.runExpr(ctree) } - - def runExpr(tree: rm.Tree): Any = runExpr(tree, WildcardType.asInstanceOf[rm.Type]) } } diff --git a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala b/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala index fc4177e956..61001a4778 100644 --- a/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala +++ b/src/compiler/scala/reflect/runtime/TreeBuildUtil.scala @@ -2,10 +2,12 @@ package scala.reflect package runtime trait TreeBuildUtil extends Universe with api.TreeBuildUtil { - - def staticClass(fullname: String): Symbol = definitions.getRequiredClass(fullname) - def staticModule(fullname: String): Symbol = definitions.getRequiredModule(fullname) - def thisModuleType(fullname: String) = staticModule(fullname).moduleClass.thisType + /** A comment to the effect of why initialize was added to all these + * would be appreciated. (We may as well start somewhere.) + */ + def staticClass(fullname: String) = definitions.getRequiredClass(fullname).initialize + def staticModule(fullname: String) = definitions.getRequiredModule(fullname).initialize + def thisModuleType(fullname: String) = staticModule(fullname).moduleClass.initialize.thisType /** Selects type symbol with given name from the defined members of prefix type */ @@ -39,7 +41,7 @@ trait TreeBuildUtil extends Universe with api.TreeBuildUtil { selectIn(owner.info, idx) } - def freeVar(name: String, info: Type, value: Any) = newFreeVar(newTermName(name), info, value) + def newFreeVar(name: String, info: Type, value: Any) = newFreeVar(newTermName(name), info, value) def modifiersFromInternalFlags(flags: Long, privateWithin: Name, annotations: List[Tree]): Modifiers = Modifiers(flags, privateWithin, annotations) diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index c786bb86c5..700f819226 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -16,7 +16,7 @@ class Universe extends SymbolTable { val gen = new TreeGen { val global: Universe.this.type = Universe.this } - def settings = new Settings + lazy val settings = new Settings def forInteractive = false def forScaladoc = false diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index 04ff0c440d..3c79fcd3fb 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -90,7 +90,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for properties that refer to compiler phases. */ object CompilerPhase extends PermissibleValue { - val values = List("namer", "typer", "pickler", "refchecks", "liftcode", + val values = List("namer", "typer", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "lazyvals", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "icode", "inliner", diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index 253d1dec5d..92d6e6320c 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -43,7 +43,8 @@ import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} * - `deprecation`, * - `docgenerator`, * - `docrootcontent`, - * - `unchecked`. + * - `unchecked`, + * - `nofail`. * * It also takes the following parameters as nested elements: * - `src` (for srcdir), @@ -122,7 +123,10 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the compiler to generate unchecked information. */ private var unchecked: Boolean = false - + + /** Instruct the ant task not to fail in the event of errors */ + private var nofail: Boolean = false + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ @@ -352,6 +356,17 @@ class Scaladoc extends ScalaMatchingTask { def setDocUncompilable(input: String) { docUncompilable = Some(input) } + + /** Set the `nofail` info attribute. + * + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. + */ + def setNoFail(input: String) { + if (Flag.isPermissible(input)) + nofail = "yes".equals(input) || "on".equals(input) + else + buildError("Unknown nofail flag '" + input + "'") + } /*============================================================================*\ ** Properties getters ** @@ -553,6 +568,8 @@ class Scaladoc extends ScalaMatchingTask { Pair(docSettings, sourceFiles) } + def safeBuildError(message: String): Unit = if (nofail) log(message) else buildError(message) + /** Performs the compilation. */ override def execute() = { val Pair(docSettings, sourceFiles) = initialize @@ -561,7 +578,7 @@ class Scaladoc extends ScalaMatchingTask { val docProcessor = new scala.tools.nsc.doc.DocFactory(reporter, docSettings) docProcessor.document(sourceFiles.map (_.toString)) if (reporter.ERROR.count > 0) - buildError( + safeBuildError( "Document failed with " + reporter.ERROR.count + " error" + (if (reporter.ERROR.count > 1) "s" else "") + @@ -576,11 +593,11 @@ class Scaladoc extends ScalaMatchingTask { } catch { case exception: Throwable if exception.getMessage ne null => exception.printStackTrace() - buildError("Document failed because of an internal documenter error (" + + safeBuildError("Document failed because of an internal documenter error (" + exception.getMessage + "); see the error output for details.") case exception => exception.printStackTrace() - buildError("Document failed because of an internal documenter error " + + safeBuildError("Document failed because of an internal documenter error " + "(no error message provided); see the error output for details.") } } diff --git a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala index a39de64c5a..5199e273d7 100644 --- a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala +++ b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala @@ -13,6 +13,7 @@ import java.io.{ File, FileWriter } import org.apache.tools.ant.Project import org.apache.tools.ant.taskdefs.Java import org.apache.tools.ant.util.{ GlobPatternMapper, SourceFileScanner } +import org.apache.tools.ant.BuildException import scala.tools.nsc.io import scala.tools.nsc.util.ScalaClassLoader @@ -150,7 +151,7 @@ class ScalacFork extends ScalaMatchingTask with ScalacShared with TaskArgs { val res = execWithArgFiles(java, paths) if (failOnError && res != 0) - sys.error("Compilation failed because of an internal compiler error;"+ + throw new BuildException("Compilation failed because of an internal compiler error;"+ " see the error output for details.") } } diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala index 8f82c997db..7c9599dc45 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -30,7 +30,7 @@ trait AnyValReps { " * @return the bitwise negation of this value\n" + " * @example {{{\n" + " * ~5 == -6\n" + - " * // in binary: ~00000101 == \n" + + " * // in binary: ~00000101 ==\n" + " * // 11111010\n" + " * }}}\n" + " */") :: ops @@ -44,9 +44,9 @@ trait AnyValReps { " * @return the bitwise OR of this value and x\n" + " * @example {{{\n" + " * (0xf0 | 0xaa) == 0xfa\n" + - " * // in binary: 11110000 \n" + - " * // | 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // | 10101010\n" + + " * // --------\n" + " * // 11111010\n" + " * }}}\n" + " */"), @@ -54,9 +54,9 @@ trait AnyValReps { " * @return the bitwise AND of this value and x\n" + " * @example {{{\n" + " * (0xf0 & 0xaa) == 0xa0\n" + - " * // in binary: 11110000 \n" + - " * // & 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // & 10101010\n" + + " * // --------\n" + " * // 10100000\n" + " * }}}\n" + " */"), @@ -64,9 +64,9 @@ trait AnyValReps { " * @return the bitwise XOR of this value and x\n" + " * @example {{{\n" + " * (0xf0 ^ 0xaa) == 0x5a\n" + - " * // in binary: 11110000 \n" + - " * // ^ 10101010 \n" + - " * // -------- \n" + + " * // in binary: 11110000\n" + + " * // ^ 10101010\n" + + " * // --------\n" + " * // 01011010\n" + " * }}}\n" + " */")) @@ -83,11 +83,11 @@ trait AnyValReps { Op(">>>", "/**\n" + " * @return this value bit-shifted right by the specified number of bits,\n" + - " * filling the new left bits with zeroes. \n" + + " * filling the new left bits with zeroes.\n" + " * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}}\n" + " * @example {{{\n" + - " * -21 >>> 3 == 536870909 \n" + - " * // in binary: 11111111 11111111 11111111 11101011 >>> 3 == \n" + + " * -21 >>> 3 == 536870909\n" + + " * // in binary: 11111111 11111111 11111111 11101011 >>> 3 ==\n" + " * // 00011111 11111111 11111111 11111101\n" + " * }}}\n" + " */"), @@ -97,8 +97,8 @@ trait AnyValReps { " * filling in the right bits with the same value as the left-most bit of this.\n" + " * The effect of this is to retain the sign of the value.\n" + " * @example {{{\n" + - " * -21 >> 3 == -3 \n" + - " * // in binary: 11111111 11111111 11111111 11101011 >> 3 == \n" + + " * -21 >> 3 == -3\n" + + " * // in binary: 11111111 11111111 11111111 11101011 >> 3 ==\n" + " * // 11111111 11111111 11111111 11111101\n" + " * }}}\n" + " */")) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index e805b4e75e..4493188b31 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -13,7 +13,7 @@ import scala.tools.util.{ Profiling, PathResolver } import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ NoPosition, Exceptional, ClassPath, SourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, ScalaClassLoader, returning } +import util.{ NoPosition, Exceptional, ClassPath, SourceFile, NoSourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, ScalaClassLoader, returning } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import settings.{ AestheticSettings } @@ -37,6 +37,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb with Plugins with PhaseAssembly with Trees + with Reifiers with TreePrinters with DocComments with MacroContext @@ -124,7 +125,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Print tree in detailed form */ object nodePrinters extends { val global: Global.this.type = Global.this - } with NodePrinters { + } with NodePrinters with ReifyPrinters { infolevel = InfoLevel.Verbose } @@ -134,6 +135,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } with TreeBrowsers val nodeToString = nodePrinters.nodeToString + val reifiedNodeToString = nodePrinters.reifiedNodeToString val treeBrowser = treeBrowsers.create() // ------------ Hooks for interactive mode------------------------- @@ -164,6 +166,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb if (opt.fatalWarnings) globalError(msg) else reporter.warning(NoPosition, msg) + // Getting in front of Predef's asserts to supplement with more info. + // This has the happy side effect of masking the one argument forms + // of assert and require (but for now I've reproduced them here, + // because there are a million to fix.) + @inline final def assert(assertion: Boolean, message: => Any) { + Predef.assert(assertion, supplementErrorMessage("" + message)) + } + @inline final def assert(assertion: Boolean) { + assert(assertion, "") + } + @inline final def require(requirement: Boolean, message: => Any) { + Predef.require(requirement, supplementErrorMessage("" + message)) + } + @inline final def require(requirement: Boolean) { + require(requirement, "") + } + // Needs to call error to make sure the compile fails. override def abort(msg: String): Nothing = { error(msg) @@ -375,10 +394,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } final def applyPhase(unit: CompilationUnit) { + if ((unit ne null) && unit.exists) + lastSeenSourceFile = unit.source + if (opt.echoFilenames) inform("[running phase " + name + " on " + unit + "]") - val unit0 = currentRun.currentUnit + val unit0 = currentUnit try { currentRun.currentUnit = unit if (!cancelled(unit)) { @@ -387,7 +409,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } currentRun.advanceUnit } finally { - //assert(currentRun.currentUnit == unit) + //assert(currentUnit == unit) currentRun.currentUnit = unit0 } } @@ -437,17 +459,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb val runsRightAfter = None } with RefChecks - // phaseName = "liftcode" - object liftcode extends { - val global: Global.this.type = Global.this - val runsAfter = List[String]("refchecks") - val runsRightAfter = None - } with LiftCode - // phaseName = "uncurry" override object uncurry extends { val global: Global.this.type = Global.this - val runsAfter = List[String]("refchecks", "liftcode") + val runsAfter = List[String]("refchecks") val runsRightAfter = None } with UnCurry @@ -615,7 +630,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb object icodeChecker extends icodeCheckers.ICodeChecker() object typer extends analyzer.Typer( - analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope) + analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, newScope) ) /** Add the internal compiler phases to the phases set. @@ -632,7 +647,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb superAccessors -> "add super accessors in traits and nested classes", pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", - liftcode -> "reify trees", uncurry -> "uncurry, translate function values to anonymous classes", tailCalls -> "replace tail calls by jumps", specializeTypes -> "@specialized-driven class and method specialization", @@ -781,9 +795,44 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb curRun = null } + /** There are common error conditions where when the exception hits + * here, currentRun.currentUnit is null. This robs us of the knowledge + * of what file was being compiled when it broke. Since I really + * really want to know, this hack. + */ + private var lastSeenSourceFile: SourceFile = NoSourceFile + /** The currently active run */ - def currentRun: Run = curRun + def currentRun: Run = curRun + def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit + def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile + + @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) + @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) + @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) + + /** Don't want to introduce new errors trying to report errors, + * so swallow exceptions. + */ + override def supplementErrorMessage(errorMessage: String): String = try { + """| + | while compiling: %s + | current phase: %s + | library version: %s + | compiler version: %s + | reconstructed args: %s + | + |%s""".stripMargin.format( + currentSource.path, + phase, + scala.util.Properties.versionString, + Properties.versionString, + settings.recreateArgs.mkString(" "), + if (opt.debug) "Current unit body:\n" + currentUnit.body + "\n" + errorMessage else errorMessage + ) + } + catch { case x: Exception => errorMessage } /** The id of the currently active run */ @@ -798,10 +847,40 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** A Run is a single execution of the compiler on a sets of units */ class Run { + /** Have been running into too many init order issues with Run + * during erroneous conditions. Moved all these vals up to the + * top of the file so at least they're not trivially null. + */ var isDefined = false + /** The currently compiled unit; set from GlobalPhase */ + var currentUnit: CompilationUnit = NoCompilationUnit + + /** Counts for certain classes of warnings during this run. */ + var deprecationWarnings: List[(Position, String)] = Nil + var uncheckedWarnings: List[(Position, String)] = Nil + + /** A flag whether macro expansions failed */ + var macroExpansionFailed = false + /** To be initialized from firstPhase. */ private var terminalPhase: Phase = NoPhase + private val unitbuf = new mutable.ListBuffer[CompilationUnit] + val compiledFiles = new mutable.HashSet[String] + + /** A map from compiled top-level symbols to their source files */ + val symSource = new mutable.HashMap[Symbol, AbstractFile] + + /** A map from compiled top-level symbols to their picklers */ + val symData = new mutable.HashMap[Symbol, PickleBuffer] + + private var phasec: Int = 0 // phases completed + private var unitc: Int = 0 // units completed this phase + private var _unitbufSize = 0 + + def size = _unitbufSize + override def toString = "scalac Run for:\n " + compiledFiles.toList.sorted.mkString("\n ") + // Calculate where to stop based on settings -Ystop-before or -Ystop-after. // Slightly complicated logic due to wanting -Ystop-before:parser to fail rather // than mysteriously running to completion. @@ -895,16 +974,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // --------------- Miscellania ------------------------------- - /** The currently compiled unit; set from GlobalPhase */ - var currentUnit: CompilationUnit = _ - - /** Counts for certain classes of warnings during this run. */ - var deprecationWarnings: List[(Position, String)] = Nil - var uncheckedWarnings: List[(Position, String)] = Nil - - /** A flag whether macro expansions failed */ - var macroExpansionFailed = false - /** Progress tracking. Measured in "progress units" which are 1 per * compilation unit per phase completed. * @@ -936,9 +1005,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } def cancel() { reporter.cancelled = true } - - private var phasec: Int = 0 // phases completed - private var unitc: Int = 0 // units completed this phase + private def currentProgress = (phasec * size) + unitc private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase private def refreshProgress() = if (size > 0) progress(currentProgress, totalProgress) @@ -977,11 +1044,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // ----------- Units and top-level classes and objects -------- - private val unitbuf = new mutable.ListBuffer[CompilationUnit] - val compiledFiles = new mutable.HashSet[String] - - private var _unitbufSize = 0 - def size = _unitbufSize /** add unit to be compiled in this run */ private def addUnit(unit: CompilationUnit) { @@ -1005,12 +1067,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb */ def units: Iterator[CompilationUnit] = unitbuf.iterator - /** A map from compiled top-level symbols to their source files */ - val symSource = new mutable.HashMap[Symbol, AbstractFile] - - /** A map from compiled top-level symbols to their picklers */ - val symData = new mutable.HashMap[Symbol, PickleBuffer] - def registerPickle(sym: Symbol): Unit = { // Convert all names to the type name: objects don't store pickled data if (opt.showPhase && (opt.showNames exists (x => findNamedMember(x.toTypeName, sym) != NoSymbol))) { @@ -1024,7 +1080,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def compiles(sym: Symbol): Boolean = if (sym == NoSymbol) false else if (symSource.isDefinedAt(sym)) true - else if (!sym.owner.isPackageClass) compiles(sym.toplevelClass) + else if (!sym.owner.isPackageClass) compiles(sym.enclosingTopLevelClass) else if (sym.isModuleClass) compiles(sym.sourceModule) else false @@ -1062,7 +1118,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x)) def snapshot() = { inform("\n[[symbol layout at end of " + phase + "]]") - atPhase(phase.next) { + afterPhase(phase) { trackers foreach { t => t.snapshot() inform(t.show("Heading from " + phase.prev.name + " to " + phase.name)) @@ -1114,6 +1170,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Compile list of units, starting with phase `fromPhase` */ def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { + try compileUnitsInternal(units, fromPhase) + catch { case ex => + globalError(supplementErrorMessage("uncaught exception during compilation: " + ex.getClass.getName)) + throw ex + } + } + + private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) { units foreach addUnit if (opt.profileAll) { inform("starting CPU profiling on compilation run") @@ -1329,7 +1393,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def printAllUnits() { print("[[syntax trees at end of " + phase + "]]") - atPhase(phase.next) { currentRun.units foreach (treePrinter.print(_)) } + afterPhase(phase) { currentRun.units foreach (treePrinter.print(_)) } } private def findMemberFromRoot(fullName: Name): Symbol = { diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index ea51fc0141..9466d1c1f2 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -71,34 +71,39 @@ abstract class NodePrinters { def nodeinfo(tree: Tree): String = if (infolevel == InfoLevel.Quiet) "" else { - val buf = new StringBuilder(" // sym=" + tree.symbol) - if (tree.hasSymbol) { - if (tree.symbol.isPrimaryConstructor) - buf.append(", isPrimaryConstructor") - else if (tree.symbol.isConstructor) - buf.append(", isConstructor") - if (tree.symbol != NoSymbol) - buf.append(", sym.owner=" + tree.symbol.owner) - buf.append(", sym.tpe=" + tree.symbol.tpe) - } - buf.append(", tpe=" + tree.tpe) - if (tree.tpe != null) { - var sym = tree.tpe.termSymbol - if (sym == NoSymbol) sym = tree.tpe.typeSymbol - buf.append(", tpe.sym=" + sym) - if (sym != NoSymbol) { - buf.append(", tpe.sym.owner=" + sym.owner) - if ((infolevel > InfoLevel.Normal) && - !(sym.owner eq definitions.ScalaPackageClass) && - !sym.isModuleClass && !sym.isPackageClass && - !sym.isJavaDefined) { - val members = for (m <- tree.tpe.decls) - yield m.toString() + ": " + m.tpe + ", " - buf.append(", tpe.decls=" + members) + try { + val buf = new StringBuilder(" // sym=" + tree.symbol) + if (tree.hasSymbol) { + if (tree.symbol.isPrimaryConstructor) + buf.append(", isPrimaryConstructor") + else if (tree.symbol.isConstructor) + buf.append(", isConstructor") + if (tree.symbol != NoSymbol) + buf.append(", sym.owner=" + tree.symbol.owner) + buf.append(", sym.tpe=" + tree.symbol.tpe) + } + buf.append(", tpe=" + tree.tpe) + if (tree.tpe != null) { + var sym = tree.tpe.termSymbol + if (sym == NoSymbol) sym = tree.tpe.typeSymbol + buf.append(", tpe.sym=" + sym) + if (sym != NoSymbol) { + buf.append(", tpe.sym.owner=" + sym.owner) + if ((infolevel > InfoLevel.Normal) && + !(sym.owner eq definitions.ScalaPackageClass) && + !sym.isModuleClass && !sym.isPackageClass && + !sym.isJavaDefined) { + val members = for (m <- tree.tpe.decls) + yield m.toString() + ": " + m.tpe + ", " + buf.append(", tpe.decls=" + members) + } } } + buf.toString + } catch { + case ex: Throwable => + return " // sym= <error> " + ex.getMessage } - buf.toString } def nodeinfo2(tree: Tree): String = (if (comma) "," else "") + nodeinfo(tree) diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala new file mode 100644 index 0000000000..21e075950f --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Reifiers.scala @@ -0,0 +1,761 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Gilles Dubochet + */ + +package scala.tools.nsc +package ast + +import symtab._ +import Flags._ +import scala.reflect.api.Modifier._ +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import scala.tools.nsc.util.FreshNameCreator +import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } + +/** Given a tree or type, generate a tree that when executed at runtime produces the original tree or type. + * See more info in the comments to `reify' in scala.reflect.macro.Context. + * + * @author Martin Odersky + * @version 2.10 + */ +trait Reifiers { self: Global => + + def reify(tree: Tree): Tree = { + class Reifier { + import definitions._ + import Reifier._ + + final val scalaPrefix = "scala." + final val localPrefix = "$local" + final val memoizerName = "$memo" + + val reifyDebug = settings.Yreifydebug.value + + private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree + private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms` + private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified + + private def definedInLiftedCode(tpe: Type) = + tpe exists (tp => boundSyms contains tp.typeSymbol) + + private def definedInLiftedCode(sym: Symbol) = + boundSyms contains sym + + /** + * Generate tree of the form + * + * { val $mr = scala.reflect.runtime.Mirror + * $local1 = new TypeSymbol(owner1, NoPosition, name1) + * ... + * $localN = new TermSymbol(ownerN, NoPositiion, nameN) + * $local1.setInfo(tpe1) + * ... + * $localN.setInfo(tpeN) + * $localN.setAnnotations(annotsN) + * rtree + * } + * + * where + * + * - `$localI` are free type symbols in the environment, as well as local symbols + * of refinement types. + * - `tpeI` are the info's of `symI` + * - `rtree` is code that generates `data` at runtime, maintaining all attributes. + * - `data` is typically a tree or a type. + */ + def reifyTopLevel(data: Any): Tree = { + val rtree = reify(data) + Block(mirrorAlias :: reifySymbolTableSetup, rtree) + } + + private def isLocatable(sym: Symbol) = + sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0 + + private def registerReifiableSymbol(sym: Symbol): Unit = + if (!(symIndex contains sym)) { + sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol + symIndex(sym) = reifiableSyms.length + reifiableSyms += sym + } + + // helper methods + + private def localName(sym: Symbol): TermName = + newTermName(localPrefix + symIndex(sym)) + + private def call(fname: String, args: Tree*): Tree = + Apply(termPath(fname), args.toList) + + private def mirrorSelect(name: String): Tree = + termPath(nme.MIRROR_PREFIX + name) + + private def mirrorCall(name: TermName, args: Tree*): Tree = + call("" + (nme.MIRROR_PREFIX append name), args: _*) + + private def mirrorCall(name: String, args: Tree*): Tree = + call(nme.MIRROR_PREFIX + name, args: _*) + + private def mirrorFactoryCall(value: Product, args: Tree*): Tree = + mirrorFactoryCall(value.productPrefix, args: _*) + + private def mirrorFactoryCall(prefix: String, args: Tree*): Tree = + mirrorCall(prefix, args: _*) + + private def scalaFactoryCall(name: String, args: Tree*): Tree = + call(scalaPrefix + name + ".apply", args: _*) + + private def mkList(args: List[Tree]): Tree = + scalaFactoryCall("collection.immutable.List", args: _*) + + private def reifyModifiers(m: Modifiers) = + mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations)) + + private def reifyAggregate(name: String, args: Any*) = + scalaFactoryCall(name, (args map reify).toList: _*) + + /** + * Reify a list + */ + private def reifyList(xs: List[Any]): Tree = + mkList(xs map reify) + + /** + * Reify an array + */ + private def reifyArray(xs: Array[_]): Tree = + // @xeno.by: doesn't work for Array(LiteralAnnotArg(...)) + // because we cannot generate manifests for path-dependent types + scalaFactoryCall(nme.Array, xs map reify: _*) + + /** Reify a name */ + private def reifyName(name: Name) = + mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString))) + + private def isFree(sym: Symbol) = + !(symIndex contains sym) + + /** + * Reify a reference to a symbol + */ + private def reifySymRef(sym: Symbol): Tree = { + symIndex get sym match { + case Some(idx) => + Ident(localName(sym)) + case None => + if (sym == NoSymbol) + mirrorSelect("NoSymbol") + else if (sym == RootPackage) + mirrorSelect("definitions.RootPackage") + else if (sym == RootClass) + mirrorSelect("definitions.RootClass") + else if (sym == EmptyPackage) + mirrorSelect("definitions.EmptyPackage") + else if (sym.isModuleClass) + Select(reifySymRef(sym.sourceModule), "moduleClass") + else if (sym.isStatic && sym.isClass) + mirrorCall("staticClass", reify(sym.fullName)) + else if (sym.isStatic && sym.isModule) + mirrorCall("staticModule", reify(sym.fullName)) + else if (isLocatable(sym)) + if (sym.isTypeParameter) + mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos)) + else { + if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter) + val rowner = reify(sym.owner) + val rname = reify(sym.name.toString) + if (sym.isType) + mirrorCall("selectType", rowner, rname) + else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) { + val index = sym.owner.info.decl(sym.name).alternatives indexOf sym + assert(index >= 0, sym) + mirrorCall("selectOverloadedMethod", rowner, rname, reify(index)) + } else + mirrorCall("selectTerm", rowner, rname) + } + else { + if (sym.isTerm) { + if (reifyDebug) println("Free: " + sym) + val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false) + def markIfCaptured(arg: Ident): Tree = + if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg + mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym))) + } else { + if (reifyDebug) println("Late local: " + sym) + registerReifiableSymbol(sym) + reifySymRef(sym) + } + } + } + } + + /** + * reify the creation of a symbol + */ + private def reifySymbolDef(sym: Symbol): Tree = { + if (reifyDebug) println("reify sym def " + sym) + + ValDef(NoMods, localName(sym), TypeTree(), + Apply( + Select(reify(sym.owner), "newNestedSymbol"), + List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags))) + ) + ) + } + + /** + * Generate code to add type and annotation info to a reified symbol + */ + private def fillInSymbol(sym: Symbol): Tree = { + val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info))) + if (sym.annotations.isEmpty) rset + else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations))) + } + + /** Reify a scope */ + private def reifyScope(scope: Scope): Tree = { + scope foreach registerReifiableSymbol + mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*) + } + + /** Reify a list of symbols that need to be created */ + private def reifySymbols(syms: List[Symbol]): Tree = { + syms foreach registerReifiableSymbol + mkList(syms map reifySymRef) + } + + /** Reify a type that defines some symbols */ + private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree = + mirrorFactoryCall(value, reifySymbols(bound), reify(underlying)) + + /** Reify a type */ + private def reifyType(tpe0: Type): Tree = { + val tpe = tpe0.normalize + + if (tpe.isErroneous) + CannotReifyErroneousType(tpe) + if (definedInLiftedCode(tpe)) + CannotReifyTypeInvolvingBoundType(tpe) + + val tsym = tpe.typeSymbol + if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic) + Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor) + else tpe match { + case t @ NoType => + reifyMirrorObject(t) + case t @ NoPrefix => + reifyMirrorObject(t) + case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => + mirrorCall(nme.thisModuleType, reify(clazz.fullName)) + case t @ RefinedType(parents, decls) => + registerReifiableSymbol(tpe.typeSymbol) + mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) + case t @ ClassInfoType(parents, decls, clazz) => + registerReifiableSymbol(clazz) + mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) + case t @ ExistentialType(tparams, underlying) => + reifyTypeBinder(t, tparams, underlying) + case t @ PolyType(tparams, underlying) => + reifyTypeBinder(t, tparams, underlying) + case t @ MethodType(params, restpe) => + reifyTypeBinder(t, params, restpe) + case t @ AnnotatedType(anns, underlying, selfsym) => + val saved1 = reifySymbols + val saved2 = reifyTypes + + try { + // one more quirk of reifying annotations + // + // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs + // that's because a lot of logic expects post-typer trees to have non-null tpes + // + // Q: reified trees are pre-typer, so there's shouldn't be a problem. + // reflective typechecker will fill in missing symbols and types, right? + // A: actually, no. annotation ASTs live inside AnnotatedTypes, + // and insides of the types is the place where typechecker doesn't look. + reifySymbols = true + reifyTypes = true + if (reifyDebug) println("reify AnnotatedType: " + tpe) + reifyProductUnsafe(tpe) + } finally { + reifySymbols = saved1 + reifyTypes = saved2 + } + case _ => + reifyProductUnsafe(tpe) + } + } + + var reifySymbols = false + var reifyTypes = false + + /** Preprocess a tree before reification */ + private def trimTree(tree: Tree): Tree = { + def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = { + var stats1 = stats filterNot (stat => stat.isDef && { + if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat) + stat.symbol.isCaseAccessorMethod + }) + stats1 = stats1 filterNot (memberDef => memberDef.isDef && { + val isSynthetic = memberDef.symbol.isSynthetic + // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass) + // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes +// val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass + val isCaseMember = true + if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef) + isSynthetic && isCaseMember + }) + stats1 = stats1 map { + case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor => + if (reifyDebug) println("resetting visibility of case accessor field: " + valdef) + val Modifiers(flags, privateWithin, annotations) = mods + val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE + val mods1 = Modifiers(flags1, privateWithin, annotations) + ValDef(mods1, name, tpt, rhs).copyAttrs(valdef) + case stat => + stat + } + stats1 + } + + def trimSyntheticCaseClassCompanions(stats: List[Tree]) = + stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => { + val isSynthetic = moddef.symbol.isSynthetic + // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass) + // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes +// val isCaseCompanion = moddef.symbol.companionClass.isCaseClass + val isCaseCompanion = true + // @xeno.by: we also have to do this ugly hack for the very same reason described above + // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach + if (reifyDebug) println("boundSym: "+ moddef.symbol) + boundSyms += moddef.symbol + if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef) + isSynthetic && isCaseCompanion + })) + + tree match { + case tree if tree.isErroneous => + tree + case ta @ TypeApply(hk, ts) => + def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null + val discard = ts collect { case tt: TypeTree => tt } exists isErased + if (reifyDebug && discard) println("discarding TypeApply: " + tree) + if (discard) hk else ta + case classDef @ ClassDef(mods, name, params, impl) => + val Template(parents, self, body) = impl + val body1 = trimSyntheticCaseClassMembers(classDef, body) + var impl1 = Template(parents, self, body1).copyAttrs(impl) + ClassDef(mods, name, params, impl1).copyAttrs(classDef) + case moduledef @ ModuleDef(mods, name, impl) => + val Template(parents, self, body) = impl + val body1 = trimSyntheticCaseClassMembers(moduledef, body) + var impl1 = Template(parents, self, body1).copyAttrs(impl) + ModuleDef(mods, name, impl1).copyAttrs(moduledef) + case template @ Template(parents, self, body) => + val body1 = trimSyntheticCaseClassCompanions(body) + Template(parents, self, body1).copyAttrs(template) + case block @ Block(stats, expr) => + val stats1 = trimSyntheticCaseClassCompanions(stats) + Block(stats1, expr).copyAttrs(block) + case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy => + if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree) + val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name + ValDef(mods, name1, tpt, rhs).copyAttrs(valdef) + case unapply @ UnApply(fun, args) => + def extractExtractor(tree: Tree): Tree = { + val Apply(fun, args) = tree + args match { + case List(Ident(special)) if special == nme.SELECTOR_DUMMY => + val Select(extractor, flavor) = fun + assert(flavor == nme.unapply || flavor == nme.unapplySeq) + extractor + case _ => + extractExtractor(fun) + } + } + + if (reifyDebug) println("unapplying unapply: " + tree) + val fun1 = extractExtractor(fun) + Apply(fun1, args).copyAttrs(unapply) + case _ => + tree + } + } + + /** Reify a tree */ + private def reifyTree(tree0: Tree): Tree = { + val tree = trimTree(tree0) + + var rtree = tree match { + case tree if tree.isErroneous => + CannotReifyErroneousTree(tree) + case self.EmptyTree => + reifyMirrorObject(EmptyTree) + case self.emptyValDef => + mirrorSelect(nme.emptyValDef) + case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) => + reifyFree(tree) + case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) => + if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { + if (reifyDebug) println("captured variable: " + tree.symbol) + captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here. + mirrorCall("Select", reifyFree(tree), reifyName(nme.elem)) + } else reifyFree(tree) + case tt: TypeTree if (tt.tpe != null) => + reifyTypeTree(tt) + case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) => + CannotReifyClassOfBoundType(tree, tpe) + case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym => + CannotReifyClassOfBoundEnum(tree, constant.tpe) + case tree if tree.isDef => + if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe)))) + boundSyms += tree.symbol + + bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule") + bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass") + bindRelatedSymbol(tree.symbol.companionClass, "companionClass") + bindRelatedSymbol(tree.symbol.companionModule, "companionModule") + Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") } + def bindRelatedSymbol(related: Symbol, name: String): Unit = + if (related != null && related != NoSymbol) { + if (reifyDebug) println("boundSym (" + name + "): " + related) + boundSyms += related + } + + val prefix = tree.productPrefix + val elements = (tree.productIterator map { + // annotations exist in two flavors: + // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context) + // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context) + // + // here we process Modifiers that are involved in deftrees + // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'') + case Modifiers(flags, privateWithin, annotations) => + assert(annotations.isEmpty) // should've been eliminated by the typer + val postTyper = tree.symbol.annotations filter (_.original != EmptyTree) + if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations)) + val preTyper = postTyper map toPreTyperAnnotation + Modifiers(flags, privateWithin, preTyper) + case x => + x + }).toList + reifyProduct(prefix, elements) + case _ => + reifyProduct(tree) + } + + // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation + // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why. + if (reifySymbols && tree.hasSymbol) { + if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree)) + rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol))) + } + if (reifyTypes && tree.tpe != null) { + if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree)) + rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe))) + } + + rtree + } + + /** Reify pre-typer representation of a type. + * + * NB: This is the trickiest part of reification! + * + * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType''). + * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''), + * then we cannot reify it, or otherwise subsequent reflective compilation will fail. + * + * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation, + * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.: + * https://issues.scala-lang.org/browse/SI-5230 + * + * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible). + * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees. + * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler). + * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on. + * + * An important property of the original is that it isn't just a pre-typer tree. + * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List). + * This is very important, since subsequent reflective compilation won't have to resolve these symbols. + * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context, + * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled. + * + * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked. + * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees. + * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless. + * This is laboriously worked around in the code below. I hope this will be the only workaround in this department. + */ + private def reifyTypeTree(tt: TypeTree): Tree = { + if (definedInLiftedCode(tt.tpe)) { + if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe) + if (tt.original != null) { + val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten + val annmap = annotations map { ann => (ann.original, ann) } toMap + + // annotations exist in two flavors: + // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes + // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes + // + // here we process AnnotatedTypes, since only they can be involved in TypeTrees + // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'') + // + // the problem with annotations is that their originals don't preserve any symbols at all + // read the comment to this method to find out why it's bad + // that's why we transplant typechecked, i.e. symful, annotations onto original trees + class AnnotationFixup extends self.Transformer { + override def transform(tree: Tree) = tree match { + case Annotated(ann0, args) => + assert(annmap contains ann0) + val ann1 = annmap(ann0) + val ann = toPreTyperAnnotation(ann1) + Annotated(ann, transform(args)) + case _ => + tree + } + } + + if (reifyDebug) println("verdict: essential, reify as original") + val patchedOriginal = new AnnotationFixup().transform(tt.original) + reifyTree(patchedOriginal) + } else { + // type is deemed to be non-essential + // erase it and hope that subsequent reflective compilation will be able to recreate it again + if (reifyDebug) println("verdict: non-essential, discard") + mirrorCall("TypeTree") + } + } else { + var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe)) + // @xeno.by: temporarily disabling reification of originals + // subsequent reflective compilation will try to typecheck them + // and this means that the reifier has to do additional efforts to ensure that this will succeed + // additional efforts + no clear benefit = will be implemented later +// if (tt.original != null) { +// val setOriginal = Select(rtt, newTermName("setOriginal")) +// val reifiedOriginal = reify(tt.original) +// rtt = Apply(setOriginal, List(reifiedOriginal)) +// } + rtt + } + } + + /** Reify post-typer representation of an annotation */ + private def reifyAnnotation(ann: AnnotationInfo): Tree = + // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important + mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs)) + + /** Reify pre-typer representation of an annotation. + * The trick here is to retain the symbols that have been populated during typechecking of the annotation. + * If we do not do that, subsequent reflective compilation will fail. + */ + private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = { + if (definedInLiftedCode(ann.atp)) { + // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs + // + // keep in mind that we can't simply use ann.original, because its args are symless + // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation + // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original + // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification) + // + // also, if we pursue the route of reifying annotations defined in lifted code + // we should think about how to provide types for all nodes of the return value + // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes + // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking + // for now I just leave this as an implementation restriction + CannotReifyAnnotationInvolvingBoundType(ann) + } else { + val args = if (ann.assocs.isEmpty) { + ann.args + } else { + def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match { + case LiteralAnnotArg(const) => + Literal(const) + case ArrayAnnotArg(arr) => + Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation) + case NestedAnnotArg(ann) => + toPreTyperAnnotation(ann) + } + + ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) } + } + + New(TypeTree(ann.atp), List(args)) + } + } + + /** + * Reify a free reference. The result will be either a mirror reference + * to a global value, or else a mirror Literal. + */ + private def reifyFree(tree: Tree): Tree = tree match { + case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass => + val sym = tree.symbol + if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) + if (reifyDebug) println("Free: " + sym) + val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym)) + mirrorCall(nme.Ident, freeVar) + case This(_) => + if (reifyDebug) println("This for %s, reified as This".format(tree.symbol)) + mirrorCall(nme.This, reifySymRef(tree.symbol)) + case _ => + mirrorCall(nme.Ident, reifySymRef(tree.symbol)) + } + + // todo: consider whether we should also reify positions + private def reifyPosition(pos: Position): Tree = + reifyMirrorObject(NoPosition) + + // !!! we must eliminate these casts. + private def reifyProductUnsafe(x: Any): Tree = + if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product]) + else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass)) + private def reifyProduct(x: Product): Tree = + reifyProduct(x.productPrefix, x.productIterator.toList) + private def reifyProduct(prefix: String, elements: List[Any]): Tree = { + // @xeno.by: reflection would be more robust, but, hey, this is a hot path + if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*) + else mirrorCall(prefix, (elements map reify): _*) + } + + /** + * Reify a case object defined in Mirror + */ + private def reifyMirrorObject(name: String): Tree = mirrorSelect(name) + private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix) + + private def isReifiableConstant(value: Any) = value match { + case null => true // seems pretty reifable to me? + case _: String => true + case _ => isAnyVal(value) + } + + /** Reify an arbitary value */ + private def reify(value: Any): Tree = value match { + case tree: Tree => reifyTree(tree) + case sym: Symbol => reifySymRef(sym) + case tpe: Type => reifyType(tpe) + case xs: List[_] => reifyList(xs) + case xs: Array[_] => reifyArray(xs) + case scope: Scope => reifyScope(scope) + case x: Name => reifyName(x) + case x: Position => reifyPosition(x) + case x: Modifiers => reifyModifiers(x) + case x: AnnotationInfo => reifyAnnotation(x) + case _ => + if (isReifiableConstant(value)) Literal(Constant(value)) + else reifyProductUnsafe(value) + } + + /** + * An (unreified) path that refers to definition with given fully qualified name + * @param mkName Creator for last portion of name (either TermName or TypeName) + */ + private def path(fullname: String, mkName: String => Name): Tree = { + val parts = fullname split "\\." + val prefixParts = parts.init + val lastName = mkName(parts.last) + if (prefixParts.isEmpty) Ident(lastName) + else { + val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _)) + Select(prefixTree, lastName) + } + } + + /** An (unreified) path that refers to term definition with given fully qualified name */ + private def termPath(fullname: String): Tree = path(fullname, newTermName) + + /** An (unreified) path that refers to type definition with given fully qualified name */ + private def typePath(fullname: String): Tree = path(fullname, newTypeName) + + private def mirrorAlias = + ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage)) + + /** + * Generate code that generates a symbol table of all symbols registered in `reifiableSyms` + */ + private def reifySymbolTableSetup: List[Tree] = { + val symDefs, fillIns = new mutable.ArrayBuffer[Tree] + var i = 0 + while (i < reifiableSyms.length) { + // fillInSymbol might create new reifiableSyms, that's why this is done iteratively + symDefs += reifySymbolDef(reifiableSyms(i)) + fillIns += fillInSymbol(reifiableSyms(i)) + i += 1 + } + + symDefs.toList ++ fillIns.toList + } + } // end of Reifier + + object Reifier { + def CannotReifyPreTyperTree(tree: Tree) = { + val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier" + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyErroneousTree(tree: Tree) = { + val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier" + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyErroneousType(tpe: Type) = { + val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier" + throw new ReifierError(NoPosition, msg) + } + + def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = { + val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe) + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = { + val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe) + throw new ReifierError(tree.pos, msg) + } + + def CannotReifyTypeInvolvingBoundType(tpe: Type) = { + val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe) + throw new ReifierError(NoPosition, msg) + } + + def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = { + val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann) + throw new ReifierError(ann.original.pos, msg) + } + } // end of Reifier + + // begin reify + import Reifier._ + if (tree.tpe != null) { + val saved = printTypings + try { + val reifyDebug = settings.Yreifydebug.value + val debugTrace = util.trace when reifyDebug + debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + debugTrace("transformed = ") { + val reifier = new Reifier() + val untyped = reifier.reifyTopLevel(tree) + + val reifyCopypaste = settings.Yreifycopypaste.value + if (reifyCopypaste) { + if (reifyDebug) println("=======================") + println(reifiedNodeToString(untyped)) + if (reifyDebug) println("=======================") + } + + untyped + } + } finally { + printTypings = saved + } + } else { + CannotReifyPreTyperTree(tree) + } + } + + /** A throwable signalling a reification error */ + class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) { + def this(msg: String) = this(NoPosition, msg) + } +} diff --git a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala new file mode 100644 index 0000000000..fce59bb099 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala @@ -0,0 +1,75 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package ast + +import compat.Platform.EOL +import symtab._ +import Flags._ + +trait ReifyPrinters { self: NodePrinters => + + val global: Global + import global._ + + object reifiedNodeToString extends Function1[Tree, String] { + def apply(tree: Tree): String = { + import scala.reflect.api.Modifier + + // @PP: I fervently hope this is a test case or something, not anything being + // depended upon. Of more fragile code I cannot conceive. + // @eb: This stuff is only needed to debug-print out reifications in human-readable format + // Rolling a full-fledged, robust TreePrinter would be several times more code. + (for (line <- (tree.toString.split(EOL) drop 2 dropRight 1)) yield { + var s = line.trim + s = s.replace("$mr.", "") + s = s.replace(".apply", "") + s = s.replace("scala.collection.immutable.", "") + s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") + s = "List\\[.*?\\]".r.replaceAllIn(s, "List") + s = s.replace("immutable.this.Nil", "List()") + s = s.replace("modifiersFromInternalFlags", "Modifiers") + s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") + s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { + val buf = new collection.mutable.ListBuffer[String] + + val annotations = m.group(3) + if (buf.nonEmpty || annotations.nonEmpty) + buf.append("List(" + annotations + ")") + + val privateWithin = "" + m.group(2) + if (buf.nonEmpty || privateWithin != "") + buf.append("newTypeName(\"" + privateWithin + "\")") + + val flags = m.group(1).toLong + val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", " + if (buf.nonEmpty || s_flags != "") + buf.append("Set(" + s_flags + ")") + + "Modifiers(" + buf.reverse.mkString(", ") + ")" + }) + s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => { + val flags = m.group(1).toLong + val mods = Flags.modifiersOfFlags(flags) map (_.sourceString) + "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))" + }) + + s + }) mkString EOL + } + } + + + def printReifyCopypaste(tree: Tree) { + val reifyDebug = settings.Yreifydebug.value + if (reifyDebug) println("=======================") + printReifyCopypaste1(tree) + if (reifyDebug) println("=======================") + } + + def printReifyCopypaste1(tree: Tree) { + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index 7b5de1f3dd..c1d6c1a4d4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -33,17 +33,16 @@ abstract class TreeBrowsers { val borderSize = 10 - def create(): SwingBrowser = new SwingBrowser(); /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class ProgramTree(units: List[UnitTree]) extends Tree { - override def toString(): String = "Program" + override def toString: String = "Program" } /** Pseudo tree class, so that all JTree nodes are treated uniformly */ case class UnitTree(unit: CompilationUnit) extends Tree { - override def toString(): String = unit.toString() + override def toString: String = unit.toString } /** diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala index 5c3071739c..3371353f25 100644 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -51,14 +51,11 @@ trait TreePrinters extends reflect.internal.TreePrinters { this: Global => treePrinter.println() treePrinter.print(definition) - case AssignOrNamedArg(lhs, rhs) => - treePrinter.print(lhs, " = ", rhs) - case TypeTreeWithDeferredRefCheck() => treePrinter.print("<tree with deferred refcheck>") case SelectFromArray(qualifier, name, _) => - treePrinter.print(qualifier, ".<arr>", treePrinter.symName(tree, name)) + treePrinter.print(qualifier, ".<arr>", symName(tree, name)) case _ => super.xprintTree(treePrinter, tree) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 88a9b5e18b..855b55bb5e 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -17,7 +17,6 @@ import scala.reflect.internal.Flags.TRAIT trait Trees extends reflect.internal.Trees { self: Global => // --- additional cases -------------------------------------------------------- - /** Only used during parsing */ case class Parens(args: List[Tree]) extends Tree @@ -31,21 +30,14 @@ trait Trees extends reflect.internal.Trees { self: Global => override def isType = definition.isType } - - /** Either an assignment or a named argument. Only appears in argument lists, - * eliminated by typecheck (doTypedApply) - */ - case class AssignOrNamedArg(lhs: Tree, rhs: Tree) - extends TermTree - /** Array selection <qualifier> . <name> only used during erasure */ case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) - extends TermTree with RefTree { } + extends TermTree with RefTree /** emitted by typer, eliminated by refchecks */ case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree - - /** Marks underlying reference to id as boxed. + + /** Marks underlying reference to id as boxed. * @pre: id must refer to a captured variable * A reference such marked will refer to the boxed entity, no dereferencing * with `.elem` is done on it. @@ -157,20 +149,17 @@ trait Trees extends reflect.internal.Trees { self: Global => traverser.traverseTrees(ts) case DocDef(comment, definition) => traverser.traverse(definition) - case AssignOrNamedArg(lhs, rhs) => - traverser.traverse(lhs); traverser.traverse(rhs) case SelectFromArray(qualifier, selector, erasure) => traverser.traverse(qualifier) case ReferenceToBoxed(idt) => traverser.traverse(idt) - case TypeTreeWithDeferredRefCheck() => // TODO: should we traverse the wrapped tree? + case TypeTreeWithDeferredRefCheck() => // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check) case _ => super.xtraverse(traverser, tree) } trait TreeCopier extends super.TreeCopierOps { def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck @@ -182,8 +171,6 @@ trait Trees extends reflect.internal.Trees { self: Global => class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier { def DocDef(tree: Tree, comment: DocComment, definition: Tree) = new DocDef(comment, definition).copyAttrs(tree) - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = - new AssignOrNamedArg(lhs, rhs).copyAttrs(tree) def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree) def ReferenceToBoxed(tree: Tree, idt: Ident) = @@ -199,18 +186,13 @@ trait Trees extends reflect.internal.Trees { self: Global => if (comment0 == comment) && (definition0 == definition) => t case _ => this.treeCopy.DocDef(tree, comment, definition) } - def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match { - case t @ AssignOrNamedArg(lhs0, rhs0) - if (lhs0 == lhs) && (rhs0 == rhs) => t - case _ => this.treeCopy.AssignOrNamedArg(tree, lhs, rhs) - } def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match { case t @ SelectFromArray(qualifier0, selector0, _) if (qualifier0 == qualifier) && (selector0 == selector) => t case _ => this.treeCopy.SelectFromArray(tree, qualifier, selector, erasure) } def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match { - case t @ ReferenceToBoxed(idt0) + case t @ ReferenceToBoxed(idt0) if (idt0 == idt) => t case _ => this.treeCopy.ReferenceToBoxed(tree, idt) } @@ -225,7 +207,7 @@ trait Trees extends reflect.internal.Trees { self: Global => try unit.body = transform(unit.body) catch { case ex: Exception => - println("unhandled exception while transforming "+unit) + println(supplementErrorMessage("unhandled exception while transforming "+unit)) throw ex } } @@ -234,8 +216,6 @@ trait Trees extends reflect.internal.Trees { self: Global => override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match { case DocDef(comment, definition) => transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition)) - case AssignOrNamedArg(lhs, rhs) => - transformer.treeCopy.AssignOrNamedArg(tree, transformer.transform(lhs), transformer.transform(rhs)) case SelectFromArray(qualifier, selector, erasure) => transformer.treeCopy.SelectFromArray( tree, transformer.transform(qualifier), selector, erasure) @@ -253,62 +233,97 @@ trait Trees extends reflect.internal.Trees { self: Global => } } - /** resets symbol and tpe fields in a tree, @see ResetAttrsTraverse + /** resets symbol and tpe fields in a tree, @see ResetAttrs */ // def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x } // def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x } - - def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrsTransformer(false).transformPoly(x) - def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrsTransformer(true).transformPoly(x) + + def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrs(false).transform(x) + def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrs(true).transform(x) /** A transformer which resets symbol and tpe fields of all nodes in a given tree, * with special treatment of: * TypeTree nodes: are replaced by their original if it exists, otherwise tpe field is reset * to empty if it started out empty or refers to local symbols (which are erased). * TypeApply nodes: are deleted if type arguments end up reverted to empty - * This(pkg) notes where pkg is a pckage: these are kept. + * This(pkg) nodes where pkg is a package: these are kept. * - * (bq:) This traverser has mutable state and should be discarded after use + * (bq:) This transformer has mutable state and should be discarded after use */ - private class ResetAttrsTransformer(localOnly: Boolean) extends Transformer { - private val erasedSyms = util.HashSet[Symbol](8) - private def resetDef(tree: Tree) { - if (tree.symbol != null && tree.symbol != NoSymbol) - erasedSyms addEntry tree.symbol - tree.symbol = NoSymbol + private class ResetAttrs(localOnly: Boolean) { + val debug = settings.debug.value + val trace = scala.tools.nsc.util.trace when debug + + val locals = util.HashSet[Symbol](8) + val orderedLocals = collection.mutable.ListBuffer[Symbol]() + def registerLocal(sym: Symbol) { + if (sym != null && sym != NoSymbol) { + if (debug && !(locals contains sym)) orderedLocals append sym + locals addEntry sym + } } - override def transform(tree: Tree): Tree = super.transform { - tree match { - case Template(_, _, body) => - body foreach resetDef - resetDef(tree) - tree.tpe = null - tree - case _: DefTree | Function(_, _) | Template(_, _, _) => - resetDef(tree) - tree.tpe = null - tree - case tpt: TypeTree => - if (tpt.original != null) - tpt.original - else if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => erasedSyms contains tp.typeSymbol)))) - tpt.tpe = null - tree - case TypeApply(fn, args) if args map transform exists (_.isEmpty) => - fn - case This(_) if tree.symbol != null && tree.symbol.isPackageClass => - tree - case EmptyTree => - tree - case _ => - if (tree.hasSymbol && (!localOnly || (erasedSyms contains tree.symbol))) - tree.symbol = NoSymbol - tree.tpe = null - tree + + class MarkLocals extends self.Traverser { + def markLocal(tree: Tree) { + if (tree.symbol != null && tree.symbol != NoSymbol) { + val sym = tree.symbol + registerLocal(sym) + registerLocal(sym.sourceModule) + registerLocal(sym.moduleClass) + } + } + + override def traverse(tree: Tree) = { + tree match { + case _: DefTree | Function(_, _) | Template(_, _, _) => + markLocal(tree) + case _ if tree.symbol.isInstanceOf[FreeVar] => + markLocal(tree) + case _ => + ; + } + + super.traverse(tree) } } - def transformPoly[T <: Tree](x: T): T = { - val x1 = transform(x) + + class Transformer extends self.Transformer { + override def transform(tree: Tree): Tree = super.transform { + tree match { + case tpt: TypeTree => + if (tpt.original != null) { + transform(tpt.original) + } else { + if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol)))) + tpt.tpe = null + tree + } + case TypeApply(fn, args) if args map transform exists (_.isEmpty) => + transform(fn) + case This(_) if tree.symbol != null && tree.symbol.isPackageClass => + tree + case EmptyTree => + tree + case _ => + if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol))) + tree.symbol = NoSymbol + tree.tpe = null + tree + } + } + } + + def transform[T <: Tree](x: T): T = { + new MarkLocals().traverse(x) + + if (debug) { + assert(locals.size == orderedLocals.size) + val eoln = System.getProperty("line.separator") + val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString eoln + trace("locals (%d total): %n".format(orderedLocals.size))(msg) + } + + val x1 = new Transformer().transform(x) assert(x.getClass isInstance x1) x1.asInstanceOf[T] } @@ -318,7 +333,6 @@ trait Trees extends reflect.internal.Trees { self: Global => case Parens(expr) (only used during parsing) case DocDef(comment, defn) => (eliminated by typer) - case AssignOrNamedArg(lhs, rhs) => (eliminated by typer) case TypeTreeWithDeferredRefCheck() => (created and eliminated by typer) case SelectFromArray(_, _, _) => (created and eliminated by erasure) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index a2a577a7ab..f712c7411f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -84,6 +84,8 @@ trait Scanners extends ScannersCommon { abstract class Scanner extends CharArrayReader with TokenData with ScannerCommon { private def isDigit(c: Char) = java.lang.Character isDigit c + + def isAtEnd = charOffset >= buf.length def flush = { charOffset = offset; nextChar(); this } @@ -178,7 +180,7 @@ trait Scanners extends ScannersCommon { * @pre: inStringInterpolation */ @inline private def inMultiLineInterpolation = - sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART + inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART /** read next token and return last offset */ @@ -215,7 +217,9 @@ trait Scanners extends ScannersCommon { if (!sepRegions.isEmpty && sepRegions.head == lastToken) sepRegions = sepRegions.tail case STRINGLIT => - if (inStringInterpolation) + if (inMultiLineInterpolation) + sepRegions = sepRegions.tail.tail + else if (inStringInterpolation) sepRegions = sepRegions.tail case _ => } @@ -384,7 +388,7 @@ trait Scanners extends ScannersCommon { if (ch == '\"') { nextRawChar() getStringPart(multiLine = true) - sepRegions = STRINGLIT :: sepRegions // indicate string part + sepRegions = STRINGPART :: sepRegions // indicate string part sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part } else { token = STRINGLIT @@ -449,7 +453,7 @@ trait Scanners extends ScannersCommon { case ']' => nextChar(); token = RBRACKET case SU => - if (charOffset >= buf.length) token = EOF + if (isAtEnd) token = EOF else { syntaxError("illegal character") nextChar() @@ -468,7 +472,7 @@ trait Scanners extends ScannersCommon { nextChar() getOperatorRest() } else { - syntaxError("illegal character") + syntaxError("illegal character '" + ("" + '\\' + 'u' + "%04x".format(ch: Int)) + "'") nextChar() } } @@ -771,10 +775,10 @@ trait Scanners extends ScannersCommon { putChar(ch) } - private def getLitChars(delimiter: Char) = - while (ch != delimiter && (ch != CR && ch != LF && ch != SU || isUnicodeEscape)) { + private def getLitChars(delimiter: Char) = { + while (ch != delimiter && !isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) getLitChar() - } + } /** read fractional part and exponent of floating point number * if one is present. diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index b5ec0ceffb..e310611e68 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -24,7 +24,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse import global._ informProgress("parsing " + unit) unit.body = - if (unit.source.file.name.endsWith(".java")) new JavaUnitParser(unit).parse() + if (unit.isJava) new JavaUnitParser(unit).parse() else if (reporter.incompleteHandled) new UnitParser(unit).parse() else new UnitParser(unit).smartParse() diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index badf5d70d1..6aee52a354 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -133,7 +133,7 @@ abstract class GenICode extends SubComponent { if (!ctx1.bb.closed) ctx1.bb.close prune(ctx1.method) } else - ctx1.method.setCode(null) + ctx1.method.setCode(NoCode) ctx1 case Template(_, _, body) => @@ -179,7 +179,7 @@ abstract class GenICode extends SubComponent { } private def genThrow(expr: Tree, ctx: Context): (Context, TypeKind) = { - require(expr.tpe <:< ThrowableClass.tpe) + require(expr.tpe <:< ThrowableClass.tpe, expr.tpe) val thrownKind = toTypeKind(expr.tpe) val ctx1 = genLoad(expr, ctx, thrownKind) @@ -480,7 +480,7 @@ abstract class GenICode extends SubComponent { */ private def msil_genLoadZeroOfNonEnumValuetype(ctx: Context, kind: TypeKind, pos: Position, leaveAddressOnStackInstead: Boolean) { val REFERENCE(clssym) = kind - assert(loaders.clrTypes.isNonEnumValuetype(clssym)) + assert(loaders.clrTypes.isNonEnumValuetype(clssym), clssym) val local = ctx.makeLocal(pos, clssym.tpe, "tmp") ctx.method.addLocal(local) ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(local), pos) @@ -1064,7 +1064,7 @@ abstract class GenICode extends SubComponent { var default: BasicBlock = afterCtx.bb for (caze @ CaseDef(pat, guard, body) <- cases) { - assert(guard == EmptyTree) + assert(guard == EmptyTree, guard) val tmpCtx = ctx1.newBlock pat match { case Literal(value) => @@ -1072,6 +1072,15 @@ abstract class GenICode extends SubComponent { targets = tmpCtx.bb :: targets case Ident(nme.WILDCARD) => default = tmpCtx.bb + case Alternative(alts) => + alts foreach { + case Literal(value) => + tags = value.intValue :: tags + targets = tmpCtx.bb :: targets + case _ => + abort("Invalid case in alternative in switch-like pattern match: " + + tree + " at: " + tree.pos) + } case _ => abort("Invalid case statement in switch-like pattern match: " + tree + " at: " + (tree.pos)) diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 2668e7f29f..298c9171a1 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -177,8 +177,7 @@ trait Members { /** method parameters */ var params: List[Local] = Nil - // TODO - see how null is stil arriving here - def hasCode = (code ne NoCode) && (code ne null) + def hasCode = code ne NoCode def setCode(code: Code): IMethod = { this.code = code; this diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala index 229bbceb36..f5be82a776 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -199,7 +199,7 @@ abstract class CopyPropagation { m foreachBlock { b => in(b) = lattice.bottom out(b) = lattice.bottom - assert(out.contains(b)) + assert(out.contains(b), out) log("Added point: " + b) } m.exh foreach { e => diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index c21507ef45..4f05678d85 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -80,9 +80,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "selected.png", "selected2-right.png", "selected2.png", - "unselected.png", - - "rootdoc.txt" + "unselected.png" ) /** Generates the Scaladoc site for a model into the site root. diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt b/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt deleted file mode 100644 index 6145429f1e..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt +++ /dev/null @@ -1,27 +0,0 @@ -This is the documentation for the Scala standard library. - -== Package structure == - -The [[scala]] package contains core types. - -scala.[[scala.collection]] and its subpackages contain a collections framework with higher-order functions for manipulation. Both [[scala.collection.immutable]] and [[scala.collection.mutable]] data structures are available, with immutable as the default. The [[scala.collection.parallel]] collections provide automatic parallel operation. - -Other important packages include: - - - scala.[[scala.actors]] - Concurrency framework inspired by Erlang. - - scala.[[scala.io]] - Input and output. - - scala.[[scala.math]] - Basic math functions and additional numeric types. - - scala.[[scala.sys]] - Interaction with other processes and the operating system. - - scala.util.[[scala.util.matching]] - Pattern matching in text using regular expressions. - - scala.util.parsing.[[scala.util.parsing.combinator]] - Composable combinators for parsing. - - scala.[[scala.xml]] - XML parsing, manipulation, and serialization. - -Many other packages exist. See the complete list on the left. - -== Automatic imports == - -Identifiers in the scala package and the [[scala.Predef]] object are always in scope by default. - -Some of these identifiers are type aliases provided as shortcuts to commonly used classes. For example, List is an alias for scala.collection.immutable.[[scala.collection.immutable.List]]. - -Other aliases refer to classes providing by the underlying platform. For example, on the JVM, String is an alias for java.lang.String. diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index ea36eb03c7..efa524503c 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -196,11 +196,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /** The start of a scaladoc code block */ protected val CodeBlockStart = - new Regex("""(.*)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""") + new Regex("""(.*?)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""") /** The end of a scaladoc code block */ protected val CodeBlockEnd = - new Regex("""(.*)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""") + new Regex("""(.*?)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""") /** A key used for a tag map. The key is built from the name of the tag and * from the linked symbol if the tag has one. @@ -250,7 +250,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @param remaining The lines that must still recursively be parsed. * @param inCodeBlock Whether the next line is part of a code block (in which no tags must be read). */ def parse0 ( - docBody: String, + docBody: StringBuilder, tags: Map[TagKey, List[String]], lastTagKey: Option[TagKey], remaining: List[String], @@ -258,9 +258,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => ): Comment = remaining match { case CodeBlockStart(before, marker, after) :: ls if (!inCodeBlock) => - if (before.trim != "") - parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, false) - else if (after.trim != "") + if (!before.trim.isEmpty && !after.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, false) + else if (!before.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: ls, false) + else if (!after.trim.isEmpty) parse0(docBody, tags, lastTagKey, marker :: after :: ls, true) else lastTagKey match { case Some(key) => @@ -271,24 +273,26 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } parse0(docBody, tags + (key -> value), lastTagKey, ls, true) case None => - parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, true) + parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, true) } case CodeBlockEnd(before, marker, after) :: ls => - if (before.trim != "") - parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, true) - else if (after.trim != "") + if (!before.trim.isEmpty && !after.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, true) + if (!before.trim.isEmpty) + parse0(docBody, tags, lastTagKey, before :: marker :: ls, true) + else if (!after.trim.isEmpty) parse0(docBody, tags, lastTagKey, marker :: after :: ls, false) else lastTagKey match { case Some(key) => val value = ((tags get key): @unchecked) match { - case Some(b :: bs) => (b + endOfLine + "}}}") :: bs + case Some(b :: bs) => (b + endOfLine + marker) :: bs case None => oops("lastTagKey set when no tag exists for key") } parse0(docBody, tags + (key -> value), lastTagKey, ls, false) case None => - parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, false) + parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, false) } case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) => @@ -311,8 +315,9 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock) case line :: ls => - val newBody = if (docBody == "") line else docBody + endOfLine + line - parse0(newBody, tags, lastTagKey, ls, inCodeBlock) + if (docBody.length > 0) docBody append endOfLine + docBody append line + parse0(docBody, tags, lastTagKey, ls, inCodeBlock) case Nil => @@ -350,7 +355,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } val com = createComment ( - body0 = Some(parseWiki(docBody, pos)), + body0 = Some(parseWiki(docBody.toString, pos)), authors0 = allTags(SimpleTagKey("author")), see0 = allTags(SimpleTagKey("see")), result0 = oneTag(SimpleTagKey("return")), @@ -374,7 +379,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } - parse0("", Map.empty, None, clean(comment), false) + parse0(new StringBuilder(comment.size), Map.empty, None, clean(comment), false) } @@ -385,7 +390,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * - Removed all end-of-line whitespace. * - Only `endOfLine` is used to mark line endings. */ def parseWiki(string: String, pos: Position): Body = { - new WikiParser(string.toArray, pos).document() + new WikiParser(string, pos).document() } /** TODO @@ -393,7 +398,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @author Ingo Maier * @author Manohar Jonnalagedda * @author Gilles Dubochet */ - protected final class WikiParser(val buffer: Array[Char], pos: Position) extends CharReader(buffer) { wiki => + protected final class WikiParser(val buffer: String, pos: Position) extends CharReader(buffer) { wiki => var summaryParsed = false @@ -411,7 +416,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => def block(): Block = { if (checkSkipInitWhitespace("{{{")) code() - else if (checkSkipInitWhitespace("=")) + else if (checkSkipInitWhitespace('=')) title() else if (checkSkipInitWhitespace("----")) hrule() @@ -493,7 +498,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => def title(): Block = { jumpWhitespace() val inLevel = repeatJump("=") - val text = inline(check(Array.fill(inLevel)('='))) + val text = inline(check("=" * inLevel)) val outLevel = repeatJump("=", inLevel) if (inLevel != outLevel) reportError(pos, "unbalanced or unclosed heading") @@ -734,11 +739,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => val pc = char nextChar() // read EOL val ok = { - checkSkipInitWhitespace(Array(endOfLine)) || - checkSkipInitWhitespace(Array('=')) || - checkSkipInitWhitespace(Array('{', '{', '{')) || + checkSkipInitWhitespace(endOfLine) || + checkSkipInitWhitespace('=') || + checkSkipInitWhitespace("{{{") || checkList || - checkSkipInitWhitespace(Array('\u003D')) + checkSkipInitWhitespace('\u003D') } offset = poff char = pc @@ -751,7 +756,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } } - protected sealed class CharReader(buffer: Array[Char]) { reader => + protected sealed class CharReader(buffer: String) { reader => var char: Char = _ var offset: Int = 0 @@ -760,36 +765,37 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => if (offset >= buffer.length) char = endOfText else { - char = buffer(offset) + char = buffer charAt offset offset += 1 } } - implicit def strintToChars(s: String): Array[Char] = s.toArray - - def store(body: => Unit): String = { - val pre = offset - body - val post = offset - buffer.toArray.slice(pre, post).toString + final def check(chars: String): Boolean = { + val poff = offset + val pc = char + val ok = jump(chars) + offset = poff + char = pc + ok } - final def check(chars: Array[Char]): Boolean = { + def checkSkipInitWhitespace(c: Char): Boolean = { val poff = offset val pc = char - val ok = jump(chars) + jumpWhitespace() + val ok = jump(c) offset = poff char = pc ok } - def checkSkipInitWhitespace(chars: Array[Char]): Boolean = { + def checkSkipInitWhitespace(chars: String): Boolean = { val poff = offset val pc = char jumpWhitespace() val (ok0, chars0) = - if (chars.head == ' ') - (offset > poff, chars.tail) + if (chars.charAt(0) == ' ') + (offset > poff, chars substring 1) else (true, chars) val ok = ok0 && jump(chars0) @@ -825,16 +831,16 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /** jumps all the characters in chars, consuming them in the process. * @return true only if the correct characters have been jumped */ - final def jump(chars: Array[Char]): Boolean = { + final def jump(chars: String): Boolean = { var index = 0 - while (index < chars.length && char == chars(index) && char != endOfText) { + while (index < chars.length && char == chars.charAt(index) && char != endOfText) { nextChar() index += 1 } index == chars.length } - final def checkedJump(chars: Array[Char]): Boolean = { + final def checkedJump(chars: String): Boolean = { val poff = offset val pc = char val ok = jump(chars) @@ -845,7 +851,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => ok } - final def repeatJump(chars: Array[Char], max: Int): Int = { + final def repeatJump(chars: String, max: Int): Int = { var count = 0 var more = true while (more && count < max) { @@ -857,7 +863,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def repeatJump(chars: Array[Char]): Int = { + final def repeatJump(chars: String): Int = { var count = 0 var more = true while (more) { @@ -878,10 +884,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def jumpUntil(chars: Array[Char]): Int = { + final def jumpUntil(chars: String): Int = { assert(chars.length > 0) var count = 0 - val c = chars(0) + val c = chars.charAt(0) while (!check(chars) && char != endOfText) { nextChar() while (char != c && char != endOfText) { @@ -922,10 +928,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => count } - final def readUntil(chars: Array[Char]): Int = { + final def readUntil(chars: String): Int = { assert(chars.length > 0) var count = 0 - val c = chars(0) + val c = chars.charAt(0) while (!check(chars) && char != endOfText) { readBuilder += char nextChar() diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 0fea0a2d92..477cec8c8e 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -1060,6 +1060,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") implicit def addOnTypeError[T](x: => T): OnTypeError[T] = new OnTypeError(x) + // OnTypeError should still catch TypeError because of cyclic references, + // but DivergentImplicit shouldn't leak anymore here class OnTypeError[T](op: => T) { def onTypeError(alt: => T) = try { op diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 3bc4e1cbe1..3a605975f4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -20,6 +20,7 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) with ScalaClassLoader { // private val defined = mutable.Map[String, Class[_]]() + override protected def trace = sys.props contains "scala.debug.classloader" @@ -43,6 +44,22 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) } } + 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, true) + if (file == null) + return null + } + + return file + } + override def getResourceAsStream(name: String) = findAbstractFile(name) match { case null => super.getResourceAsStream(name) case file => file.input @@ -74,4 +91,24 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) // case null => super.getResource(name) // case file => new URL(...) // } + + 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/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index 9f5fde70d8..39a1a406ba 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -62,7 +62,7 @@ trait ExprTyper { else Some(trees) } } - def tokens(line: String) = beSilentDuring(codeParser.tokens(line)) + def tokens(line: String) = beQuietDuring(codeParser.tokens(line)) // TODO: integrate these into a CodeHandler[Type]. diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 8cdd2334ab..de408f083f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -196,7 +196,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def foreach[U](f: Tree => U): Unit = t foreach { x => f(x) ; () } }).toList } - + implicit def installReplTypeOps(tp: Type): ReplTypeOps = new ReplTypeOps(tp) class ReplTypeOps(tp: Type) { def orElse(other: => Type): Type = if (tp ne NoType) tp else other @@ -831,7 +831,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", "")) } private def compileAndSaveRun(label: String, code: String) = { - showCodeIfDebugging(code) + showCodeIfDebugging(packaged(code)) val (success, run) = compileSourcesKeepingRun(new BatchSourceFile(label, packaged(code))) lastRun = run success @@ -906,11 +906,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends if (!handlers.last.definesValue) "" else handlers.last.definesTerm match { case Some(vname) if typeOf contains vname => - """ - |lazy val %s = { - | %s - | %s - |}""".stripMargin.format(lineRep.resultName, lineRep.printName, fullPath(vname)) + "lazy val %s = %s".format(lineRep.resultName, fullPath(vname)) case _ => "" } // first line evaluates object to make sure constructor is run @@ -956,7 +952,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends typesOfDefinedTerms // compile the result-extraction object - beSilentDuring { + beQuietDuring { savingSettings(_.nowarn.value = true) { lineRep compile ResultObjectSourceCode(handlers) } @@ -1078,7 +1074,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val clazz = classOfTerm(id) getOrElse { return NoType } val staticSym = tpe.typeSymbol val runtimeSym = getClassIfDefined(clazz.getName) - + if ((runtimeSym != NoSymbol) && (runtimeSym != staticSym) && (runtimeSym isSubClass staticSym)) runtimeSym.info else NoType @@ -1125,6 +1121,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends val termname = newTypeName(name) findName(termname) getOrElse getModuleIfDefined(termname) } + def types[T: ClassManifest] : Symbol = types(classManifest[T].erasure.getName) + def terms[T: ClassManifest] : Symbol = terms(classManifest[T].erasure.getName) + def apply[T: ClassManifest] : Symbol = apply(classManifest[T].erasure.getName) /** the previous requests this interpreter has processed */ private lazy val prevRequests = mutable.ListBuffer[Request]() diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index b4a9b9b0e3..835fbb5638 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -20,7 +20,7 @@ import io.{ Path } class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: ReplValsImpl) { import intp.{ beQuietDuring, typeOfExpression, interpret, parse } import intp.global._ - import definitions.{ manifestToType, getClassIfDefined, getModuleIfDefined } + import definitions.{ manifestToType, manifestToSymbol, getClassIfDefined, getModuleIfDefined } abstract class SymSlurper { def isKeep(sym: Symbol): Boolean @@ -65,10 +65,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl } } - class PackageSlurper(pkgName: String) extends SymSlurper { - val pkgSymbol = getModuleIfDefined(pkgName) - val modClass = pkgSymbol.moduleClass - + class PackageSlurper(packageClass: Symbol) extends SymSlurper { /** Looking for dwindling returns */ def droppedEnough() = unseenHistory.size >= 4 && { unseenHistory takeRight 4 sliding 2 forall { it => @@ -79,9 +76,16 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl def isRecur(sym: Symbol) = true def isIgnore(sym: Symbol) = sym.isAnonOrRefinementClass || (sym.name.toString contains "$mc") - def isKeep(sym: Symbol) = sym.hasTransOwner(modClass) + def isKeep(sym: Symbol) = sym.hasTransOwner(packageClass) def isFinished() = droppedEnough() - def slurp() = apply(modClass) + def slurp() = { + if (packageClass.isPackageClass) + apply(packageClass) + else { + repldbg("Not a package class! " + packageClass) + Set() + } + } } private def customBanner = replProps.powerBanner.option flatMap (f => io.File(f).safeSlurp()) @@ -124,7 +128,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl def to_str(m: Symbol) = "%12s %s".format( m.decodedName, "" + elimRefinement(m.accessedOrSelf.tpe) stripPrefix "scala.tools.nsc.") - ( rutil.info[ReplValsImpl].declares + ( rutil.info[ReplValsImpl].membersDeclared filter (m => m.isPublic && !m.hasModuleFlag && !m.isConstructor) sortBy (_.decodedName) map to_str @@ -136,59 +140,81 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit def apply[T: Manifest] : InternalInfo[T] = new InternalInfo[T](None) } object InternalInfo extends LowPriorityInternalInfo { } + + /** Now dealing with the problem of acidentally calling a method on Type + * when you're holding a Symbol and seeing the Symbol converted to the + * type of Symbol rather than the type of the thing represented by the + * symbol, by only implicitly installing one method, "?", and the rest + * of the conveniences exist on that wrapper. + */ + trait LowPriorityInternalInfoWrapper { + implicit def apply[T: Manifest] : InternalInfoWrapper[T] = new InternalInfoWrapper[T](None) + } + object InternalInfoWrapper extends LowPriorityInternalInfoWrapper { + + } + class InternalInfoWrapper[T: Manifest](value: Option[T] = None) { + def ? : InternalInfo[T] = new InternalInfo[T](value) + } /** Todos... * translate manifest type arguments into applied types * customizable symbol filter (had to hardcode no-spec to reduce noise) */ class InternalInfo[T: Manifest](value: Option[T] = None) { - // Decided it was unwise to have implicit conversions via commonly - // used type/symbol methods, because it's too easy to e.g. call - // "x.tpe" where x is a Type, and rather than failing you get the - // Type representing Types#Type (or Manifest, or whatever.) - private def tpe = tpe_ - private def symbol = symbol_ - private def name = name_ - - def symbol_ : Symbol = getClassIfDefined(erasure.getName) - def tpe_ : Type = manifestToType(man) - def name_ : Name = symbol.name - def companion = symbol.companionSymbol - def info = symbol.info - def module = symbol.moduleClass - def owner = symbol.owner - def owners = symbol.ownerChain drop 1 - def defn = symbol.defString - def decls = symbol.info.decls - - def declares = decls.toList - def inherits = members filterNot (declares contains _) - def types = members filter (_.name.isTypeName) - def methods = members filter (_.isMethod) - def overrides = declares filter (_.isOverride) - def inPackage = owners find (x => x.isPackageClass || x.isPackage) getOrElse definitions.RootPackage - - def man = manifest[T] - def erasure = man.erasure - def members = tpe.members filterNot (_.name.toString contains "$mc") - def allMembers = tpe.members - def bts = info.baseTypeSeq.toList - def btsmap = bts map (x => (x, x.decls.toList)) toMap - def pkgName = Option(erasure.getPackage) map (_.getName) - def pkg = pkgName map getModuleIfDefined getOrElse NoSymbol - def pkgmates = pkg.tpe.members - def pkgslurp = pkgName match { - case Some(name) => new PackageSlurper(name) slurp() - case _ => Set() - } - def ? = this - - def whoHas(name: String) = bts filter (_.decls exists (_.name.toString == name)) - def <:<[U: Manifest](other: U) = tpe <:< InternalInfo[U].tpe - def lub[U: Manifest](other: U) = intp.global.lub(List(tpe, InternalInfo[U].tpe)) - def glb[U: Manifest](other: U) = intp.global.glb(List(tpe, InternalInfo[U].tpe)) + private def newInfo[U: Manifest](value: U): InternalInfo[U] = new InternalInfo[U](Some(value)) + private def isSpecialized(s: Symbol) = s.name.toString contains "$mc" + private def isImplClass(s: Symbol) = s.name.toString endsWith "$class" + + /** Standard noise reduction filter. */ + def excludeMember(s: Symbol) = ( + isSpecialized(s) + || isImplClass(s) + || s.isAnonOrRefinementClass + || s.isAnonymousFunction + ) + def symbol = manifestToSymbol(fullManifest) + def tpe = manifestToType(fullManifest) + def name = symbol.name + def companion = symbol.companionSymbol + def info = symbol.info + def moduleClass = symbol.moduleClass + def owner = symbol.owner + def owners = symbol.ownerChain drop 1 + def signature = symbol.defString + + def decls = info.decls + def declsOverride = membersDeclared filter (_.isOverride) + def declsOriginal = membersDeclared filterNot (_.isOverride) + + def members = membersUnabridged filterNot excludeMember + def membersUnabridged = tpe.members + def membersDeclared = members filterNot excludeMember + def membersInherited = members filterNot (membersDeclared contains _) + def memberTypes = members filter (_.name.isTypeName) + def memberMethods = members filter (_.isMethod) + + def pkg = symbol.enclosingPackage + def pkgName = pkg.fullName + def pkgClass = symbol.enclosingPackageClass + def pkgMembers = pkg.info.members filterNot excludeMember + def pkgClasses = pkgMembers filter (s => s.isClass && s.isDefinedInPackage) + def pkgSymbols = new PackageSlurper(pkgClass).slurp() filterNot excludeMember + + def fullManifest = manifest[T] + def erasure = fullManifest.erasure + def shortClass = erasure.getName split "[$.]" last + + def baseClasses = tpe.baseClasses + def baseClassDecls = baseClasses map (x => (x, x.info.decls.toList.sortBy(_.name.toString))) toMap + def ancestors = baseClasses drop 1 + def ancestorDeclares(name: String) = ancestors filter (_.info member newTermName(name) ne NoSymbol) + def baseTypes = tpe.baseTypeSeq.toList + + def <:<[U: Manifest](other: U) = tpe <:< newInfo(other).tpe + def lub[U: Manifest](other: U) = intp.global.lub(List(tpe, newInfo(other).tpe)) + def glb[U: Manifest](other: U) = intp.global.glb(List(tpe, newInfo(other).tpe)) - def shortClass = erasure.getName split "[$.]" last override def toString = value match { case Some(x) => "%s (%s)".format(x, shortClass) case _ => erasure.getName @@ -288,11 +314,17 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl def slurp(): String = io.Streamable.slurp(url) def pp() { intp prettyPrint slurp() } } - + class RichSymbolList(syms: List[Symbol]) { + def sigs = syms map (_.defString) + def infos = syms map (_.info) + } + trait Implicits1 { // fallback implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new SinglePrettifierClass[T](x) + + implicit def liftToTypeName(s: String): TypeName = newTypeName(s) } trait Implicits2 extends Implicits1 { class RichSymbol(sym: Symbol) { @@ -309,7 +341,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name) implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol) - implicit def replInternalInfo[T: Manifest](x: T): InternalInfo[T] = new InternalInfo[T](Some(x)) + implicit def replInternalInfo[T: Manifest](x: T): InternalInfoWrapper[T] = new InternalInfoWrapper[T](Some(x)) implicit def replEnhancedStrings(s: String): RichReplString = new RichReplString(s) implicit def replMultiPrinting[T: Prettifier](xs: TraversableOnce[T]): MultiPrettifierClass[T] = new MultiPrettifierClass[T](xs.toSeq) @@ -318,6 +350,9 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) + + implicit def liftToTermName(s: String): TermName = newTermName(s) + implicit def replListOfSymbols(xs: List[Symbol]) = new RichSymbolList(xs) } trait ReplUtilities { diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala index dac20ad348..130af990ad 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala @@ -14,7 +14,10 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i // Avoiding deadlock if the compiler starts logging before // the lazy val is complete. if (intp.isInitializeComplete) { - if (intp.totalSilence) () + if (intp.totalSilence) { + if (isReplTrace) + super.printMessage("[silent] " + msg) + } else super.printMessage(msg) } else Console.println("[init] " + msg) diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala index 494eb4d50b..b51cf1228c 100644 --- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala +++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala @@ -211,7 +211,7 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { var start = 0 while (true) { val index = path.indexOf(separator, start) - assert(index < 0 || start < index) + assert(index < 0 || start < index, ((path, directory, start, index))) val name = path.substring(start, if (index < 0) length else index) file = getFile(file, name, if (index < 0) directory else true) if ((file eq null) || index < 0) return file diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala index 9efff089ba..a1b8e5e4d5 100644 --- a/src/compiler/scala/tools/nsc/io/Path.scala +++ b/src/compiler/scala/tools/nsc/io/Path.scala @@ -48,7 +48,7 @@ object Path { implicit def jfile2path(jfile: JFile): Path = apply(jfile) // java 7 style, we don't use it yet - // object AccessMode extends Enumeration("AccessMode") { + // object AccessMode extends Enumeration { // val EXECUTE, READ, WRITE = Value // } // def checkAccess(modes: AccessMode*): Boolean = { diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala index e5748b7c23..18409cfffe 100644 --- a/src/compiler/scala/tools/nsc/matching/Patterns.scala +++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala @@ -37,7 +37,7 @@ trait Patterns extends ast.TreeDSL { // } private lazy val dummyMethod = - new TermSymbol(NoSymbol, NoPosition, newTermName("matching$dummy")) + NoSymbol.newTermSymbol(newTermName("matching$dummy")) // Fresh patterns def emptyPatterns(i: Int): List[Pattern] = List.fill(i)(NoPattern) diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 36227c1052..da913a1601 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -28,7 +28,7 @@ trait Plugins { val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map Path.apply val classes = Plugin.loadAllFrom(jars, dirs, settings.disable.value) - // Lach plugin must only be instantiated once. A common pattern + // Each plugin must only be instantiated once. A common pattern // is to register annotation checkers during object construction, so // creating multiple plugin instances will leave behind stale checkers. classes map (Plugin.instantiate(_, this)) diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index b468e7c0af..f99d1399c0 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -95,8 +95,7 @@ class MutableSettings(val errorFn: String => Unit) */ def copy(): Settings = { val s = new Settings() - val xs = userSetSettings flatMap (_.unparse) - s.processArguments(xs.toList, true) + s.processArguments(recreateArgs, true) s } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6806ca03ba..e949cb3eb2 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -140,6 +140,7 @@ trait ScalaSettings extends AbsScalaSettings val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.") val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs.") val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") + val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") val skip = PhasesSetting ("-Yskip", "Skip") val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") @@ -165,7 +166,8 @@ trait ScalaSettings extends AbsScalaSettings val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.") val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification actions.") + val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification.") + val Ymacrodebug = BooleanSetting ("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, expansion, exceptions.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") . withPostSetHook(_ => interpreter.replProps.debug setValue true) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 4205c2ff36..942ec1fa86 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -207,7 +207,7 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { assert(root.isPackageClass, root) - root.setInfo(new PackageClassInfoType(new Scope(), root)) + root.setInfo(new PackageClassInfoType(newScope, root)) val sourcepaths = classpath.sourcepaths for (classRep <- classpath.classes if platform.doLoad(classRep)) { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index b818927ceb..a8083d7a2d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -238,9 +238,9 @@ abstract class ClassfileParser { val index = in.getChar(start + 1) val name = getExternalName(in.getChar(starts(index) + 1)) //assert(name.endsWith("$"), "Not a module class: " + name) - f = forceMangledName(name.subName(0, name.length - 1), true) + f = forceMangledName(name dropRight 1, true) if (f == NoSymbol) - f = definitions.getModule(name.subName(0, name.length - 1)) + f = definitions.getModule(name dropRight 1) } else { val origName = nme.originalName(name) val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol @@ -392,7 +392,7 @@ abstract class ClassfileParser { } def getBytes(indices: List[Int]): Array[Byte] = { - assert(!indices.isEmpty) + assert(!indices.isEmpty, indices) var value = values(indices.head).asInstanceOf[Array[Byte]] if (value eq null) { val bytesBuffer = ArrayBuffer.empty[Byte] @@ -536,8 +536,8 @@ abstract class ClassfileParser { addEnclosingTParams(clazz) parseInnerClasses() // also sets the isScala / isScalaRaw / hasMeta flags, see r15956 // get the class file parser to reuse scopes. - instanceDefs = new Scope - staticDefs = new Scope + instanceDefs = newScope + staticDefs = newScope val classInfo = ClassInfoType(parseParents, instanceDefs, clazz) val staticInfo = ClassInfoType(List(), staticDefs, statics) @@ -679,7 +679,7 @@ abstract class ClassfileParser { var index = 0 val end = sig.length def accept(ch: Char) { - assert(sig(index) == ch) + assert(sig(index) == ch, (sig(index), ch)) index += 1 } def subName(isDelimiter: Char => Boolean): Name = { @@ -736,7 +736,7 @@ abstract class ClassfileParser { } } accept('>') - assert(xs.length > 0) + assert(xs.length > 0, tp) newExistentialType(existentials.toList, typeRef(pre, classSym, xs.toList)) } else if (classSym.isMonomorphicType) { tp @@ -750,7 +750,7 @@ abstract class ClassfileParser { res } case tp => - assert(sig(index) != '<') + assert(sig(index) != '<', tp) tp } @@ -776,7 +776,7 @@ abstract class ClassfileParser { appliedType(definitions.ArrayClass.tpe, List(elemtp)) case '(' => // we need a method symbol. given in line 486 by calling getType(methodSym, ..) - assert(sym ne null) + assert(sym ne null, sig) val paramtypes = new ListBuffer[Type]() while (sig(index) != ')') { paramtypes += objToAny(sig2type(tparams, skiptvs)) @@ -809,7 +809,7 @@ abstract class ClassfileParser { var tparams = classTParams val newTParams = new ListBuffer[Symbol]() if (sig(index) == '<') { - assert(sym != null) + assert(sym != null, sig) index += 1 val start = index while (sig(index) != '>') { @@ -974,18 +974,18 @@ abstract class ClassfileParser { def parseScalaSigBytes: Option[ScalaSigBytes] = { val tag = in.nextByte.toChar - assert(tag == STRING_TAG) + assert(tag == STRING_TAG, tag) Some(ScalaSigBytes(pool getBytes in.nextChar)) } def parseScalaLongSigBytes: Option[ScalaSigBytes] = { val tag = in.nextByte.toChar - assert(tag == ARRAY_TAG) + assert(tag == ARRAY_TAG, tag) val stringCount = in.nextChar val entries = for (i <- 0 until stringCount) yield { val stag = in.nextByte.toChar - assert(stag == STRING_TAG) + assert(stag == STRING_TAG, stag) in.nextChar.toInt } Some(ScalaSigBytes(pool.getBytes(entries.toList))) @@ -1074,27 +1074,27 @@ abstract class ClassfileParser { } def enterClassAndModule(entry: InnerClassEntry, completer: global.loaders.SymbolLoader, jflags: Int) { - val name = entry.originalName - var sflags = toScalaClassFlags(jflags) + val name = entry.originalName + var sflags = toScalaClassFlags(jflags) + val owner = getOwner(jflags) + val scope = getScope(jflags) + val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer + val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer - val innerClass = getOwner(jflags).newClass(name.toTypeName).setInfo(completer).setFlag(sflags) - val innerModule = getOwner(jflags).newModule(name.toTermName).setInfo(completer).setFlag(sflags) innerModule.moduleClass setInfo global.loaders.moduleClassLoader - - getScope(jflags) enter innerClass - getScope(jflags) enter innerModule + scope enter innerClass + scope enter innerModule val decls = innerClass.enclosingPackage.info.decls - val e = decls.lookupEntry(className(entry.externalName)) - if (e ne null) { - //println("removing " + e) - decls.unlink(e) - } - val e1 = decls.lookupEntry(className(entry.externalName).toTypeName) - if (e1 ne null) { - //println("removing " + e1) - decls.unlink(e1) + def unlinkIfPresent(name: Name) = { + val e = decls lookupEntry name + if (e ne null) + decls unlink e } + + val cName = className(entry.externalName) + unlinkIfPresent(cName.toTermName) + unlinkIfPresent(cName.toTypeName) } for (entry <- innerClasses.values) { @@ -1208,7 +1208,12 @@ abstract class ClassfileParser { atPhase(currentRun.typerPhase)(getMember(sym, innerName.toTypeName)) else getMember(sym, innerName.toTypeName) - assert(s ne NoSymbol, sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members) + + assert(s ne NoSymbol, + "" + ((externalName, outerName, innerName, sym.fullLocationString)) + " / " + + " while parsing " + ((in.file, busy)) + + sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members + ) s case None => @@ -1264,13 +1269,13 @@ abstract class ClassfileParser { 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.toplevelClass != NoSymbol) - sym.privateWithin = sym.toplevelClass.owner + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner // protected in java means package protected. #3946 if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.toplevelClass != NoSymbol) - sym.privateWithin = sym.toplevelClass.owner + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner } @inline private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 3c97122c9c..7d42dabc08 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -434,7 +434,7 @@ abstract class ICodeReader extends ClassfileParser { val padding = if ((pc + size) % 4 != 0) 4 - ((pc + size) % 4) else 0 size += padding in.bp += padding - assert((pc + size % 4) != 0) + assert((pc + size % 4) != 0, pc) /* var byte1 = in.nextByte; size += 1; while (byte1 == 0) { byte1 = in.nextByte; size += 1; } val default = byte1 << 24 | in.nextByte << 16 | in.nextByte << 8 | in.nextByte; @@ -454,7 +454,7 @@ abstract class ICodeReader extends ClassfileParser { val padding = if ((pc + size) % 4 != 0) 4 - ((pc + size) % 4) else 0 size += padding in.bp += padding - assert((pc + size % 4) != 0) + assert((pc + size % 4) != 0, pc) val default = pc + in.nextInt; size += 4 val npairs = in.nextInt; size += 4 var tags: List[List[Int]] = Nil @@ -988,7 +988,7 @@ abstract class ICodeReader extends ClassfileParser { def enterParam(idx: Int, kind: TypeKind) = { val sym = method.symbol.newVariable(newTermName("par" + idx)).setInfo(kind.toType) val l = new Local(sym, kind, true) - assert(!locals.isDefinedAt(idx)) + assert(!locals.isDefinedAt(idx), locals(idx)) locals += (idx -> List((l, kind))) l } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala index ead431c8d7..eb8e7a14a5 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/MetaParser.scala @@ -108,7 +108,7 @@ abstract class MetaParser{ } protected def parseClass() { - locals = new Scope + locals = newScope def parse(): Type = { nextToken() if (token == "[") { @@ -130,7 +130,7 @@ abstract class MetaParser{ protected def parseMethod() { val globals = locals - locals = if (locals eq null) new Scope else new Scope(locals) + locals = if (locals eq null) newScope else newNestedScope(locals) def parse(): Type = { nextToken(); if (token == "[") PolyType(parseTypeParams(), parse()) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 2eddd36db0..25ae6f33d2 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -59,7 +59,7 @@ abstract class Pickler extends SubComponent { } } // If there are any erroneous types in the tree, then we will crash - // when we pickle it: so let's report an erorr instead. We know next + // when we pickle it: so let's report an error instead. We know next // to nothing about what happened, but our supposition is a lot better // than "bad type: <error>" in terms of explanatory power. for (t <- unit.body ; if t.isErroneous) { diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index 6c238f52cc..e11a5a4ad9 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -165,7 +165,7 @@ abstract class TypeParser { clrTypes.sym2type(typMgdPtr) = clazzMgdPtr /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance, because there's no metadata-level representation for a "boxed valuetype" */ - val instanceDefsMgdPtr = new Scope + val instanceDefsMgdPtr = newScope val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr) clazzMgdPtr.setFlag(flags) clazzMgdPtr.setInfo(classInfoMgdPtr) @@ -196,8 +196,8 @@ abstract class TypeParser { } } /* END CLR generics (snippet 2) */ - instanceDefs = new Scope - staticDefs = new Scope + instanceDefs = newScope + staticDefs = newScope val classInfoAsInMetadata = { val ifaces: Array[MSILType] = typ.getInterfaces() @@ -212,7 +212,7 @@ abstract class TypeParser { } // methods, properties, events, fields are entered in a moment if (canBeTakenAddressOf) { - val instanceDefsBoxed = new Scope + val instanceDefsBoxed = newScope ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed) } else ClassInfoType(parents.toList, instanceDefs, clazz) diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 17d63ea439..b4ec8a23ce 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -84,11 +84,16 @@ abstract class AddInterfaces extends InfoTransform { atPhase(implClassPhase) { log("%s.implClass == %s".format(iface, iface.implClass)) val implName = nme.implClassName(iface.name) - var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol - impl.info + var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol + + // !!! Why does forcing the impl's info here lead to a crash? + // See test case pos/trait-force-info.scala for a minimization. + // It crashes like this: + // + // [log lazyvals] trait ContextTrees.implClass == class ContextTrees$class + // error: java.lang.AssertionError: assertion failed: (scala.tools.nsc.typechecker.Contexts$NoContext$,scala.tools.nsc.typechecker.Contexts,NoContext$,trait Contexts in package typechecker) / while parsing (/scala/trunk/build/pack/lib/scala-compiler.jar(scala/tools/nsc/interactive/ContextTrees$class.class),Some(class ContextTrees$class))trait Contexts.NoContext$ linkedModule: <none>List() val originalImpl = impl - val originalImplString = originalImpl.hasFlagsToString(-1L) if (impl != NoSymbol) { // Unlink a pre-existing symbol only if the implementation class is // visible on the compilation classpath. In general this is true under @@ -114,8 +119,8 @@ abstract class AddInterfaces extends InfoTransform { impl setInfo new LazyImplClassType(iface) implClassMap(iface) = impl debuglog( - "generating impl class " + impl + " " + impl.hasFlagsToString(-1L) + " in " + iface.owner + ( - if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.fullLocationString + " " + originalImplString + ")" + "generating impl class " + impl.debugLocationString + " in " + iface.owner + ( + if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.debugLocationString + ")" ) ) impl @@ -139,7 +144,7 @@ abstract class AddInterfaces extends InfoTransform { * given the decls ifaceDecls of its interface. */ private def implDecls(implClass: Symbol, ifaceDecls: Scope): Scope = { - val decls = new Scope + val decls = newScope if ((ifaceDecls lookup nme.MIXIN_CONSTRUCTOR) == NoSymbol) decls enter ( implClass.newMethod(nme.MIXIN_CONSTRUCTOR, implClass.pos) @@ -179,7 +184,7 @@ abstract class AddInterfaces extends InfoTransform { ) def implType(tp: Type): Type = tp match { case ClassInfoType(parents, decls, _) => - assert(phase == implClassPhase) + assert(phase == implClassPhase, tp) ClassInfoType( ObjectClass.tpe +: (parents.tail map mixinToImplClass filter (_.typeSymbol != ObjectClass)) :+ iface.tpe, implDecls(sym, decls), diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 23817545e2..d1c71faf1e 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -447,7 +447,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { val closureClass = clazz.newClass(nme.delayedInitArg.toTypeName, impl.pos, SYNTHETIC | FINAL) val closureParents = List(AbstractFunctionClass(0).tpe, ScalaObjectClass.tpe) - closureClass setInfoAndEnter new ClassInfoType(closureParents, new Scope, closureClass) + closureClass setInfoAndEnter new ClassInfoType(closureParents, newScope, closureClass) val outerField = ( closureClass diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 71696c24e6..5f84d765b9 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -44,22 +44,16 @@ abstract class Erasure extends AddInterfaces // class object is that of java.lang.Integer, not Int. // // TODO: If T is final, return type could be Class[T]. Should it? - def getClassReturnType(tp: Type): Type = { - val sym = tp.typeSymbol - - if (phase.erasedTypes) ClassClass.tpe - else if (isValueClass(sym)) ClassType(tp.widen) - else { - val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) - val upperBound = ( - if (isPhantomClass(sym)) AnyClass.tpe + def getClassReturnType(tpe: Type): Type = { + if (phase.erasedTypes) ClassClass.tpe else { + val tp = tpe.widen.normalize + val sym = tp.typeSymbol + + if (isValueClass(sym)) ClassType(tp) + else boundedClassType( + if (isPhantomClass(sym)) ObjectClass.tpe else if (sym.isLocalClass) intersectionDominator(tp.parents) - else tp.widen - ) - - existentialAbstraction( - eparams, - ClassType(eparams.head setInfo TypeBounds.upper(upperBound) tpe) + else tp ) } } @@ -742,7 +736,7 @@ abstract class Erasure extends AddInterfaces //println("computing bridges for " + owner)//DEBUG assert(phase == currentRun.erasurePhase) val site = owner.thisType - val bridgesScope = new Scope + val bridgesScope = newScope val bridgeTarget = new mutable.HashMap[Symbol, Symbol] var bridges: List[Tree] = List() val opc = atPhase(currentRun.explicitouterPhase) { @@ -797,7 +791,7 @@ abstract class Erasure extends AddInterfaces // && (bridge.paramss.nonEmpty && bridge.paramss.head.nonEmpty && bridge.paramss.head.tail.isEmpty) // does the first argument list has exactly one argument -- for user-defined unapplies we can't be sure && !(atPhase(phase.next)(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?) import CODE._ - val typeTest = gen.mkIsInstanceOf(REF(bridge.paramss.head.head), member.tpe.params.head.tpe, any = true, wrapInApply = true) // any = true since we're before erasure (?), wrapInapply is true since we're after uncurry + val typeTest = gen.mkIsInstanceOf(REF(bridge.firstParam), member.tpe.params.head.tpe, any = true, wrapInApply = true) // any = true since we're before erasure (?), wrapInapply is true since we're after uncurry // println("unapp type test: "+ typeTest) IF (typeTest) THEN bridgingCall ELSE REF(NoneModule) } else bridgingCall diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 14f3dc16fa..7f7f7e7b65 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -427,7 +427,7 @@ abstract class ExplicitOuter extends InfoTransform } val t = atPos(tree.pos) { - val context = MatrixContext(currentRun.currentUnit, transform, localTyper, currentOwner, tree.tpe) + val context = MatrixContext(currentUnit, transform, localTyper, currentOwner, tree.tpe) val t_untyped = handlePattern(nselector, ncases, checkExhaustive, context) /* if @switch annotation is present, verify the resulting tree is a Match */ @@ -506,7 +506,7 @@ abstract class ExplicitOuter extends InfoTransform val outerVal = atPos(tree.pos)(qual match { // it's a call between constructors of same class case _: This => - assert(outerParam != NoSymbol) + assert(outerParam != NoSymbol, tree) outerValue case _ => gen.mkAttributedQualifier(qual.tpe.prefix match { diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index b17fd7b9b0..89f1cc26e0 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -60,12 +60,12 @@ abstract class Flatten extends InfoTransform { private val flattened = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if isFlattenablePrefix(pre) => - assert(args.isEmpty && sym.toplevelClass != NoSymbol, sym.ownerChain) - typeRef(sym.toplevelClass.owner.thisType, sym, Nil) + assert(args.isEmpty && sym.enclosingTopLevelClass != NoSymbol, sym.ownerChain) + typeRef(sym.enclosingTopLevelClass.owner.thisType, sym, Nil) case ClassInfoType(parents, decls, clazz) => var parents1 = parents val decls1 = scopeTransform(clazz) { - val decls1 = new Scope() + val decls1 = newScope if (clazz.isPackageClass) { atPhase(phase.next)(decls foreach (decls1 enter _)) } else { @@ -119,7 +119,7 @@ abstract class Flatten extends InfoTransform { val sym = tree.symbol val tree1 = tree match { case ClassDef(_, _, _, _) if sym.isNestedClass => - liftedDefs(sym.toplevelClass.owner) += tree + liftedDefs(sym.enclosingTopLevelClass.owner) += tree EmptyTree case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) => atPhase(phase.next) { diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 712298bd89..4fc7b9f92f 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -65,7 +65,10 @@ abstract class LambdaLift extends InfoTransform { /** The set of symbols that need to be renamed. */ private val renamable = newSymSet - private val renamableImplClasses = mutable.HashMap[Name, Symbol]() withDefaultValue NoSymbol + // (trait, name) -> owner + private val localTraits = mutable.HashMap[(Symbol, Name), Symbol]() + // (owner, name) -> implClass + private val localImplClasses = mutable.HashMap[(Symbol, Name), Symbol]() /** A flag to indicate whether new free variables have been found */ private var changedFreeVars: Boolean = _ @@ -167,8 +170,13 @@ abstract class LambdaLift extends InfoTransform { // arrangements, and then have separate methods which attempt to compensate // for that failure. There should be exactly one method for any given // entity which always gives the right answer. - if (sym.isImplClass) renamableImplClasses(nme.interfaceName(sym.name)) = sym - else renamable addEntry sym + if (sym.isImplClass) + localImplClasses((sym.owner, nme.interfaceName(sym.name))) = sym + else { + renamable addEntry sym + if (sym.isTrait) + localTraits((sym, sym.name)) = sym.owner + } } case DefDef(_, _, _, _, _, _) => if (sym.isLocal) { @@ -237,14 +245,21 @@ abstract class LambdaLift extends InfoTransform { debuglog("renaming impl class in step with %s: %s => %s".format(traitSym, originalImplName, implSym.name)) } - + for (sym <- renamable) { // If we renamed a trait from Foo to Foo$1, we must rename the implementation // class from Foo$class to Foo$1$class. (Without special consideration it would - // become Foo$class$1 instead.) - val implClass = if (sym.isTrait) renamableImplClasses(sym.name) else NoSymbol - if ((implClass ne NoSymbol) && (sym.owner == implClass.owner)) renameTrait(sym, implClass) - else renameSym(sym) + // become Foo$class$1 instead.) Since the symbols are being renamed out from + // under us, and there's no reliable link between trait symbol and impl symbol, + // we have maps from ((trait, name)) -> owner and ((owner, name)) -> impl. + localTraits remove ((sym, sym.name)) match { + case None => renameSym(sym) + case Some(owner) => + localImplClasses remove ((owner, sym.name)) match { + case Some(implSym) => renameTrait(sym, implSym) + case _ => renameSym(sym) // pure interface, no impl class + } + } } atPhase(phase.next) { diff --git a/src/compiler/scala/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala deleted file mode 100644 index c5475fa0f2..0000000000 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ /dev/null @@ -1,501 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Gilles Dubochet - */ - -package scala.tools.nsc -package transform - -import symtab._ -import Flags._ -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer -import scala.tools.nsc.util.FreshNameCreator -import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } - -/** - * Translate expressions of the form reflect.Code.lift(exp) - * to the reified "reflect trees" representation of exp. - * Also: mutable variables that are accessed from a local function are wrapped in refs. - * - * @author Martin Odersky - * @version 2.10 - */ -abstract class LiftCode extends Transform with TypingTransformers { - - import global._ // the global environment - import definitions._ // standard classes and methods - import typer.{ typed, atOwner } // methods to type trees - - val symbols: global.type = global - - /** the following two members override abstract members in Transform */ - val phaseName: String = "liftcode" - - def newTransformer(unit: CompilationUnit): Transformer = - new Codifier(unit) - - private lazy val MirrorMemberNames = - ReflectRuntimeMirror.info.nonPrivateMembers filter (_.isTerm) map (_.toString) toSet - - // Would be nice if we could use something like this to check the names, - // but it seems that info is unavailable when I need it. - private def mirrorFactoryName(value: Any): Option[String] = value match { - // Modest (inadequate) sanity check that there's a member by this name. - case x: Product if MirrorMemberNames(x.productPrefix) => - Some(x.productPrefix) - case _ => - Some(value.getClass.getName split """[$.]""" last) filter MirrorMemberNames - } - private def isMirrorMemberObject(value: Product) = value match { - case NoType | NoPrefix | NoPosition | EmptyTree => true - case _ => false - } - - class Codifier(unit: CompilationUnit) extends TypingTransformer(unit) { - - val reifyDebug = settings.Yreifydebug.value - val debugTrace = util.trace when reifyDebug - - val reifyCopypaste = settings.Yreifycopypaste.value - def printCopypaste(tree: Tree) { - import scala.reflect.api.Modifier - import scala.reflect.api.Modifier._ - - def copypasteModifier(mod: Modifier.Value): String = mod match { - case mod @ ( - `protected` | `private` | `override` | - `abstract` | `final` | `sealed` | - `implicit` | `lazy` | `macro` | - `case` | `trait`) => "`" + mod.toString + "`" - case mod => mod.toString - } - - // I fervently hope this is a test case or something, not anything being - // depended upon. Of more fragile code I cannot conceive. - for (line <- (tree.toString.split(Properties.lineSeparator) drop 2 dropRight 1)) { - var s = line.trim - s = s.replace("$mr.", "") - s = s.replace(".apply", "") - s = s.replace("scala.collection.immutable.", "") - s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") - s = "List\\[.*?\\]".r.replaceAllIn(s, "List") - s = s.replace("immutable.this.Nil", "List()") - s = s.replace("modifiersFromInternalFlags", "Modifiers") - s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") - s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { - val buf = new StringBuilder - - val flags = m.group(1).toLong - val s_flags = Flags.modifiersOfFlags(flags) map copypasteModifier mkString ", " - if (s_flags != "") - buf.append("Set(" + s_flags + ")") - - val privateWithin = "" + m.group(2) - if (privateWithin != "") - buf.append(", newTypeName(\"" + privateWithin + "\")") - - val annotations = m.group(3) - if (annotations.nonEmpty) - buf.append(", List(" + annotations + ")") - - "Modifiers(" + buf.toString + ")" - }) - s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => { - val flags = m.group(1).toLong - val mods = Flags.modifiersOfFlags(flags) map copypasteModifier - "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))" - }) - - println(s) - } - } - - override def transformUnit(unit: CompilationUnit) { - atPhase(phase.next) { - super.transformUnit(unit) - } - } - - override def transform(tree: Tree): Tree = { - val sym = tree.symbol - tree match { - case Apply(_, List(tree)) if sym == Code_lift => // reify Code.lift[T](expr) instances - val saved = printTypings - try { - printTypings = reifyDebug - debugTrace("transformed = ") { - val result = localTyper.typedPos(tree.pos)(codify(super.transform(tree))) - if (reifyCopypaste) printCopypaste(result) - result - } - } finally printTypings = saved - case _ => - super.transform(tree) - } - } - - def codify(tree: Tree): Tree = debugTrace("codified " + tree + " -> ") { - val targetType = definitions.CodeClass.primaryConstructor.info.paramTypes.head - val reifier = new Reifier() - val arg = gen.mkAsInstanceOf(reifier.reifyTopLevel(tree), targetType, wrapInApply = false) - val treetpe = - if (tree.tpe.typeSymbol.isAnonymousClass) tree.tpe.typeSymbol.classBound - else tree.tpe - New(TypeTree(appliedType(definitions.CodeClass.typeConstructor, List(treetpe.widen))), - List(List(arg))) - } - } - - /** - * Given a tree or type, generate a tree that when executed at runtime produces the original tree or type. - * For instance: Given - * - * var x = 1; Code(x + 1) - * - * The `x + 1` expression is reified to - * - * $mr.Apply($mr.Select($mr.Ident($mr.freeVar("x". <Int>, x), "+"), List($mr.Literal($mr.Constant(1)))))) - * - * Or, the term name 'abc' is reified to: - * - * $mr.Apply($mr.Select($mr.Ident("newTermName")), List(Literal(Constant("abc"))))) - * - * todo: Treat embedded Code blocks by merging them into containing block - * - */ - class Reifier() { - - final val scalaPrefix = "scala." - final val localPrefix = "$local" - final val memoizerName = "$memo" - - val reifyDebug = settings.Yreifydebug.value - - private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree - private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms` - private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified - - /** - * Generate tree of the form - * - * { val $mr = scala.reflect.runtime.Mirror - * $local1 = new TypeSymbol(owner1, NoPosition, name1) - * ... - * $localN = new TermSymbol(ownerN, NoPositiion, nameN) - * $local1.setInfo(tpe1) - * ... - * $localN.setInfo(tpeN) - * $localN.setAnnotations(annotsN) - * rtree - * } - * - * where - * - * - `$localI` are free type symbols in the environment, as well as local symbols - * of refinement types. - * - `tpeI` are the info's of `symI` - * - `rtree` is code that generates `data` at runtime, maintaining all attributes. - * - `data` is typically a tree or a type. - */ - def reifyTopLevel(data: Any): Tree = { - val rtree = reify(data) - Block(mirrorAlias :: reifySymbolTableSetup, rtree) - } - - private def isLocatable(sym: Symbol) = - sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0 - - private def registerReifiableSymbol(sym: Symbol): Unit = - if (!(symIndex contains sym)) { - sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol - symIndex(sym) = reifiableSyms.length - reifiableSyms += sym - } - - // helper methods - - private def localName(sym: Symbol): TermName = - newTermName(localPrefix + symIndex(sym)) - - private def call(fname: String, args: Tree*): Tree = - Apply(termPath(fname), args.toList) - - private def mirrorSelect(name: String): Tree = - termPath(nme.MIRROR_PREFIX + name) - - private def mirrorCall(name: TermName, args: Tree*): Tree = - call("" + (nme.MIRROR_PREFIX append name), args: _*) - - private def mirrorCall(name: String, args: Tree*): Tree = - call(nme.MIRROR_PREFIX + name, args: _*) - - private def mirrorFactoryCall(value: Product, args: Tree*): Tree = - mirrorCall(value.productPrefix, args: _*) - - private def scalaFactoryCall(name: String, args: Tree*): Tree = - call(scalaPrefix + name + ".apply", args: _*) - - private def mkList(args: List[Tree]): Tree = - scalaFactoryCall("collection.immutable.List", args: _*) - - private def reifyModifiers(m: Modifiers) = - mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations)) - - private def reifyAggregate(name: String, args: Any*) = - scalaFactoryCall(name, (args map reify).toList: _*) - - /** - * Reify a list - */ - private def reifyList(xs: List[Any]): Tree = - mkList(xs map reify) - - /** Reify a name */ - private def reifyName(name: Name) = - mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString))) - - private def isFree(sym: Symbol) = - !(symIndex contains sym) - - /** - * Reify a reference to a symbol - */ - private def reifySymRef(sym: Symbol): Tree = { - symIndex get sym match { - case Some(idx) => - Ident(localName(sym)) - case None => - if (sym == NoSymbol) - mirrorSelect("NoSymbol") - else if (sym.isModuleClass) - Select(reifySymRef(sym.sourceModule), "moduleClass") - else if (sym.isStatic && sym.isClass) - mirrorCall("staticClass", reify(sym.fullName)) - else if (sym.isStatic && sym.isModule) - mirrorCall("staticModule", reify(sym.fullName)) - else if (isLocatable(sym)) - if (sym.isTypeParameter) - mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos)) - else { - if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter) - val rowner = reify(sym.owner) - val rname = reify(sym.name.toString) - if (sym.isType) - mirrorCall("selectType", rowner, rname) - else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) { - val index = sym.owner.info.decl(sym.name).alternatives indexOf sym - assert(index >= 0, sym) - mirrorCall("selectOverloadedMethod", rowner, rname, reify(index)) - } else - mirrorCall("selectTerm", rowner, rname) - } - else { - if (sym.isTerm) { - if (reifyDebug) println("Free: " + sym) - val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false) - def markIfCaptured(arg: Ident): Tree = - if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg - mirrorCall("freeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym))) - } else { - if (reifyDebug) println("Late local: " + sym) - registerReifiableSymbol(sym) - reifySymRef(sym) - } - } - } - } - - /** - * reify the creation of a symbol - */ - private def reifySymbolDef(sym: Symbol): Tree = { - if (reifyDebug) println("reify sym def " + sym) - - ValDef(NoMods, localName(sym), TypeTree(), - Apply( - Select(reify(sym.owner), "newNestedSymbol"), - List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags))) - ) - ) - } - - /** - * Generate code to add type and annotation info to a reified symbol - */ - private def fillInSymbol(sym: Symbol): Tree = { - val rset = Apply(Select(reifySymRef(sym), nme.setTypeSig), List(reifyType(sym.info))) - if (sym.annotations.isEmpty) rset - else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations))) - } - - /** Reify a scope */ - private def reifyScope(scope: Scope): Tree = { - scope foreach registerReifiableSymbol - mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*) - } - - /** Reify a list of symbols that need to be created */ - private def reifySymbols(syms: List[Symbol]): Tree = { - syms foreach registerReifiableSymbol - mkList(syms map reifySymRef) - } - - /** Reify a type that defines some symbols */ - private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree = - mirrorFactoryCall(value, reifySymbols(bound), reify(underlying)) - - /** Reify a type */ - private def reifyType(tpe0: Type): Tree = { - val tpe = tpe0.normalize - val tsym = tpe.typeSymbol - if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic) - Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor) - else tpe match { - case t @ NoType => - reifyMirrorObject(t) - case t @ NoPrefix => - reifyMirrorObject(t) - case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => - mirrorCall(nme.thisModuleType, reify(clazz.fullName)) - case t @ RefinedType(parents, decls) => - registerReifiableSymbol(tpe.typeSymbol) - mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) - case t @ ClassInfoType(parents, decls, clazz) => - registerReifiableSymbol(clazz) - mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) - case t @ ExistentialType(tparams, underlying) => - reifyTypeBinder(t, tparams, underlying) - case t @ PolyType(tparams, underlying) => - reifyTypeBinder(t, tparams, underlying) - case t @ MethodType(params, restpe) => - reifyTypeBinder(t, params, restpe) - case _ => - reifyProductUnsafe(tpe) - } - } - - /** Reify a tree */ - private def reifyTree(tree: Tree): Tree = tree match { - case EmptyTree => - reifyMirrorObject(EmptyTree) - case This(_) if !(boundSyms contains tree.symbol) => - reifyFree(tree) - case Ident(_) if !(boundSyms contains tree.symbol) => - if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { - captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here. - mirrorCall("Select", reifyFree(tree), reifyName(nme.elem)) - } else reifyFree(tree) - case tt: TypeTree if (tt.tpe != null) => - if (!(boundSyms exists (tt.tpe contains _))) mirrorCall("TypeTree", reifyType(tt.tpe)) - else if (tt.original != null) reify(tt.original) - else mirrorCall(nme.TypeTree) - case ta @ TypeApply(hk, ts) => - val thereAreOnlyTTs = ts collect { case t if !t.isInstanceOf[TypeTree] => t } isEmpty; - val ttsAreNotEssential = ts collect { case tt: TypeTree => tt } find { tt => tt.original != null } isEmpty; - if (thereAreOnlyTTs && ttsAreNotEssential) reifyTree(hk) else reifyProduct(ta) - case global.emptyValDef => - mirrorSelect(nme.emptyValDef) - case _ => - if (tree.isDef) - boundSyms += tree.symbol - - reifyProduct(tree) - /* - if (tree.isDef || tree.isInstanceOf[Function]) - registerReifiableSymbol(tree.symbol) - if (tree.hasSymbol) - rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol))) - Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe))) -*/ - } - - /** - * Reify a free reference. The result will be either a mirror reference - * to a global value, or else a mirror Literal. - */ - private def reifyFree(tree: Tree): Tree = - mirrorCall(nme.Ident, reifySymRef(tree.symbol)) - - // todo: consider whether we should also reify positions - private def reifyPosition(pos: Position): Tree = - reifyMirrorObject(NoPosition) - - // !!! we must eliminate these casts. - private def reifyProductUnsafe(x: Any): Tree = - reifyProduct(x.asInstanceOf[Product]) - private def reifyProduct(x: Product): Tree = - mirrorCall(x.productPrefix, (x.productIterator map reify).toList: _*) - - /** - * Reify a case object defined in Mirror - */ - private def reifyMirrorObject(name: String): Tree = mirrorSelect(name) - private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix) - - private def isReifiableConstant(value: Any) = value match { - case null => true // seems pretty reifable to me? - case _: String => true - case _ => isAnyVal(value) - } - - /** Reify an arbitary value */ - private def reify(value: Any): Tree = value match { - case tree: Tree => reifyTree(tree) - case sym: Symbol => reifySymRef(sym) - case tpe: Type => reifyType(tpe) - case xs: List[_] => reifyList(xs) - case xs: Array[_] => scalaFactoryCall(nme.Array, xs map reify: _*) - case scope: Scope => reifyScope(scope) - case x: Name => reifyName(x) - case x: Position => reifyPosition(x) - case x: Modifiers => reifyModifiers(x) - case _ => - if (isReifiableConstant(value)) Literal(Constant(value)) - else reifyProductUnsafe(value) - } - - /** - * An (unreified) path that refers to definition with given fully qualified name - * @param mkName Creator for last portion of name (either TermName or TypeName) - */ - private def path(fullname: String, mkName: String => Name): Tree = { - val parts = fullname split "\\." - val prefixParts = parts.init - val lastName = mkName(parts.last) - if (prefixParts.isEmpty) Ident(lastName) - else { - val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _)) - Select(prefixTree, lastName) - } - } - - /** An (unreified) path that refers to term definition with given fully qualified name */ - private def termPath(fullname: String): Tree = path(fullname, newTermName) - - /** An (unreified) path that refers to type definition with given fully qualified name */ - private def typePath(fullname: String): Tree = path(fullname, newTypeName) - - private def mirrorAlias = - ValDef(NoMods, nme.MIRROR_SHORT, TypeTree(), termPath(fullnme.MirrorPackage)) - - /** - * Generate code that generates a symbol table of all symbols registered in `reifiableSyms` - */ - private def reifySymbolTableSetup: List[Tree] = { - val symDefs, fillIns = new mutable.ArrayBuffer[Tree] - var i = 0 - while (i < reifiableSyms.length) { - // fillInSymbol might create new reifiableSyms, that's why this is done iteratively - symDefs += reifySymbolDef(reifiableSyms(i)) - fillIns += fillInSymbol(reifiableSyms(i)) - i += 1 - } - - symDefs.toList ++ fillIns.toList - } - - private def cannotReify(value: Any): Nothing = - abort("don't know how to reify " + value + " of " + value.getClass) - } -} diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index bf19cf10e9..b3b7596f9a 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -403,12 +403,12 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass, clazz + " has no sourceModule: sym = " + sym + " sym.tpe = " + sym.tpe) parents1 = List() - decls1 = new Scope(decls.toList filter isImplementedStatically) + decls1 = newScopeWith(decls.toList filter isImplementedStatically: _*) } else if (!parents.isEmpty) { parents1 = parents.head :: (parents.tail map toInterface) } } - //decls1 = atPhase(phase.next)(new Scope(decls1.toList))//debug + //decls1 = atPhase(phase.next)(newScopeWith(decls1.toList: _*))//debug if ((parents1 eq parents) && (decls1 eq decls)) tp else ClassInfoType(parents1, decls1, clazz) @@ -480,7 +480,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** The rootContext used for typing */ private val rootContext = - erasure.NoContext.make(EmptyTree, RootClass, new Scope) + erasure.NoContext.make(EmptyTree, RootClass, newScope) /** The typer */ private var localTyper: erasure.Typer = _ @@ -1053,7 +1053,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else accessedRef match { case Literal(_) => accessedRef case _ => - val init = Assign(accessedRef, Ident(sym.paramss.head.head)) + val init = Assign(accessedRef, Ident(sym.firstParam)) val getter = sym.getter(clazz) if (!needsInitFlag(getter)) init diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 70f8d37585..e49f8d7c0b 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -45,8 +45,14 @@ abstract class OverridingPairs { * Types always match. Term symbols match if their membertypes * relative to <base>.this do */ - protected def matches(sym1: Symbol, sym2: Symbol): Boolean = - sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) + protected def matches(sym1: Symbol, sym2: Symbol): Boolean = { + def tp_s(s: Symbol) = self.memberType(s) + "/" + self.memberType(s).getClass + val result = sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) + debuglog("overriding-pairs? %s matches %s (%s vs. %s) == %s".format( + sym1.fullLocationString, sym2.fullLocationString, tp_s(sym1), tp_s(sym2), result)) + + result + } /** An implementation of BitSets as arrays (maybe consider collection.BitSet * for that?) The main purpose of this is to implement @@ -74,7 +80,7 @@ abstract class OverridingPairs { } /** The symbols that can take part in an overriding pair */ - private val decls = new Scope + private val decls = newScope // fill `decls` with overriding shadowing overridden */ { def fillDecls(bcs: List[Symbol], deferredflag: Int) { diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index fd826fb6d8..8c34a9139d 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -6,12 +6,9 @@ package scala.tools.nsc package transform - import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } - - /** Specialize code on types. * * Make sure you've read the thesis: @@ -71,10 +68,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { RootClass, BooleanClass, UnitClass, ArrayClass, ScalaValueClasses, isValueClass, isScalaValueType, SpecializedClass, RepeatedParamClass, JavaRepeatedParamClass, - AnyRefClass, ObjectClass, Predef_AnyRef, - uncheckedVarianceClass + AnyRefClass, ObjectClass, AnyRefModule, + GroupOfSpecializable, uncheckedVarianceClass, ScalaInlineClass } - + /** TODO - this is a lot of maps. */ @@ -105,16 +102,26 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def isSpecialized(sym: Symbol) = sym hasAnnotation SpecializedClass private def hasSpecializedFlag(sym: Symbol) = sym hasFlag SPECIALIZED private def specializedTypes(tps: List[Symbol]) = tps filter isSpecialized - private def specializedOn(sym: Symbol) = sym getAnnotation SpecializedClass match { - case Some(AnnotationInfo(_, args, _)) => args - case _ => Nil + private def specializedOn(sym: Symbol): List[Symbol] = { + sym getAnnotation SpecializedClass match { + case Some(ann @ AnnotationInfo(_, args, _)) => + args map (_.tpe) flatMap { tp => + tp baseType GroupOfSpecializable match { + case TypeRef(_, GroupOfSpecializable, arg :: Nil) => + arg.typeArgs map (_.typeSymbol) + case _ => + List(tp.typeSymbol) + } + } + case _ => Nil + } } // If we replace `isBoundedGeneric` with (tp <:< AnyRefClass.tpe), // then pos/spec-List.scala fails - why? Does this kind of check fail // for similar reasons? Does `sym.isAbstractType` make a difference? private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = ( - specializedOn(sym).exists(_.symbol == Predef_AnyRef) // specialized on AnyRef + (specializedOn(sym) contains AnyRefModule) && !isValueClass(tp.typeSymbol) && isBoundedGeneric(tp) ) @@ -322,28 +329,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - lazy val primitiveTypes = ScalaValueClasses map (_.tpe) + lazy val specializableTypes = (ScalaValueClasses :+ AnyRefClass) map (_.tpe) sorted + + /** If the symbol is the companion of a value class, the value class. + * Otherwise, AnyRef. + */ + def specializesClass(sym: Symbol): Symbol = { + val c = sym.companionClass + if (isValueClass(c)) c else AnyRefClass + } /** Return the types `sym` should be specialized at. This may be some of the primitive types * or AnyRef. AnyRef means that a new type parameter T will be generated later, known to be a * subtype of AnyRef (T <: AnyRef). * These are in a meaningful order for stability purposes. */ - def concreteTypes(sym: Symbol): List[Type] = ( - if (!isSpecialized(sym)) Nil // no @specialized Annotation - else specializedOn(sym) match { - case Nil => primitiveTypes // specialized on everything - case args => // specialized on args - (args map { tp => - if (tp.symbol == Predef_AnyRef) { - if (isBoundedGeneric(sym.tpe)) - reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".") - AnyRefClass.tpe - } - else tp.symbol.companionClass.tpe - }).sorted - } - ) + def concreteTypes(sym: Symbol): List[Type] = { + val types = ( + if (!isSpecialized(sym)) Nil // no @specialized Annotation + else specializedOn(sym) match { + case Nil => specializableTypes // specialized on everything + case args => args map (s => specializesClass(s).tpe) sorted // specialized on args + } + ) + if (isBoundedGeneric(sym.tpe) && (types contains AnyRefClass)) + reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".") + + types + } /** Return a list of all type environments for all specializations * of @specialized types in `tps`. @@ -502,7 +515,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { typeEnv(sClass) = env this.specializedClass((clazz, env0)) = sClass - val decls1 = new Scope // declarations of the newly specialized class 'sClass' + val decls1 = newScope // declarations of the newly specialized class 'sClass' var oldClassTParams: List[Symbol] = Nil // original unspecialized type parameters var newClassTParams: List[Symbol] = Nil // unspecialized type parameters of 'specializedClass' (cloned) @@ -819,7 +832,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { log("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym) } - sym.resetFlag(FINAL) val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec)) typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s } @@ -1089,7 +1101,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (tparams.nonEmpty) " (poly)" else "", clazz, parents1, phase) ) - val newScope = new Scope(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz)) + val newScope = newScopeWith(specializeClass(clazz, typeEnv(clazz)) ++ specialOverrides(clazz): _*) // If tparams.isEmpty, this is just the ClassInfoType. polyType(tparams, ClassInfoType(parents1, newScope, clazz)) case _ => @@ -1711,8 +1723,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { private def makeArguments(fun: Symbol, vparams: List[Symbol]): List[Tree] = ( //! TODO: make sure the param types are seen from the right prefix - for ((tp, arg) <- fun.info.paramTypes zip vparams) yield - gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe) + map2(fun.info.paramTypes, vparams)((tp, arg) => gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe)) ) private def findSpec(tp: Type): Type = tp match { case TypeRef(pre, sym, _ :: _) => specializedType(tp) @@ -1721,9 +1732,27 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { class SpecializationTransformer(unit: CompilationUnit) extends Transformer { informProgress("specializing " + unit) - override def transform(tree: Tree) = - if (settings.nospecialization.value) tree + override def transform(tree: Tree) = { + val resultTree = if (settings.nospecialization.value) tree else atPhase(phase.next)(specializeCalls(unit).transform(tree)) + + // Remove the final modifier and @inline annotation from anything in the + // original class (since it's being overridden in at least onesubclass). + // + // We do this here so that the specialized subclasses will correctly copy + // final and @inline. + info.foreach { + case (sym, SpecialOverload(target, _)) => { + sym.resetFlag(FINAL) + target.resetFlag(FINAL) + sym.removeAnnotation(ScalaInlineClass) + target.removeAnnotation(ScalaInlineClass) + } + case _ => {} + } + + resultTree + } } def printSpecStats() { diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 56d9658377..ab4a2141a9 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -259,7 +259,7 @@ abstract class UnCurry extends InfoTransform else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe) else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe) - anonClass setInfo ClassInfoType(parents, new Scope, anonClass) + anonClass setInfo ClassInfoType(parents, newScope, anonClass) val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL) applyMethod setInfoAndEnter MethodType(applyMethod newSyntheticValueParams formals, restpe) anonClass addAnnotation serialVersionUIDAnnotation @@ -422,7 +422,7 @@ abstract class UnCurry extends InfoTransform if (tp.typeSymbol.isBottomClass) getManifest(AnyClass.tpe) else if (!manifestOpt.tree.isEmpty) manifestOpt.tree else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi) - else localTyper.getManifestTree(tree.pos, tp, false) + else localTyper.getManifestTree(tree, tp, false) } atPhase(phase.next) { localTyper.typedPos(pos) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 16d55c26ca..18c7635b1e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -23,6 +23,7 @@ trait Analyzer extends AnyRef with Macros with NamesDefaults with TypeDiagnostics + with ContextErrors { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala new file mode 100644 index 0000000000..6ee09d064f --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -0,0 +1,1056 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package typechecker + +import scala.collection.{ mutable, immutable } +import scala.tools.util.StringOps.{ countElementsAsString, countAsString } +import symtab.Flags.{ PRIVATE, PROTECTED } +import scala.tools.util.EditDistance.similarString + +trait ContextErrors { + self: Analyzer => + + import global._ + + object ErrorKinds extends Enumeration { + type ErrorKind = Value + val Normal, Access, Ambiguous, Divergent = Value + } + + import ErrorKinds.ErrorKind + + trait AbsTypeError extends Throwable { + def errPos: Position + def errMsg: String + def kind: ErrorKind + } + + case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + + def errPos:Position = underlyingTree.pos + override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg + } + + case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + + def errPos = underlyingSym.pos + } + + case class TypeErrorWrapper(ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + def errMsg = ex.msg + def errPos = ex.pos + } + + case class TypeErrorWithUnderlyingTree(tree: Tree, ex: TypeError, kind: ErrorKind = ErrorKinds.Normal) + extends AbsTypeError { + def errMsg = ex.msg + def errPos = tree.pos + } + + case class AmbiguousTypeError(underlyingTree: Tree, errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Ambiguous) extends AbsTypeError + + case class PosAndMsgTypeError(errPos: Position, errMsg: String, kind: ErrorKind = ErrorKinds.Normal) extends AbsTypeError + + object ErrorUtils { + def issueNormalTypeError(tree: Tree, msg: String)(implicit context: Context) { + issueTypeError(NormalTypeError(tree, msg)) + } + + def issueSymbolTypeError(sym: Symbol, msg: String)(implicit context: Context) { + issueTypeError(SymbolTypeError(sym, msg)) + } + + def issueDivergentImplicitsError(tree: Tree, msg: String)(implicit context: Context) { + issueTypeError(NormalTypeError(tree, msg, ErrorKinds.Divergent)) + } + + def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { + context.issueAmbiguousError(pre, sym1, sym2, err) + } + + def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } + + def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = { + def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" + "type mismatch" + foundReqMsg(found, req) + missingArgsMsg + } + } + + import ErrorUtils._ + + trait TyperContextErrors { + self: Typer => + + import infer.setError + + object TyperErrorGen { + implicit val context0: Context = infer.getContext + + def UnstableTreeError(tree: Tree) = { + def addendum = { + "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." + } + issueNormalTypeError(tree, + "stable identifier required, but "+tree+" found." + ( + if (isStableExceptVolatile(tree)) addendum else "")) + setError(tree) + } + + def NoImplicitFoundError(tree: Tree, param: Symbol) = { + def errMsg = { + val paramName = param.name + val paramTp = param.tpe + paramTp.typeSymbol match { + case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) + case _ => + "could not find implicit value for "+ + (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type " + else "parameter "+paramName+": ")+paramTp + } + } + issueNormalTypeError(tree, errMsg) + } + + def AdaptTypeError(tree: Tree, found: Type, req: Type) = { + // If the expected type is a refinement type, and the found type is a refinement or an anon + // class, we can greatly improve the error message by retyping the tree to recover the actual + // members present, then display along with the expected members. This is done here because + // this is the last point where we still have access to the original tree, rather than just + // the found/req types. + val foundType: Type = req.normalize match { + case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => + val retyped = typed (tree.duplicate setType null) + val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) + + if (foundDecls.isEmpty) found + else { + // The members arrive marked private, presumably because there was no + // expected type and so they're considered members of an anon class. + foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) + // TODO: if any of the found parents match up with required parents after normalization, + // print the error so that they match. The major beneficiary there would be + // java.lang.Object vs. AnyRef. + refinedType(found.parents, found.typeSymbol.owner, foundDecls, tree.pos) + } + case _ => + found + } + assert(!found.isErroneous && !req.isErroneous, (found, req)) + + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) ) + if (settings.explaintypes.value) + explainTypes(found, req) + } + + def WithFilterError(tree: Tree, ex: AbsTypeError) = { + issueTypeError(ex) + setError(tree) + } + + def ParentTypesError(templ: Template, ex: TypeError) = { + templ.tpe = null + issueNormalTypeError(templ, ex.getMessage()) + } + + // additional parentTypes errors + def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = + issueNormalTypeError(supertpt, "missing type arguments") + + // typedIdent + def AmbiguousIdentError(tree: Tree, name: Name, msg: String) = + NormalTypeError(tree, "reference to " + name + " is ambiguous;\n" + msg) + + def SymbolNotFoundError(tree: Tree, name: Name, owner: Symbol, startingIdentCx: Context) = { + /*** Disabled pending investigation of performance impact. + + // This laborious determination arrived at to keep the tests working. + val calcSimilar = ( + name.length > 2 && ( + startingIdentCx.reportErrors + || startingIdentCx.enclClassOrMethod.reportErrors + ) + ) + // avoid calculating if we're in "silent" mode. + // name length check to limit unhelpful suggestions for e.g. "x" and "b1" + val similar = { + if (!calcSimilar) "" + else { + val allowed = ( + startingIdentCx.enclosingContextChain + flatMap (ctx => ctx.scope.toList ++ ctx.imports.flatMap(_.allImportedSymbols)) + filter (sym => sym.isTerm == name.isTermName) + filterNot (sym => sym.isPackage || sym.isSynthetic || sym.hasMeaninglessName) + ) + val allowedStrings = ( + allowed.map("" + _.name).distinct.sorted + filterNot (s => (s contains '$') || (s contains ' ')) + ) + similarString("" + name, allowedStrings) + } + } + */ + val similar = "" + NormalTypeError(tree, "not found: "+decodeWithKind(name, owner) + similar) + } + + // typedAppliedTypeTree + def AppliedTypeNoParametersError(tree: Tree, errTpe: Type) = { + issueNormalTypeError(tree, errTpe + " does not take type parameters") + setError(tree) + } + + def AppliedTypeWrongNumberOfArgsError(tree: Tree, tpt: Tree, tparams: List[Symbol]) = { + val tptSafeString: String = try { + tpt.tpe.toString() + } catch { + case _: CyclicReference => + tpt.toString() + } + val msg = "wrong number of type arguments for "+tptSafeString+", should be "+tparams.length + issueNormalTypeError(tree, msg) + setError(tree) + } + + // typedTypeDef + def LowerBoundError(tree: TypeDef, lowB: Type, highB: Type) = + issueNormalTypeError(tree, "lower bound "+lowB+" does not conform to upper bound "+highB) + + def HiddenSymbolWithError[T <: Tree](tree: T): T = + setError(tree) + + def SymbolEscapesScopeError[T <: Tree](tree: T, badSymbol: Symbol): T = { + val modifierString = if (badSymbol.isPrivate) "private " else "" + issueNormalTypeError(tree, modifierString + badSymbol + " escapes its defining scope as part of type "+tree.tpe) + setError(tree) + } + + // typedDefDef + def StarParamNotLastError(param: Tree) = + issueNormalTypeError(param, "*-parameter must come last") + + def StarWithDefaultError(meth: Symbol) = + issueSymbolTypeError(meth, "a parameter section with a `*'-parameter is not allowed to have default arguments") + + def InvalidConstructorDefError(ddef: Tree) = + issueNormalTypeError(ddef, "constructor definition not allowed here") + + def DeprecatedParamNameError(param: Symbol, name: Name) = + issueSymbolTypeError(param, "deprecated parameter name "+ name +" has to be distinct from any other parameter name (deprecated or not).") + + // computeParamAliases + def SuperConstrReferenceError(tree: Tree) = + NormalTypeError(tree, "super constructor cannot be passed a self reference unless parameter is declared by-name") + + def SuperConstrArgsThisReferenceError(tree: Tree) = + NormalTypeError(tree, "super constructor arguments cannot reference unconstructed `this`") + + // typedValDef + def VolatileValueError(vdef: Tree) = + issueNormalTypeError(vdef, "values cannot be volatile") + + def FinalVolatileVarError(vdef: Tree) = + issueNormalTypeError(vdef, "final vars cannot be volatile") + + def LocalVarUninitializedError(vdef: Tree) = + issueNormalTypeError(vdef, "local variables must be initialized") + + //typedAssign + def AssignmentError(tree: Tree, varSym: Symbol) = { + issueNormalTypeError(tree, + if (varSym != null && varSym.isValue) "reassignment to val" + else "assignment to non variable") + setError(tree) + } + + def UnexpectedTreeAssignmentConversionError(tree: Tree) = { + issueNormalTypeError(tree, "Unexpected tree during assignment conversion.") + setError(tree) + } + + def MultiDimensionalArrayError(tree: Tree) = { + issueNormalTypeError(tree, "cannot create a generic multi-dimensional array of more than "+ definitions.MaxArrayDims+" dimensions") + setError(tree) + } + + //typedSuper + def MixinMissingParentClassNameError(tree: Tree, mix: Name, clazz: Symbol) = + issueNormalTypeError(tree, mix+" does not name a parent class of "+clazz) + + def AmbiguousParentClassError(tree: Tree) = + issueNormalTypeError(tree, "ambiguous parent class qualifier") + + //typedSelect + def NotAMemberError(sel: Tree, qual: Tree, name: Name) = { + def errMsg = { + val owner = qual.tpe.typeSymbol + val target = qual.tpe.widen + def targetKindString = if (owner.isTypeParameterOrSkolem) "type parameter " else "" + def nameString = decodeWithKind(name, owner) + /** Illuminating some common situations and errors a bit further. */ + def addendum = { + val companion = { + if (name.isTermName && owner.isPackageClass) { + target.member(name.toTypeName) match { + case NoSymbol => "" + case sym => "\nNote: %s exists, but it has no companion object.".format(sym) + } + } + else "" + } + val semicolon = ( + if (linePrecedes(qual, sel)) + "\npossible cause: maybe a semicolon is missing before `"+nameString+"'?" + else + "" + ) + companion + semicolon + } + withAddendum(qual.pos)( + if (name == nme.CONSTRUCTOR) target + " does not have a constructor" + else nameString + " is not a member of " + targetKindString + target + addendum + ) + } + issueNormalTypeError(sel, errMsg) + // the error has to be set for the copied tree, otherwise + // the error remains persistent acros multiple compilations + // and causes problems + //setError(sel) + } + + //typedNew + def IsAbstractError(tree: Tree, sym: Symbol) = { + issueNormalTypeError(tree, sym + " is abstract; cannot be instantiated") + setError(tree) + } + + def DoesNotConformToSelfTypeError(tree: Tree, sym: Symbol, tpe0: Type) = { + issueNormalTypeError(tree, sym + " cannot be instantiated because it does not conform to its self-type " + tpe0) + setError(tree) + } + + //typedEta + def UnderscoreEtaError(tree: Tree) = { + issueNormalTypeError(tree, "_ must follow method; cannot follow " + tree.tpe) + setError(tree) + } + + //typedReturn + def ReturnOutsideOfDefError(tree: Tree) = { + issueNormalTypeError(tree, "return outside method definition") + setError(tree) + } + + def ReturnWithoutTypeError(tree: Tree, owner: Symbol) = { + issueNormalTypeError(tree, owner + " has return statement; needs result type") + setError(tree) + } + + //typedBind + def VariableInPatternAlternativeError(tree: Tree) = { + issueNormalTypeError(tree, "illegal variable in pattern alternative") + //setError(tree) + } + + //typedCase + def StarPositionInPatternError(tree: Tree) = + issueNormalTypeError(tree, "_* may only come last") + + //typedFunction + def MaxFunctionArityError(fun: Tree) = { + issueNormalTypeError(fun, "implementation restricts functions to " + definitions.MaxFunctionArity + " parameters") + setError(fun) + } + + def WrongNumberOfParametersError(tree: Tree, argpts: List[Type]) = { + issueNormalTypeError(tree, "wrong number of parameters; expected = " + argpts.length) + setError(tree) + } + + def MissingParameterTypeError(fun: Tree, vparam: ValDef, pt: Type) = { + def anonMessage = ( + "\nThe argument types of an anonymous function must be fully known. (SLS 8.5)" + + "\nExpected type was: " + pt.toLongString + ) + + val suffix = + if (!vparam.mods.isSynthetic) "" + else " for expanded function" + (fun match { + case Function(_, Match(_, _)) => anonMessage + case _ => " " + fun + }) + + issueNormalTypeError(vparam, "missing parameter type" + suffix) + } + + def ConstructorsOrderError(tree: Tree) = { + issueNormalTypeError(tree, "called constructor's definition must precede calling constructor's definition") + setError(tree) + } + + def OnlyDeclarationsError(tree: Tree) = { + issueNormalTypeError(tree, "only declarations allowed here") + setError(tree) + } + + // typedAnnotation + def AnnotationNotAConstantError(tree: Tree) = + NormalTypeError(tree, "annotation argument needs to be a constant; found: " + tree) + + def AnnotationArgNullError(tree: Tree) = + NormalTypeError(tree, "annotation argument cannot be null") + + def ArrayConstantsError(tree: Tree) = + NormalTypeError(tree, "Array constants have to be specified using the `Array(...)' factory method") + + def ArrayConstantsTypeMismatchError(tree: Tree, pt: Type) = + NormalTypeError(tree, "found array constant, expected argument of type " + pt) + + def UnexpectedTreeAnnotation(tree: Tree) = + NormalTypeError(tree, "unexpected tree in annotation: "+ tree) + + def AnnotationTypeMismatchError(tree: Tree, expected: Type, found: Type) = + NormalTypeError(tree, "expected annotation of type " + expected + ", found " + found) + + def MultipleArgumentListForAnnotationError(tree: Tree) = + NormalTypeError(tree, "multiple argument lists on classfile annotation") + + def UnknownAnnotationNameError(tree: Tree, name: Name) = + NormalTypeError(tree, "unknown annotation argument name: " + name) + + def DuplicateValueAnnotationError(tree: Tree, name: Name) = + NormalTypeError(tree, "duplicate value for annotation argument " + name) + + def ClassfileAnnotationsAsNamedArgsError(tree: Tree) = + NormalTypeError(tree, "classfile annotation arguments have to be supplied as named arguments") + + def AnnotationMissingArgError(tree: Tree, annType: Type, sym: Symbol) = + NormalTypeError(tree, "annotation " + annType.typeSymbol.fullName + " is missing argument " + sym.name) + + def NestedAnnotationError(tree: Tree, annType: Type) = + NormalTypeError(tree, "nested classfile annotations must be defined in java; found: "+ annType) + + def UnexpectedTreeAnnotationError(tree: Tree, unexpected: Tree) = + NormalTypeError(tree, "unexpected tree after typing annotation: "+ unexpected) + + // TODO no test case + //typedExistentialTypeTree + def AbstractionFromVolatileTypeError(vd: ValDef) = + issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + + def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = { + issueNormalTypeError(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + setError(tree) + } + + def TypedApplyDoesNotTakeTpeParametersError(tree: Tree, fun: Tree) = { + issueNormalTypeError(tree, treeSymTypeMsg(fun)+" does not take type parameters.") + setError(tree) + } + + // doTypeApply + //tryNamesDefaults + def WrongNumberOfArgsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun)) + + def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) + + // can it still happen? see test case neg/t960.scala + // TODO no test case + def OverloadedUnapplyError(tree: Tree) = + issueNormalTypeError(tree, "cannot resolve overloaded unapply") + + def UnapplyWithSingleArgError(tree: Tree) = + issueNormalTypeError(tree, "an unapply method must accept a single argument.") + + def MultipleVarargError(tree: Tree) = + NormalTypeError(tree, "when using named arguments, the vararg parameter has to be specified exactly once") + + def ModuleUsingCompanionClassDefaultArgsErrror(tree: Tree) = + NormalTypeError(tree, "module extending its companion class cannot use default constructor arguments") + + def NotEnoughArgsError(tree: Tree, fun0: Tree, missing0: List[Symbol]) = { + def notEnoughArgumentsMsg(fun: Tree, missing: List[Symbol]) = { + val suffix = { + if (missing.isEmpty) "" + else { + val keep = missing take 3 map (_.name) + ".\nUnspecified value parameter%s %s".format( + if (missing.tail.isEmpty) "" else "s", + if (missing drop 3 nonEmpty) (keep :+ "...").mkString(", ") + else keep.mkString("", ", ", ".") + ) + } + } + + "not enough arguments for " + treeSymTypeMsg(fun) + suffix + } + NormalTypeError(tree, notEnoughArgumentsMsg(fun0, missing0)) + } + + //doTypedApply - patternMode + // TODO: missing test case + def TooManyArgsPatternError(fun: Tree) = + NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity) + + def WrongNumberArgsPatternError(tree: Tree, fun: Tree) = + NormalTypeError(tree, "wrong number of arguments for "+treeSymTypeMsg(fun)) + + def ApplyWithoutArgsError(tree: Tree, fun: Tree) = + NormalTypeError(tree, fun.tpe+" does not take parameters") + + //checkClassType + def TypeNotAStablePrefixError(tpt: Tree, pre: Type) = { + issueNormalTypeError(tpt, "type "+pre+" is not a stable prefix") + setError(tpt) + } + + def ClassTypeRequiredError(tree: Tree, found: AnyRef) = { + issueNormalTypeError(tree, "class type required but "+found+" found") + setError(tree) + } + + // validateParentClasses + def ParentSuperSubclassError(parent: Tree, superclazz: Symbol, + parentSym: Symbol, mixin: Symbol) = + NormalTypeError(parent, "illegal inheritance; super"+superclazz+ + "\n is not a subclass of the super"+parentSym+ + "\n of the mixin " + mixin) + + def ParentNotATraitMixinError(parent: Tree, mixin: Symbol) = + NormalTypeError(parent, mixin+" needs to be a trait to be mixed in") + + def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) = + NormalTypeError(parent, "illegal inheritance from final "+mixin) + + def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = + NormalTypeError(parent, "illegal inheritance from sealed " + psym + ": " + context.unit.source.file.canonicalPath + " != " + psym.sourceFile.canonicalPath) + + def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) = + NormalTypeError(parent, + "illegal inheritance;\n self-type "+selfType+" does not conform to "+ + parent +"'s selftype "+parent.tpe.typeOfThis) + + // TODO: missing test case + def ParentInheritedTwiceError(parent: Tree, parentSym: Symbol) = + NormalTypeError(parent, parentSym+" is inherited twice") + + //adapt + def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { + issueNormalTypeError(tree, + "missing arguments for " + meth.fullLocationString + ( + if (meth.isConstructor) "" + else ";\nfollow this method with `_' if you want to treat it as a partially applied function" + )) + setError(tree) + } + + def MissingTypeParametersError(tree: Tree) = { + issueNormalTypeError(tree, tree.symbol+" takes type parameters") + setError(tree) + } + + def KindArityMismatchError(tree: Tree, pt: Type) = { + issueNormalTypeError(tree, + tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+ + ", expected: "+countAsString(pt.typeParams.length)) + setError(tree) + } + + def CaseClassConstructorError(tree: Tree) = { + issueNormalTypeError(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + setError(tree) + } + + //TODO Needs test case + def ConstructorPrefixError(tree: Tree, restpe: Type) = { + issueNormalTypeError(tree, restpe.prefix+" is not a legal prefix for a constructor") + setError(tree) + } + + // SelectFromTypeTree + def TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) = { + issueNormalTypeError(tree, "illegal type selection from volatile type "+qual.tpe) + setError(tree) + } + + // packedType + def InferTypeWithVolatileTypeSelectionError(tree: Tree, pre: Type) = + issueNormalTypeError(tree, "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre) + + def AbstractExistentiallyOverParamerizedTpeError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "can't existentially abstract over parameterized type " + tp) + + //manifestTreee + def MissingManifestError(tree: Tree, full: Boolean, tp: Type) = { + issueNormalTypeError(tree, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp) + setError(tree) + } + + // TODO needs test case + // cases where we do not necessarily return trees + def DependentMethodTpeConversionToFunctionError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "method with dependent type "+tp+" cannot be converted to function value") + + //checkStarPatOK + def StarPatternWithVarargParametersError(tree: Tree) = + issueNormalTypeError(tree, "star patterns must correspond with varargs parameters") + + // TODO missing test case + def FinitaryError(tparam: Symbol) = + issueSymbolTypeError(tparam, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive") + + // TODO missing test case for a second case + def QualifyingClassError(tree: Tree, qual: Name) = { + issueNormalTypeError(tree, + if (qual.isEmpty) tree + " can be used only in a class, object, or template" + else qual + " is not an enclosing class") + setError(tree) + } + + // def stabilize + def NotAValueError(tree: Tree, sym: Symbol) = { + issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value") + setError(tree) + } + + // checkNoDoubleDefs... + def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = + issueSymbolTypeError(sym0, sym1+" is defined twice"+ + {if(!settings.debug.value) "" else " in "+context0.unit}+ + {if (sym0.isMacro && sym1.isMacro) " \n(note that macros cannot be overloaded)" else ""}) + + // cyclic errors + def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0)) + + def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) + + def MacroExpandError(tree: Tree, t: Any) = { + issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) + setError(tree) + } + } + } + + trait InferencerContextErrors { + self: Inferencer => + + private def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = { + def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")") + + def resType = if (pt isWildcard) "" else " with expected result type " + pt + def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt + def locals = alternatives(tree) flatMap (_.typeParams) + + withDisambiguation(locals, allTypes: _*) { + treeSymTypeMsg(tree) + msg + asParams(argtpes) + resType + } + } + + object InferErrorGen { + + implicit val context0 = getContext + + object PolyAlternativeErrorKind extends Enumeration { + type ErrorType = Value + val WrongNumber, NoParams, ArgsDoNotConform = Value + } + + private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) = + if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) { + val methodName = nme.defaultGetterToMethod(sym1.name) + (sym1.enclClass.pos, + "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + + " define default arguments") + } else { + (pos, + ("ambiguous reference to overloaded definition,\n" + + "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + + "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + + "\nmatch " + rest) + ) + } + + def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = { + def errMsg = { + val location = if (sym.isClassConstructor) owner0 else pre.widen + + underlying(sym).fullLocationString + " cannot be accessed in " + + location + explanation + } + NormalTypeError(tree, errMsg, ErrorKinds.Access) + } + + def NoMethodInstanceError(fn: Tree, args: List[Tree], msg: String) = + issueNormalTypeError(fn, + "no type parameters for " + + applyErrorMsg(fn, " exist so that it can be applied to arguments ", args map (_.tpe.widen), WildcardType) + + "\n --- because ---\n" + msg) + + // TODO: no test case + def NoConstructorInstanceError(tree: Tree, restpe: Type, pt: Type, msg: String) = { + issueNormalTypeError(tree, + "constructor of type " + restpe + + " cannot be uniquely instantiated to expected type " + pt + + "\n --- because ---\n" + msg) + setError(tree) + } + + def ConstrInstantiationError(tree: Tree, restpe: Type, pt: Type) = { + issueNormalTypeError(tree, + "constructor cannot be instantiated to expected type" + foundReqMsg(restpe, pt)) + setError(tree) + } + + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = + issueNormalTypeError(tree, + applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) + + def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, + firstCompeting: Symbol, argtpes: List[Type], pt: Type) = { + val msg0 = + "argument types " + argtpes.mkString("(", ",", ")") + + (if (pt == WildcardType) "" else " and expected result type " + pt) + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + } + + def NoBestExprAlternativeError(tree: Tree, pt: Type) = + issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(tree.symbol.tpe, pt, isPossiblyMissingArgs(tree.symbol.tpe, pt)))) + + def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type) = { + val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) + setError(tree) + issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg)) + } + + // checkBounds + def KindBoundErrors(tree: Tree, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) = { + issueNormalTypeError(tree, + prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + + " do not conform to the expected kinds of the type parameters "+ + tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + + kindErrors.toList.mkString("\n", ", ", "")) + } + + def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) = { + if (settings.explaintypes.value) { + val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) + (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) + (targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi)) + () + } + + issueNormalTypeError(tree, + prefix + "type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (_.defString)).mkString("[", ",", "]")) + } + + //substExpr + def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) = + issueNormalTypeError(tree, + "polymorphic expression cannot be instantiated to expected type" + + foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt)) + + //checkCheckable + def TypePatternOrIsInstanceTestError(tree: Tree, tp: Type) = + issueNormalTypeError(tree, "type "+tp+" cannot be used in a type pattern or isInstanceOf test") + + def PatternTypeIncompatibleWithPtError1(tree: Tree, pattp: Type, pt: Type) = + issueNormalTypeError(tree, "pattern type is incompatible with expected type" + foundReqMsg(pattp, pt)) + + def IncompatibleScrutineeTypeError(tree: Tree, pattp: Type, pt: Type) = + issueNormalTypeError(tree, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) + + def PatternTypeIncompatibleWithPtError2(pat: Tree, pt1: Type, pt: Type) = { + def errMsg = { + val sym = pat.tpe.typeSymbol + val clazz = sym.companionClass + val addendum = ( + if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { + // TODO: move these somewhere reusable. + val typeString = clazz.typeParams match { + case Nil => "" + clazz.name + case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") + } + val caseString = ( + clazz.caseFieldAccessors + map (_ => "_") // could use the actual param names here + mkString (clazz.name + "(", ",", ")") + ) + ( + "\nNote: if you intended to match against the class, try `case _: " + + typeString + "` or `case " + caseString + "`" + ) + } + else "" + ) + "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum + } + issueNormalTypeError(pat, errMsg) + } + + def PolyAlternativeError(tree: Tree, argtypes: List[Type], sym: Symbol, err: PolyAlternativeErrorKind.ErrorType) = { + import PolyAlternativeErrorKind._ + val msg = + err match { + case WrongNumber => + "wrong number of type parameters for " + treeSymTypeMsg(tree) + case NoParams => + treeSymTypeMsg(tree) + " does not take type parameters" + case ArgsDoNotConform => + "type arguments " + argtypes.mkString("[", ",", "]") + + " conform to the bounds of none of the overloaded alternatives of\n "+sym+ + ": "+sym.info + } + issueNormalTypeError(tree, msg) + () + } + } + } + + trait NamerContextErrors { + self: Namer => + + object NamerErrorGen { + + implicit val context0 = context + + object SymValidateErrors extends Enumeration { + val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject, + OverrideClass, SealedNonClass, AbstractNonClass, + OverrideConstr, AbstractOverride, LazyAndEarlyInit, + ByNameParameter, AbstractVar = Value + } + + object DuplicatesErrorKinds extends Enumeration { + val RenamedTwice, AppearsTwice = Value + } + + import SymValidateErrors._ + import DuplicatesErrorKinds._ + import symtab.Flags + + def TypeSigError(tree: Tree, ex: TypeError) = { + ex match { + case CyclicReference(sym, info: TypeCompleter) => + issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + case _ => + context0.issue(TypeErrorWithUnderlyingTree(tree, ex)) + } + } + + def GetterDefinedTwiceError(getter: Symbol) = + issueSymbolTypeError(getter, getter+" is defined twice") + + def ValOrValWithSetterSuffixError(tree: Tree) = + issueNormalTypeError(tree, "Names of vals or vars may not end in `_='") + + def PrivateThisCaseClassParameterError(tree: Tree) = + issueNormalTypeError(tree, "private[this] not allowed for case class parameters") + + def BeanPropertyAnnotationLimitationError(tree: Tree) = + issueNormalTypeError(tree, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") + + def BeanPropertyAnnotationFieldWithoutLetterError(tree: Tree) = + issueNormalTypeError(tree, "`BeanProperty' annotation can be applied only to fields that start with a letter") + + def BeanPropertyAnnotationPrivateFieldError(tree: Tree) = + issueNormalTypeError(tree, "`BeanProperty' annotation can be applied only to non-private fields") + + def DoubleDefError(currentSym: Symbol, prevSym: Symbol) = { + val s1 = if (prevSym.isModule) "case class companion " else "" + val s2 = if (prevSym.isSynthetic) "(compiler-generated) " + s1 else "" + val s3 = if (prevSym.isCase) "case class " + prevSym.name else "" + prevSym + + issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3) + } + + def MaxParametersCaseClassError(tree: Tree) = + issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") + + def InheritsItselfError(tree: Tree) = + issueNormalTypeError(tree, tree.tpe.typeSymbol+" inherits itself") + + def MissingParameterOrValTypeError(vparam: Tree) = + issueNormalTypeError(vparam, "missing parameter type") + + def RootImportError(tree: Tree) = + issueNormalTypeError(tree, "_root_ cannot be imported") + + def SymbolValidationError(sym: Symbol, errKind: SymValidateErrors.Value) { + val msg = errKind match { + case ImplicitConstr => + "`implicit' modifier not allowed for constructors" + + case ImplicitNotTerm => + "`implicit' modifier can be used only for values, variables and methods" + + case ImplicitTopObject => + "`implicit' modifier cannot be used for top-level objects" + + case OverrideClass => + "`override' modifier not allowed for classes" + + case SealedNonClass => + "`sealed' modifier can be used only for classes" + + case AbstractNonClass => + "`abstract' modifier can be used only for classes; it should be omitted for abstract members" + + case OverrideConstr => + "`override' modifier not allowed for constructors" + + case AbstractOverride => + "`abstract override' modifier only allowed for members of traits" + + case LazyAndEarlyInit => + "`lazy' definitions may not be initialized early" + + case ByNameParameter => + "pass-by-name arguments not allowed for case class parameters" + + case AbstractVar => + "only classes can have declared but undefined members" + abstractVarMessage(sym) + + } + issueSymbolTypeError(sym, msg) + } + + + def AbstractMemberWithModiferError(sym: Symbol, flag: Int) = + issueSymbolTypeError(sym, "abstract member may not have " + Flags.flagsToString(flag) + " modifier") + + def IllegalModifierCombination(sym: Symbol, flag1: Int, flag2: Int) = + issueSymbolTypeError(sym, "illegal combination of modifiers: %s and %s for: %s".format( + Flags.flagsToString(flag1), Flags.flagsToString(flag2), sym)) + + def IllegalDependentMethTpeError(sym: Symbol)(context: Context) = { + val errorAddendum = + ": parameter appears in the type of another parameter in the same section or an earlier one" + issueSymbolTypeError(sym, "illegal dependent method type" + errorAddendum)(context) + } + + def DuplicatesError(tree: Tree, name: Name, kind: DuplicatesErrorKinds.Value) = { + val msg = kind match { + case RenamedTwice => + "is renamed twice" + case AppearsTwice => + "appears twice as a target of a renaming" + } + + issueNormalTypeError(tree, name.decode + " " + msg) + } + } + } + + trait ImplicitsContextErrors { + self: ImplicitSearch => + + import definitions._ + + def AmbiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo, + pre1: String, pre2: String, trailer: String) + (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { + if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { + val coreMsg = + pre1+" "+info1.sym.fullLocationString+" of type "+info1.tpe+"\n "+ + pre2+" "+info2.sym.fullLocationString+" of type "+info2.tpe+"\n "+ + trailer + val errMsg = + if (isView) { + val found = pt.typeArgs(0) + val req = pt.typeArgs(1) + def defaultExplanation = + "Note that implicit conversions are not applicable because they are ambiguous:\n "+ + coreMsg+"are possible conversion functions from "+ found+" to "+req + + def explanation = { + val sym = found.typeSymbol + // Explain some common situations a bit more clearly. + if (AnyRefClass.tpe <:< req) { + if (sym == AnyClass || sym == UnitClass) { + "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + + "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." + } + else boxedClass get sym match { + case Some(boxed) => + "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + + "methods inherited from Object are rendered ambiguous. This is to avoid\n" + + "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + + "You may wish to use a type ascription: `x: " + boxed.fullName + "`." + case _ => + defaultExplanation + } + } + else defaultExplanation + } + + typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + "\n" + explanation + } else { + "ambiguous implicit values:\n "+coreMsg + "match expected type "+pt + } + context.issueAmbiguousError(AmbiguousTypeError(tree, tree.pos, errMsg)) + } + } + + def DivergingImplicitExpansionError(tree: Tree, pt: Type, sym: Symbol)(implicit context0: Context) = + issueDivergentImplicitsError(tree, + "diverging implicit expansion for type "+pt+"\nstarting with "+ + sym.fullLocationString) + } + + object NamesDefaultsErrorsGen { + import typer.infer.setError + + def NameClashError(sym: Symbol, arg: Tree)(implicit context: Context) = { + setError(arg) // to distinguish it from ambiguous reference error + + def errMsg = + "%s definition needs %s because '%s' is used as a named argument in its body.".format( + "variable", // "method" + "type", // "result type" + sym.name) + issueSymbolTypeError(sym, errMsg) + } + + def AmbiguousReferenceInNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + if (!arg.isErroneous) { // check if name clash wasn't reported already + issueNormalTypeError(arg, + "reference to "+ name +" is ambiguous; it is both a method parameter "+ + "and a variable in scope.") + setError(arg) + } else arg + } + + def UnknownParameterNameNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + issueNormalTypeError(arg, "unknown parameter name: " + name) + setError(arg) + } + + def DoubleParamNamesDefaultError(arg: Tree, name: Name)(implicit context: Context) = { + issueNormalTypeError(arg, "parameter specified twice: "+ name) + setError(arg) + } + + def PositionalAfterNamedNamesDefaultError(arg: Tree)(implicit context: Context) = { + issueNormalTypeError(arg, "positional after named argument.") + setError(arg) + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index faff4ccab2..740acbd10f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package typechecker import symtab.Flags._ -import scala.collection.mutable.ListBuffer +import scala.collection.mutable.{LinkedHashSet, Set} import annotation.tailrec /** @@ -66,8 +66,7 @@ trait Contexts { self: Analyzer => sc.depth += 1 } val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports) - c.reportAmbiguousErrors = !erasedTypes - c.reportGeneralErrors = !erasedTypes + if (erasedTypes) c.setThrowErrors() else c.setReportErrors() c.implicitsEnabled = !erasedTypes c } @@ -83,7 +82,17 @@ trait Contexts { self: Analyzer => } } + private object Errors { + final val ReportErrors = 1 << 0 + final val BufferErrors = 1 << 1 + final val AmbiguousErrors = 1 << 2 + final val notThrowMask = ReportErrors | BufferErrors + final val AllMask = ReportErrors | BufferErrors | AmbiguousErrors + } + class Context private[typechecker] { + import Errors._ + var unit: CompilationUnit = NoCompilationUnit var tree: Tree = _ // Tree associated with this context var owner: Symbol = NoSymbol // The current owner @@ -109,8 +118,6 @@ trait Contexts { self: Analyzer => // (the call to the super or self constructor in the first line of a constructor) // in this context the object's fields should not be in scope - var reportAmbiguousErrors = false - var reportGeneralErrors = false var diagnostic: List[String] = Nil // these messages are printed when issuing an error var implicitsEnabled = false var checking = false @@ -138,12 +145,41 @@ trait Contexts { self: Analyzer => tparams } - def withoutReportingErrors[T](op: => T): T = { - val saved = reportGeneralErrors - reportGeneralErrors = false - try op - finally reportGeneralErrors = saved + private[this] var mode = 0 + private[this] val buffer = LinkedHashSet[AbsTypeError]() + + def errBuffer = buffer + def hasErrors = buffer.nonEmpty + + def state: Int = mode + def restoreState(state0: Int) = mode = state0 + + def reportErrors = (state & ReportErrors) != 0 + def bufferErrors = (state & BufferErrors) != 0 + def ambiguousErrors = (state & AmbiguousErrors) != 0 + def throwErrors = (state & notThrowMask) == 0 + + def setReportErrors() = mode = (ReportErrors | AmbiguousErrors) + def setBufferErrors() = { + assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer) + mode = BufferErrors + } + def setThrowErrors() = mode &= (~AllMask) + def setAmbiguousErrors(report: Boolean) = if (report) mode |= AmbiguousErrors else mode &= notThrowMask + + def updateBuffer(errors: Set[AbsTypeError]) = buffer ++= errors + def condBufferFlush(removeP: AbsTypeError => Boolean) { + val elems = buffer.filter(removeP) + buffer --= elems } + def flushBuffer() { buffer.clear() } + def flushAndReturnBuffer(): Set[AbsTypeError] = { + val current = buffer.clone() + buffer.clear() + current + } + + def logError(err: AbsTypeError) = buffer += err def withImplicitsDisabled[T](op: => T): T = { val saved = implicitsEnabled @@ -183,8 +219,7 @@ trait Contexts { self: Analyzer => c.depth = if (scope == this.scope) this.depth else this.depth + 1 c.imports = imports c.inSelfSuperCall = inSelfSuperCall - c.reportAmbiguousErrors = this.reportAmbiguousErrors - c.reportGeneralErrors = this.reportGeneralErrors + c.restoreState(this.state) c.diagnostic = this.diagnostic c.typingIndentLevel = typingIndentLevel c.implicitsEnabled = this.implicitsEnabled @@ -196,10 +231,10 @@ trait Contexts { self: Analyzer => c } + // TODO: remove? Doesn't seem to be used def make(unit: CompilationUnit): Context = { val c = make(unit, EmptyTree, owner, scope, imports) - c.reportAmbiguousErrors = true - c.reportGeneralErrors = true + c.setReportErrors() c.implicitsEnabled = true c } @@ -218,7 +253,7 @@ trait Contexts { self: Analyzer => make(unit, tree, owner, scope, imports) def makeNewScope(tree: Tree, owner: Symbol): Context = - make(tree, owner, new Scope(scope)) + make(tree, owner, newNestedScope(scope)) // IDE stuff: distinguish between scopes created for typing and scopes created for naming. def make(tree: Tree, owner: Symbol): Context = @@ -229,8 +264,8 @@ trait Contexts { self: Analyzer => def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = { val c = make(newtree) - c.reportGeneralErrors = false - c.reportAmbiguousErrors = reportAmbiguousErrors + c.setBufferErrors() + c.setAmbiguousErrors(reportAmbiguousErrors) c } @@ -242,13 +277,11 @@ trait Contexts { self: Analyzer => def makeConstructorContext = { var baseContext = enclClass.outer - //todo: find out why we need next line while (baseContext.tree.isInstanceOf[Template]) baseContext = baseContext.outer val argContext = baseContext.makeNewScope(tree, owner) argContext.inSelfSuperCall = true - argContext.reportGeneralErrors = this.reportGeneralErrors - argContext.reportAmbiguousErrors = this.reportAmbiguousErrors + argContext.restoreState(this.state) def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { if (e != null && e.owner == c.scope) { @@ -275,41 +308,41 @@ trait Contexts { self: Analyzer => private def unitError(pos: Position, msg: String) = unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) + def issue(err: AbsTypeError) { + if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg)) + else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { + if (ambiguousErrors) { + if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) + unitError(err.errPos, err.errMsg) + } else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + def issueAmbiguousError(err: AbsTypeError) { + if (ambiguousErrors) + unitError(err.errPos, addDiagString(err.errMsg)) + else if (bufferErrors) { buffer += err } + else throw new TypeError(err.errPos, err.errMsg) + } + + // TODO remove def error(pos: Position, err: Throwable) = - if (reportGeneralErrors) unitError(pos, addDiagString(err.getMessage())) + if (reportErrors) unitError(pos, addDiagString(err.getMessage())) else throw err def error(pos: Position, msg: String) = { val msg1 = addDiagString(msg) - if (reportGeneralErrors) unitError(pos, msg1) + if (reportErrors) unitError(pos, msg1) else throw new TypeError(pos, msg1) } - def warning(pos: Position, msg: String) = { - if (reportGeneralErrors) unit.warning(pos, msg) - } - - def ambiguousError(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) { - val (reportPos, msg) = ( - if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) { - val methodName = nme.defaultGetterToMethod(sym1.name) - (sym1.enclClass.pos, - "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + - " define default arguments") - } - else { - (pos, - ("ambiguous reference to overloaded definition,\n" + - "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + - "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + - "\nmatch " + rest) - ) - } - ) - if (reportAmbiguousErrors) { - if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) - unit.error(reportPos, msg) - } else throw new TypeError(pos, msg) + def warning(pos: Position, msg: String): Unit = warning(pos, msg, false) + def warning(pos: Position, msg: String, force: Boolean) { + if (reportErrors || force) unit.warning(pos, msg) } def isLocal(): Boolean = tree match { @@ -343,8 +376,8 @@ trait Contexts { self: Analyzer => def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain - override def toString = "Context(%s@%s unit=%s scope=%s)".format( - owner.fullName, tree.shortClass, unit, scope.## + override def toString = "Context(%s@%s unit=%s scope=%s errors=%b)".format( + owner.fullName, tree.shortClass, unit, scope.##, hasErrors ) /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ @@ -552,7 +585,7 @@ trait Contexts { self: Analyzer => debuglog("collect local implicits " + scope.toList)//DEBUG collectImplicits(scope.toList, NoPrefix) } else if (imports != nextOuter.imports) { - assert(imports.tail == nextOuter.imports) + assert(imports.tail == nextOuter.imports, (imports, nextOuter.imports)) collectImplicitImports(imports.head) } else if (owner.isPackageClass) { // the corresponding package object may contain implicit members. diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 3536608efd..179bea0035 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -7,7 +7,6 @@ package scala.tools.nsc package typechecker import scala.tools.nsc.symtab.Flags - import scala.collection.{ mutable, immutable } /** Duplicate trees and re-type check them, taking care to replace @@ -18,6 +17,7 @@ import scala.collection.{ mutable, immutable } */ abstract class Duplicators extends Analyzer { import global._ + import definitions.{ AnyRefClass, AnyValClass } def retyped(context: Context, tree: Tree): Tree = { resetClassOwners @@ -308,17 +308,26 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1)) */ case Match(scrut, cases) => - val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType) + val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType) val scrutTpe = scrut1.tpe.widen - val cases1 = if (scrutTpe.isFinalType) cases filter { - case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) => - // the typed pattern is not incompatible with the scrutinee type - scrutTpe.matchesPattern(fixType(tpt.tpe)) - case CaseDef(Typed(_, tpt), EmptyTree, body) => - // the typed pattern is not incompatible with the scrutinee type - scrutTpe.matchesPattern(fixType(tpt.tpe)) - case _ => true - } else cases + val cases1 = { + if (scrutTpe.isFinalType) cases filter { + case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) => + // the typed pattern is not incompatible with the scrutinee type + scrutTpe matchesPattern fixType(tpt.tpe) + case CaseDef(Typed(_, tpt), EmptyTree, body) => + // the typed pattern is not incompatible with the scrutinee type + scrutTpe matchesPattern fixType(tpt.tpe) + case _ => true + } + // Without this, AnyRef specializations crash on patterns like + // case _: Boolean => ... + // Not at all sure this is safe. + else if (scrutTpe <:< AnyRefClass.tpe) + cases filterNot (_.pat.tpe <:< AnyValClass.tpe) + else + cases + } super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index e14f0bcd87..3d2f86d54d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -31,20 +31,26 @@ trait Implicits { import typeDebug.{ ptTree, ptBlock, ptLine } import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = + inferImplicit(tree, pt, reportAmbiguous, isView, context, true) + /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` * for more info how the search is conducted. - * @param tree The tree for which the implicit needs to be inserted. - * (the inference might instantiate some of the undetermined - * type parameters of that tree. - * @param pt The expected type of the implicit. - * @param reportAmbiguous Should ambiguous implicit errors be reported? - * False iff we search for a view to find out - * whether one type is coercible to another. - * @param isView We are looking for a view - * @param context The current context - * @return A search result + * @param tree The tree for which the implicit needs to be inserted. + * (the inference might instantiate some of the undetermined + * type parameters of that tree. + * @param pt The expected type of the implicit. + * @param reportAmbiguous Should ambiguous implicit errors be reported? + * False iff we search for a view to find out + * whether one type is coercible to another. + * @param isView We are looking for a view + * @param context The current context + * @param saveAmbiguousDivergent False if any divergent/ambiguous errors should be ignored after + * implicits search, + * true if they should be reported (used in further typechecking). + * @return A search result */ - def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = { + def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = { printInference("[infer %s] %s with pt=%s in %s".format( if (isView) "view" else "implicit", tree, pt, context.owner.enclClass) @@ -64,8 +70,10 @@ trait Implicits { val start = startTimer(implicitNanos) if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) - - val result = new ImplicitSearch(tree, pt, isView, context.makeImplicit(reportAmbiguous)).bestImplicit + val implicitSearchContext = context.makeImplicit(reportAmbiguous) + val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext).bestImplicit + if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) + context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent)) printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains @@ -205,7 +213,7 @@ trait Implicits { /** An extractor for types of the form ? { name: (? >: argtpe <: Any*)restp } */ object HasMethodMatching { - val dummyMethod = new TermSymbol(NoSymbol, NoPosition, newTermName("typer$dummy")) + val dummyMethod = NoSymbol.newTermSymbol(newTermName("typer$dummy")) def templateArgType(argtpe: Type) = new BoundedWildcardType(TypeBounds.lower(argtpe)) def apply(name: Name, argtpes: List[Type], restpe: Type): Type = { @@ -244,7 +252,8 @@ trait Implicits { * @param isView We are looking for a view * @param context0 The context used for the implicit search */ - class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) extends Typer(context0) { + class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context) + extends Typer(context0) with ImplicitsContextErrors { printTyping( ptBlock("new ImplicitSearch", "tree" -> tree, @@ -327,50 +336,6 @@ trait Implicits { incCounter(implicitSearchCount) - /** Issues an error signalling ambiguous implicits */ - private def ambiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo, - pre1: String, pre2: String, trailer: String) = - if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { - val coreMsg = - pre1+" "+info1.sym.fullLocationString+" of type "+info1.tpe+"\n "+ - pre2+" "+info2.sym.fullLocationString+" of type "+info2.tpe+"\n "+ - trailer - error(tree.pos, - if (isView) { - val found = pt.typeArgs(0) - val req = pt.typeArgs(1) - def defaultExplanation = - "Note that implicit conversions are not applicable because they are ambiguous:\n "+ - coreMsg+"are possible conversion functions from "+ found+" to "+req - - def explanation = { - val sym = found.typeSymbol - // Explain some common situations a bit more clearly. - if (AnyRefClass.tpe <:< req) { - if (sym == AnyClass || sym == UnitClass) { - "Note: " + sym.name + " is not implicitly converted to AnyRef. You can safely\n" + - "pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so." - } - else boxedClass get sym match { - case Some(boxed) => - "Note: an implicit exists from " + sym.fullName + " => " + boxed.fullName + ", but\n" + - "methods inherited from Object are rendered ambiguous. This is to avoid\n" + - "a blanket implicit which would convert any " + sym.fullName + " to any AnyRef.\n" + - "You may wish to use a type ascription: `x: " + boxed.fullName + "`." - case _ => - defaultExplanation - } - } - else defaultExplanation - } - - typeErrorMsg(found, req) + "\n" + explanation - } - else { - "ambiguous implicit values:\n "+coreMsg + "match expected type "+pt - }) - } - /** The type parameters to instantiate */ val undetParams = if (isView) List() else context.outer.undetparams @@ -400,9 +365,7 @@ trait Implicits { // println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG if (context.openImplicits.tail.isEmpty) { if (!(pt.isErroneous)) - context.unit.error( - tree.pos, "diverging implicit expansion for type "+pt+"\nstarting with "+ - info.sym.fullLocationString) + DivergingImplicitExpansionError(tree, pt, info.sym)(context) SearchFailure } else { throw DivergentImplicit @@ -578,6 +541,9 @@ trait Implicits { else typed1(itree, EXPRmode, wildPt) + if (context.hasErrors) + return fail("typed implicit %s has errors".format(info.sym.fullLocationString)) + incCounter(typedImplicits) printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) @@ -597,8 +563,8 @@ trait Implicits { } } - if (itree2.tpe.isError) - SearchFailure + if (context.hasErrors) + fail("hasMatchingSymbol reported threw error(s)") else if (!hasMatchingSymbol(itree1)) fail("candidate implicit %s is shadowed by other implicit %s".format( info.sym.fullLocationString, itree1.symbol.fullLocationString)) @@ -620,7 +586,9 @@ trait Implicits { false, lubDepth(List(itree2.tpe, pt))) // #2421: check that we correctly instantiated type parameters outside of the implicit tree: - checkBounds(itree2.pos, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + if (context.hasErrors) + return fail("type parameters weren't correctly instantiated outside of the implicit tree") // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams @@ -646,21 +614,29 @@ trait Implicits { // re-typecheck) // TODO: the return tree is ignored. This seems to make // no difference, but it's bad practice regardless. - itree2 match { + + + val checked = itree2 match { case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c case t => t } - val result = new SearchResult(itree2, subst) - incCounter(foundImplicits) - printInference("[success] found %s for pt %s".format(result, ptInstantiated)) - result + + if (context.hasErrors) + fail("typing TypeApply reported errors for the implicit tree") + else { + val result = new SearchResult(itree2, subst) + incCounter(foundImplicits) + printInference("[success] found %s for pt %s".format(result, ptInstantiated)) + result + } } else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) } } catch { - case ex: TypeError => fail(ex.getMessage()) + case ex: TypeError => + fail(ex.getMessage()) } } @@ -794,7 +770,11 @@ trait Implicits { catch divergenceHandler tryImplicitInfo(i) match { - case SearchFailure => rankImplicits(is, acc) + case SearchFailure => + // We don't want errors that occur during checking implicit info + // to influence the check of further infos. + context.condBufferFlush(_.kind != ErrorKinds.Divergent) + rankImplicits(is, acc) case newBest => best = newBest val newPending = undoLog undo { @@ -829,7 +809,8 @@ trait Implicits { case chosen :: rest => rest find (alt => !improves(chosen, alt)) match { case Some(competing) => - ambiguousImplicitError(chosen, competing, "both", "and", "") + AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context) + return SearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala case _ => if (isView) chosen.useCountView += 1 else chosen.useCountArg += 1 @@ -1182,7 +1163,7 @@ trait Implicits { /* !!! the following is almost right, but we have to splice nested manifest * !!! types into this type. This requires a substantial extension of * !!! reifiers. - val reifier = new liftcode.Reifier() + val reifier = new Reifier() val rtree = reifier.reifyTopLevel(tp1) manifestFactoryCall("apply", tp, rtree) */ @@ -1230,12 +1211,14 @@ trait Implicits { incCounter(inscopeImplicitHits) } if (result == SearchFailure) { + val previousErrs = context.flushAndReturnBuffer() val failstart = startTimer(oftypeFailNanos) val succstart = startTimer(oftypeSucceedNanos) result = implicitManifestOrOfExpectedType(pt) if (result == SearchFailure) { + context.updateBuffer(previousErrs) stopTimer(oftypeFailNanos, failstart) } else { stopTimer(oftypeSucceedNanos, succstart) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 9db291a306..b97fbebec2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -191,12 +191,14 @@ trait Infer { private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) { + class Inferencer(context: Context) extends InferencerContextErrors { + import InferErrorGen._ + /* -- Error Messages --------------------------------------------------- */ def setError[T <: Tree](tree: T): T = { def name = newTermName("<error: " + tree.symbol + ">") - def errorClass = if (context.reportGeneralErrors) context.owner.newErrorClass(name.toTypeName) else stdErrorClass - def errorValue = if (context.reportGeneralErrors) context.owner.newErrorValue(name) else stdErrorValue + def errorClass = if (context.reportErrors) context.owner.newErrorClass(name.toTypeName) else stdErrorClass + def errorValue = if (context.reportErrors) context.owner.newErrorValue(name) else stdErrorValue def errorSym = if (tree.isType) errorClass else errorValue if (tree.hasSymbol) @@ -205,59 +207,12 @@ trait Infer { tree setType ErrorType } - def error(pos: Position, msg: String) { - context.error(pos, msg) - } - - def errorTree(tree: Tree, msg: String): Tree = { - if (!tree.isErroneous) error(tree.pos, msg) - setError(tree) - } - - def typeError(pos: Position, found: Type, req: Type) { - if (!found.isErroneous && !req.isErroneous) { - error(pos, withAddendum(pos)(typeErrorMsg(found, req))) - - if (settings.explaintypes.value) - explainTypes(found, req) - } - } - - def typeErrorMsg(found: Type, req: Type) = { - def isPossiblyMissingArgs = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) - def missingArgsMsg = if (isPossiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else "" - - "type mismatch" + foundReqMsg(found, req) + missingArgsMsg - } - - def typeErrorTree(tree: Tree, found: Type, req: Type): Tree = { - // If the expected type is a refinement type, and the found type is a refinement or an anon - // class, we can greatly improve the error message by retyping the tree to recover the actual - // members present, then display along with the expected members. This is done here because - // this is the last point where we still have access to the original tree, rather than just - // the found/req types. - val foundType: Type = req.normalize match { - case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass => - val retyped = typer typed (tree.duplicate setType null) - val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic) - - if (foundDecls.isEmpty) found - else { - // The members arrive marked private, presumably because there was no - // expected type and so they're considered members of an anon class. - foundDecls foreach (_ resetFlag (PRIVATE | PROTECTED)) - // TODO: if any of the found parents match up with required parents after normalization, - // print the error so that they match. The major beneficiary there would be - // java.lang.Object vs. AnyRef. - refinedType(found.parents, found.typeSymbol.owner, foundDecls, tree.pos) - } - case _ => - found - } - typeError(tree.pos, foundType, req) - setError(tree) - } + def getContext = context + def issue(err: AbsTypeError): Unit = context.issue(err) + + def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req) + def explainTypes(tp1: Type, tp2: Type) = withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) @@ -273,13 +228,12 @@ trait Infer { if (sym.isError) { tree setSymbol sym setType ErrorType } else { - val topClass = context.owner.toplevelClass + val topClass = context.owner.enclosingTopLevelClass if (context.unit.exists) - context.unit.depends += sym.toplevelClass + context.unit.depends += sym.enclosingTopLevelClass var sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) // Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG - if (sym1 == NoSymbol && sym.isJavaDefined && context.unit.isJava) // don't try to second guess Java; see #4402 sym1 = sym @@ -289,7 +243,7 @@ trait Infer { Console.println(tree) Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) } - new AccessError(tree, sym, pre, + ErrorUtils.issueTypeError(AccessError(tree, sym, pre, context.enclClass.owner, if (settings.check.isDefault) analyzer.lastAccessCheckDetails else @@ -303,7 +257,8 @@ trait Infer { "context.owner" -> context.owner, "context.outer.enclClass.owner" -> context.outer.enclClass.owner ) - ) + ))(context) + setError(tree) } else { if (sym1.isTerm) @@ -316,10 +271,11 @@ trait Infer { if (settings.debug.value) ex.printStackTrace val sym2 = underlyingSymbol(sym1) val itype = pre.memberType(sym2) - new AccessError(tree, sym, pre, - "\n because its instance type "+itype+ - (if ("malformed type: "+itype.toString==ex.msg) " is malformed" - else " contains a "+ex.msg)).emit() + ErrorUtils.issueTypeError( + AccessError(tree, sym, pre, context.enclClass.owner, + "\n because its instance type "+itype+ + (if ("malformed type: "+itype.toString==ex.msg) " is malformed" + else " contains a "+ex.msg)))(context) ErrorType } } @@ -684,13 +640,7 @@ trait Infer { case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) case MethodType(params, _) => - val formals0 = params map { param => - param.tpe match { - case TypeRef(_, sym, List(tpe)) if sym isNonBottomSubClass CodeClass => tpe - case tpe => tpe - } - } - val formals = formalTypes(formals0, argtpes0.length) + val formals = formalTypes(params map { _.tpe }, argtpes0.length) def tryTupleApply: Boolean = { // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 @@ -762,25 +712,20 @@ trait Infer { false } - /** Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors). + /** + * Todo: Try to make isApplicable always safe (i.e. not cause TypeErrors). + * The chance of TypeErrors should be reduced through context errors */ private[typechecker] def isApplicableSafe(undetparams: List[Symbol], ftpe: Type, argtpes0: List[Type], pt: Type): Boolean = { - val reportAmbiguousErrors = context.reportAmbiguousErrors - context.reportAmbiguousErrors = false - try { - isApplicable(undetparams, ftpe, argtpes0, pt) - } catch { - case ex: TypeError => - try { - isApplicable(undetparams, ftpe, argtpes0, WildcardType) - } catch { - case ex: TypeError => - false - } - } finally { - context.reportAmbiguousErrors = reportAmbiguousErrors - } + val silentContext = context.makeSilent(false) + val typer0 = newTyper(silentContext) + val res1 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, pt) + if (pt != WildcardType && silentContext.hasErrors) { + silentContext.flushBuffer() + val res2 = typer0.infer.isApplicable(undetparams, ftpe, argtpes0, WildcardType) + if (silentContext.hasErrors) false else res2 + } else res1 } /** Is type <code>ftpe1</code> strictly more specific than type <code>ftpe2</code> @@ -942,38 +887,22 @@ trait Infer { */ /** error if arguments not within bounds. */ - def checkBounds(pos: Position, pre: Type, owner: Symbol, - tparams: List[Symbol], targs: List[Type], prefix: String) = { + def checkBounds(tree: Tree, pre: Type, owner: Symbol, + tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = { //@M validate variances & bounds of targs wrt variances & bounds of tparams //@M TODO: better place to check this? //@M TODO: errors for getters & setters are reported separately val kindErrors = checkKindBounds(tparams, targs, pre, owner) - if (!kindErrors.isEmpty) { - if (targs contains WildcardType) () - else error(pos, - prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + - " do not conform to the expected kinds of the type parameters "+ - tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + - kindErrors.toList.mkString("\n", ", ", "")) - } - else if (!isWithinBounds(pre, owner, tparams, targs)) { + if(!kindErrors.isEmpty) { + if (targs contains WildcardType) true + else { KindBoundErrors(tree, prefix, targs, tparams, kindErrors); false } + } else if (!isWithinBounds(pre, owner, tparams, targs)) { if (!(targs exists (_.isErroneous)) && !(tparams exists (_.isErroneous))) { - //val bounds = instantiatedBounds(pre, owner, tparams, targs)//DEBUG - //println("bounds = "+bounds+", targs = "+targs+", targclasses = "+(targs map (_.getClass))+", parents = "+(targs map (_.parents))) - //println(List.map2(bounds, targs)((bound, targ) => bound containsType targ)) - error(pos, - prefix + "type arguments " + targs.mkString("[", ",", "]") + - " do not conform to " + tparams.head.owner + "'s type parameter bounds " + - (tparams map (_.defString)).mkString("[", ",", "]")) - if (settings.explaintypes.value) { - val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) - (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) - (targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi)) - () - } - } - } + NotWithinBounds(tree, prefix, targs, tparams, kindErrors) + false + } else true + } else true } def checkKindBounds(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): List[String] = { @@ -1055,8 +984,7 @@ trait Infer { targs: List[Type], pt: Type) { if (targs eq null) { if (!tree.tpe.isErroneous && !pt.isErroneous) - error(tree.pos, "polymorphic expression cannot be instantiated to expected type" + - foundReqMsg(polyType(undetparams, skipImplicit(tree.tpe)), pt)) + PolymorphicExpressionInstantiationError(tree, undetparams, pt) } else { new TreeTypeSubstituter(undetparams, targs).traverse(tree) } @@ -1092,27 +1020,24 @@ trait Infer { (okparams map (_.name), okargs).zipped.map(_ + "=" + _).mkString("solved: ", ", ", "") )) - checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ") - val treeSubst = new TreeTypeSubstituter(okparams, okargs) - treeSubst traverseTrees fn :: args + if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) { + val treeSubst = new TreeTypeSubstituter(okparams, okargs) + treeSubst traverseTrees fn :: args - leftUndet match { - case Nil => Nil - case xs => - // #3890 - val xs1 = treeSubst.typeMap mapOver xs - if (xs ne xs1) - new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args + leftUndet match { + case Nil => Nil + case xs => + // #3890 + val xs1 = treeSubst.typeMap mapOver xs + if (xs ne xs1) + new TreeSymSubstTraverser(xs, xs1) traverseTrees fn :: args - xs1 - } + xs1 + } + } else Nil } catch ifNoInstance { msg => - errorTree(fn, "no type parameters for " + - applyErrorMsg(fn, " exist so that it can be applied to arguments ", args map (_.tpe.widen), WildcardType) + - "\n --- because ---\n" + msg - ) - Nil + NoMethodInstanceError(fn, args, msg); List() } } @@ -1137,20 +1062,16 @@ trait Infer { try { val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe), true, lubDepth(List(restpe, pt))) -// checkBounds(tree.pos, NoPrefix, NoSymbol, undetparams, targs, "inferred ") +// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") // no checkBounds here. If we enable it, test bug602 fails. new TreeTypeSubstituter(undetparams, targs).traverse(tree) - } catch { - case ex: NoInstance => - errorTree(tree, "constructor of type " + restpe + - " cannot be uniquely instantiated to expected type " + pt + - "\n --- because ---\n" + ex.getMessage()) + } catch ifNoInstance{ msg => + NoConstructorInstanceError(tree, restpe, pt, msg) } def instError = { if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt) if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt) - errorTree(tree, "constructor cannot be instantiated to expected type" + - foundReqMsg(restpe, pt)) + ConstrInstantiationError(tree, restpe, pt) } if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) { computeArgs @@ -1220,9 +1141,9 @@ trait Infer { } } - def checkCheckable(pos: Position, tp: Type, kind: String) { + def checkCheckable(tree: Tree, tp: Type, kind: String) { def patternWarning(tp0: Type, prefix: String) = { - context.unit.uncheckedWarning(pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") + context.unit.uncheckedWarning(tree.pos, prefix+tp0+" in type "+kind+tp+" is unchecked since it is eliminated by erasure") } def check(tp: Type, bound: List[Symbol]) { def isLocalBinding(sym: Symbol) = @@ -1241,7 +1162,7 @@ trait Infer { } else if (sym.isAliasType) { check(tp.normalize, bound) } else if (sym == NothingClass || sym == NullClass || sym == AnyValClass) { - error(pos, "type "+tp+" cannot be used in a type pattern or isInstanceOf test") + TypePatternOrIsInstanceTestError(tree, tp) } else { for (arg <- args) { if (sym == ArrayClass) check(arg, bound) @@ -1265,11 +1186,12 @@ trait Infer { case ExistentialType(quantified, tp1) => check(tp1, bound ::: quantified) case ThisType(_) => - ; + () case NoPrefix => - ; + () case _ => patternWarning(tp, "type ") + () } } check(tp, List()) @@ -1292,7 +1214,7 @@ trait Infer { } } - def inferTypedPattern(pos: Position, pattp: Type, pt0: Type): Type = { + def inferTypedPattern(tree0: Tree, pattp: Type, pt0: Type): Type = { val pt = widen(pt0) val ptparams = freeTypeParamsOfTerms.collect(pt) val tpparams = freeTypeParamsOfTerms.collect(pattp) @@ -1304,10 +1226,12 @@ trait Infer { * This is the case if the scrutinee has no unresolved type arguments * and is a "final type", meaning final + invariant in all type parameters. */ - if (pt.isFinalType && ptparams.isEmpty && !ptMatchesPattp) - error(pos, "scrutinee is incompatible with pattern type" + foundReqMsg(pattp, pt)) + if (pt.isFinalType && ptparams.isEmpty && !ptMatchesPattp) { + IncompatibleScrutineeTypeError(tree0, pattp, pt) + return ErrorType + } - checkCheckable(pos, pattp, "pattern ") + checkCheckable(tree0, pattp, "pattern ") if (pattp <:< pt) () else { debuglog("free type params (1) = " + tpparams) @@ -1330,8 +1254,8 @@ trait Infer { if (isPopulated(tp, pt1) && isInstantiatable(tvars ++ ptvars) || pattpMatchesPt) ptvars foreach instantiateTypeVar else { - error(pos, "pattern type is incompatible with expected type" + foundReqMsg(pattp, pt)) - return pattp + PatternTypeIncompatibleWithPtError1(tree0, pattp, pt) + return ErrorType } } tvars foreach instantiateTypeVar @@ -1352,30 +1276,8 @@ trait Infer { val pt1 = pt.instantiateTypeParams(ptparams, ptvars) if (pat.tpe <:< pt1) ptvars foreach instantiateTypeVar - else { - val sym = pat.tpe.typeSymbol - val clazz = sym.companionClass - val addendum = ( - if (sym.isModuleClass && clazz.isCaseClass && (clazz isSubClass pt1.typeSymbol)) { - // TODO: move these somewhere reusable. - val typeString = clazz.typeParams match { - case Nil => "" + clazz.name - case xs => xs map (_ => "_") mkString (clazz.name + "[", ",", "]") - } - val caseString = ( - clazz.caseFieldAccessors - map (_ => "_") // could use the actual param names here - mkString (clazz.name + "(", ",", ")") - ) - ( - "\nNote: if you intended to match against the class, try `case _: " + - typeString + "` or `case " + caseString + "`" - ) - } - else "" - ) - error(pat.pos, "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt) + addendum) - } + else + PatternTypeIncompatibleWithPtError2(pat, pt1, pt) } object toOrigin extends TypeMap { @@ -1452,7 +1354,7 @@ trait Infer { * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. */ - def inferExprAlternative(tree: Tree, pt: Type): Unit = tree.tpe match { + def inferExprAlternative(tree: Tree, pt: Type) = tree.tpe match { case OverloadedType(pre, alts) => tryTwice { val alts0 = alts filter (alt => isWeaklyCompatible(pre.memberType(alt), pt)) val secondTry = alts0.isEmpty @@ -1483,15 +1385,10 @@ trait Infer { case _ => } } - typeErrorTree(tree, tree.symbol.tpe, pt) + NoBestExprAlternativeError(tree, pt) } else if (!competing.isEmpty) { - if (secondTry) { - typeErrorTree(tree, tree.symbol.tpe, pt) - } else { - if (!pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt) - setError(tree) - } + if (secondTry) NoBestExprAlternativeError(tree, pt) + else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) } } else { // val applicable = alts1 filter (alt => // global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt)) @@ -1501,9 +1398,11 @@ trait Infer { } } - @inline private def wrapTypeError(expr: => Boolean): Boolean = - try expr - catch { case _: TypeError => false } + @inline private def inSilentMode(expr: Typer => Boolean): Boolean = { + val silentContext = context.makeSilent(context.ambiguousErrors) + val res = expr(newTyper(silentContext)) + if (silentContext.hasErrors) false else res + } // Checks against the name of the parameter and also any @deprecatedName. private def paramMatchesName(param: Symbol, name: Name) = @@ -1573,9 +1472,7 @@ trait Infer { val applicable = resolveOverloadedMethod(argtpes, { alts filter { alt => - // TODO: this will need to be re-written once we substitute throwing exceptions - // with generating error trees. We wrap this applicability in try/catch because of #4457. - wrapTypeError(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && + inSilentMode(typer0 => typer0.infer.isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && (!varArgsOnly || isVarArgsList(alt.tpe.params)) } }) @@ -1591,16 +1488,13 @@ trait Infer { if (improves(alt, best)) alt else best) val competing = applicable.dropWhile(alt => best == alt || improves(best, alt)) if (best == NoSymbol) { - if (pt == WildcardType) { - errorTree(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) - } else { + if (pt == WildcardType) + NoBestMethodAlternativeError(tree, argtpes, pt) + else inferMethodAlternative(tree, undetparams, argtpes, WildcardType) - } } else if (!competing.isEmpty) { if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, - "argument types " + argtpes.mkString("(", ",", ")") + - (if (pt == WildcardType) "" else " and expected result type " + pt)) + AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt) setError(tree) () } else { @@ -1616,18 +1510,27 @@ trait Infer { * * @param infer ... */ - def tryTwice(infer: => Unit) { + def tryTwice(infer: => Unit): Unit = { if (context.implicitsEnabled) { - val reportGeneralErrors = context.reportGeneralErrors - context.reportGeneralErrors = false - try context.withImplicitsDisabled(infer) - catch { - case ex: CyclicReference => throw ex - case ex: TypeError => - context.reportGeneralErrors = reportGeneralErrors + val saved = context.state + var fallback = false + context.setBufferErrors() + val res = try { + context.withImplicitsDisabled(infer) + if (context.hasErrors) { + fallback = true + context.restoreState(saved) + context.flushBuffer() infer + } + } catch { + case ex: CyclicReference => throw ex + case ex: TypeError => // recoverable cyclic references + context.restoreState(saved) + if (!fallback) infer else () } - context.reportGeneralErrors = reportGeneralErrors + context.restoreState(saved) + res } else infer } @@ -1642,13 +1545,13 @@ trait Infer { def inferPolyAlternatives(tree: Tree, argtypes: List[Type]): Unit = { val OverloadedType(pre, alts) = tree.tpe val sym0 = tree.symbol filter (alt => sameLength(alt.typeParams, argtypes)) - def fail(msg: String): Unit = error(tree.pos, msg) + def fail(kind: PolyAlternativeErrorKind.ErrorType) = + PolyAlternativeError(tree, argtypes, sym0, kind) - if (sym0 == NoSymbol) return fail( + if (sym0 == NoSymbol) return ( if (alts exists (_.typeParams.nonEmpty)) - "wrong number of type parameters for " + treeSymTypeMsg(tree) - else treeSymTypeMsg(tree) + " does not take type parameters" - ) + fail(PolyAlternativeErrorKind.WrongNumber) + else fail(PolyAlternativeErrorKind.NoParams)) val (resSym, resTpe) = { if (!sym0.isOverloaded) @@ -1656,11 +1559,8 @@ trait Infer { else { val sym = sym0 filter (alt => isWithinBounds(pre, alt.owner, alt.typeParams, argtypes)) if (sym == NoSymbol) { - if (argtypes forall (x => !x.isErroneous)) fail( - "type arguments " + argtypes.mkString("[", ",", "]") + - " conform to the bounds of none of the overloaded alternatives of\n "+sym0+ - ": "+sym0.info - ) + if (argtypes forall (x => !x.isErroneous)) + fail(PolyAlternativeErrorKind.ArgsDoNotConform) return } else if (sym.isOverloaded) { @@ -1677,24 +1577,6 @@ trait Infer { // Side effects tree with symbol and type tree setSymbol resSym setType resTpe } - - abstract class TreeForwarder(forwardTo: Tree) extends Tree { - override def pos = forwardTo.pos - override def hasSymbol = forwardTo.hasSymbol - override def symbol = forwardTo.symbol - override def symbol_=(x: Symbol) = forwardTo.symbol = x - } - - case class AccessError(tree: Tree, sym: Symbol, pre: Type, explanation: String) extends TreeForwarder(tree) { - setError(this) - - // @PP: It is improbable this logic shouldn't be in use elsewhere as well. - private def location = if (sym.isClassConstructor) context.enclClass.owner else pre.widen - def emit(): Tree = { - val realsym = underlyingSymbol(sym) - errorTree(tree, realsym.fullLocationString + " cannot be accessed in " + location + explanation) - } - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 7f9e56a926..470f3e7117 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -20,50 +20,50 @@ trait Macros { self: Analyzer => macroArgs(fn) :+ args case TypeApply(fn, args) => macroArgs(fn) :+ args - case Select(qual, name) if !isStaticMacro(tree.symbol) => + case Select(qual, name) => List(List(qual)) case _ => List(List()) } - private def isStaticMacro(mac: Symbol): Boolean = - mac.owner.isModuleClass - /** - * The definition of the method implementing a macro. Example: + * The definition of the method implementing a macro. Example: * Say we have in a class C * * def macro foo[T](xs: List[T]): T = expr * * Then the following macro method is generated for `foo`: * - * def defmacro$foo(glob: scala.reflect.api.Universe) - * (_this: glob.Tree) - * (T: glob.Type) - * (xs: glob.Tree): glob.Tree = { - * implicit val $glob = glob + * def defmacro$foo + * (_context: scala.reflect.macro.Context) + * (_this: _context.Tree) + * (T: _context.TypeTree) + * (xs: _context.Tree): _context.Tree = { + * import _context._ // this means that all methods of Context can be used unqualified in macro's body * expr * } * - * If `foo` is declared in an object, the second parameter list is () instead of (_this: glob.Tree). + * If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether). + * + * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug. */ def macroMethDef(mdef: DefDef): Tree = { def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree) val contextType = TypeTree(ReflectMacroContext.tpe) - val globParamSec = List(paramDef(nme.context, contextType)) - def globSelect(name: Name) = Select(Ident(nme.context), name) - def globTree = globSelect(newTypeName("Tree")) - def globType = globSelect(newTypeName("Type")) - val thisParamSec = if (isStaticMacro(mdef.symbol)) List() else List(paramDef(newTermName("_this"), globTree)) - def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globType) + val globParamSec = List(paramDef(nme.macroContext, contextType)) + def globSelect(name: Name) = Select(Ident(nme.macroContext), name) + def globTree = globSelect(tpnme.Tree) + def globTypeTree = globSelect(tpnme.TypeTree) + val thisParamSec = List(paramDef(newTermName(nme.macroThis), globTree)) + def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globTypeTree) def vparamInMacro(vdef: ValDef): ValDef = paramDef(vdef.name, vdef.tpt match { case tpt @ AppliedTypeTree(hk, _) if treeInfo.isRepeatedParamType(tpt) => AppliedTypeTree(hk, List(globTree)) case _ => globTree }) def wrapImplicit(tree: Tree) = atPos(tree.pos) { // implicit hasn't proven useful so far, so I'm disabling it - //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.contextImplicit, SingletonTypeTree(Ident(nme.context)), Ident(nme.context)) - val importGlob = Import(Ident(nme.context), List(ImportSelector(nme.WILDCARD, -1, null, -1))) + //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.macroContextImplicit, SingletonTypeTree(Ident(nme.macroContext)), Ident(nme.macroContext)) + val importGlob = Import(Ident(nme.macroContext), List(ImportSelector(nme.WILDCARD, -1, null, -1))) Block(List(importGlob), tree) } var formals = (mdef.vparamss map (_ map vparamInMacro)) @@ -82,7 +82,7 @@ trait Macros { self: Analyzer => def addMacroMethods(templ: Template, namer: Namer): Unit = { for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) { - val trace = scala.tools.nsc.util.trace when settings.debug.value + val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef))) trace("added to "+namer.context.owner.enclClass+": ")(sym) } @@ -97,24 +97,52 @@ trait Macros { self: Analyzer => override def defaultReflectiveClassLoader() = libraryClassLoader } - class MacroExpandError(val msg: String) extends Exception(msg) - /** Return optionally address of companion object and implementation method symbol - * of given macro; or None if implementation classfile cannot be loaded or does + * of given macro; or None if implementation classfile cannot be loaded or does * not contain the macro implementation. */ def macroImpl(mac: Symbol): Option[(AnyRef, mirror.Symbol)] = { + val debug = settings.Ymacrodebug.value + val trace = scala.tools.nsc.util.trace when debug + trace("looking for macro implementation: ")(mac.fullNameString) + try { val mmeth = macroMeth(mac) + trace("found implementation at: ")(mmeth.fullNameString) + if (mmeth == NoSymbol) None else { - val receiverClass: mirror.Symbol = mirror.classWithName(mmeth.owner.fullName) + val receiverClass: mirror.Symbol = mirror.symbolForName(mmeth.owner.fullName) + if (debug) { + println("receiverClass is: " + receiverClass.fullNameString) + + val jreceiverClass = mirror.classToJava(receiverClass) + val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource + println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource)) + + val jreceiverClasspath = jreceiverClass.getClassLoader match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "<unknown>" + } + println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, jreceiverClasspath)) + } + val receiverObj = receiverClass.companionModule - if (receiverObj == NoSymbol) None + trace("receiverObj is: ")(receiverObj.fullNameString) + + if (receiverObj == mirror.NoSymbol) None else { - val receiver = mirror.getCompanionObject(receiverClass) + val receiver = mirror.companionInstance(receiverClass) val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString)) - Some((receiver, rmeth)) + if (debug) { + println("rmeth is: " + rmeth.fullNameString) + println("jrmeth is: " + mirror.methodToJava(rmeth)) + } + + if (rmeth == mirror.NoSymbol) None + else { + Some((receiver, rmeth)) + } } } } catch { @@ -127,53 +155,85 @@ trait Macros { self: Analyzer => * Or, if that fails, and the macro overrides a method return * tree that calls this method instead of the macro. */ - def macroExpand(tree: Tree): Any = { + def macroExpand(tree: Tree, context: Context): Option[Any] = { + val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value + trace("macroExpand: ")(tree) + val macroDef = tree.symbol macroImpl(macroDef) match { case Some((receiver, rmeth)) => val argss = List(global) :: macroArgs(tree) val paramss = macroMeth(macroDef).paramss + trace("paramss: ")(paramss) val rawArgss = for ((as, ps) <- argss zip paramss) yield { if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) else as } val rawArgs: Seq[Any] = rawArgss.flatten + trace("rawArgs: ")(rawArgs) + val savedInfolevel = nodePrinters.infolevel try { - mirror.invoke(receiver, rmeth, rawArgs: _*) + // @xeno.by: InfoLevel.Verbose examines and prints out infos of symbols + // by the means of this'es these symbols can climb up the lexical scope + // when these symbols will be examined by a node printer + // they will enumerate and analyze their children (ask for infos and tpes) + // if one of those children involves macro expansion, things might get nasty + // that's why I'm temporarily turning this behavior off + nodePrinters.infolevel = nodePrinters.InfoLevel.Quiet + Some(mirror.invoke(receiver, rmeth)(rawArgs: _*)) } catch { case ex => val realex = ReflectionUtils.unwrapThrowable(ex) - val stacktrace = new java.io.StringWriter() - realex.printStackTrace(new java.io.PrintWriter(stacktrace)) - val msg = System.getProperty("line.separator") + stacktrace - throw new MacroExpandError("exception during macro expansion: " + msg) + val msg = if (settings.Ymacrodebug.value) { + val stacktrace = new java.io.StringWriter() + realex.printStackTrace(new java.io.PrintWriter(stacktrace)) + System.getProperty("line.separator") + stacktrace + } else { + realex.getMessage + } + context.unit.error(tree.pos, "exception during macro expansion: " + msg) + None + } finally { + nodePrinters.infolevel = savedInfolevel } case None => - val trace = scala.tools.nsc.util.trace when settings.debug.value - def notFound() = throw new MacroExpandError("macro implementation not found: " + macroDef.name) - def fallBackToOverridden(tree: Tree): Tree = { + def notFound() = { + context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) + None + } + def fallBackToOverridden(tree: Tree): Option[Tree] = { tree match { case Select(qual, name) if (macroDef.isMacro) => macroDef.allOverriddenSymbols match { - case first :: others => - return Select(qual, name) setPos tree.pos setSymbol first - case _ => + case first :: _ => + Some(Select(qual, name) setPos tree.pos setSymbol first) + case _ => trace("macro is not overridden: ")(tree) notFound() } case Apply(fn, args) => - Apply(fallBackToOverridden(fn), args) setPos tree.pos + fallBackToOverridden(fn) match { + case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos) + case _ => None + } case TypeApply(fn, args) => - TypeApply(fallBackToOverridden(fn), args) setPos tree.pos + fallBackToOverridden(fn) match { + case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos) + case _ => None + } case _ => trace("unexpected tree in fallback: ")(tree) notFound() } } - val tree1 = fallBackToOverridden(tree) - trace("falling back to ")(tree1) - currentRun.macroExpansionFailed = true - tree1 + fallBackToOverridden(tree) match { + case Some(tree1) => + trace("falling back to ")(tree1) + currentRun.macroExpansionFailed = true + Some(tree1) + case None => + None + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 29dffd99d6..915d7a98db 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -17,6 +17,139 @@ trait MethodSynthesis { import global._ import definitions._ + import CODE._ + + object synthesisUtil { + type M[T] = Manifest[T] + type CM[T] = ClassManifest[T] + + def ValOrDefDef(sym: Symbol, body: Tree) = + if (sym.isLazy) ValDef(sym, body) + else DefDef(sym, body) + + def applyTypeInternal(manifests: List[M[_]]): Type = { + val symbols = manifests map manifestToSymbol + val container :: args = symbols + val tparams = container.typeConstructor.typeParams + + // Conservative at present - if manifests were more usable this could do a lot more. + require(symbols forall (_ ne NoSymbol), "Must find all manifests: " + symbols) + require(container.owner.isPackageClass, "Container must be a top-level class in a package: " + container) + require(tparams.size == args.size, "Arguments must match type constructor arity: " + tparams + ", " + args) + + typeRef(container.typeConstructor.prefix, container, args map (_.tpe)) + } + + def companionType[T](implicit m: M[T]) = + getRequiredModule(m.erasure.getName).tpe + + // Use these like `applyType[List, Int]` or `applyType[Map, Int, String]` + def applyType[CC](implicit m1: M[CC]): Type = + applyTypeInternal(List(m1)) + + def applyType[CC[X1], X1](implicit m1: M[CC[_]], m2: M[X1]): Type = + applyTypeInternal(List(m1, m2)) + + def applyType[CC[X1, X2], X1, X2](implicit m1: M[CC[_,_]], m2: M[X1], m3: M[X2]): Type = + applyTypeInternal(List(m1, m2, m3)) + + def applyType[CC[X1, X2, X3], X1, X2, X3](implicit m1: M[CC[_,_,_]], m2: M[X1], m3: M[X2], m4: M[X3]): Type = + applyTypeInternal(List(m1, m2, m3, m4)) + + def newMethodType[F](owner: Symbol)(implicit m: Manifest[F]): Type = { + val fnSymbol = manifestToSymbol(m) + assert(fnSymbol isSubClass FunctionClass(m.typeArguments.size - 1), (owner, m)) + val symbols = m.typeArguments map (m => manifestToSymbol(m)) + val formals = symbols.init map (_.typeConstructor) + val params = owner newSyntheticValueParams formals + + MethodType(params, symbols.last.typeConstructor) + } + } + import synthesisUtil._ + + class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { + private def isOverride(name: TermName) = + clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) + + def newMethodFlags(name: TermName) = { + val overrideFlag = if (isOverride(name)) OVERRIDE else 0L + overrideFlag | SYNTHETIC + } + def newMethodFlags(method: Symbol) = { + val overrideFlag = if (isOverride(method.name)) OVERRIDE else 0L + (method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED + } + + private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = + logResult("finishMethod")(localTyper typed ValOrDefDef(method, f(method))) + + private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { + val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + finishMethod(m setInfoAndEnter info, f) + } + private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = { + val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name)) + finishMethod(m setInfoAndEnter infoFn(m), f) + } + private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { + val m = original.cloneSymbol(clazz, newMethodFlags(original)) setPos clazz.pos.focus + m.name = name + finishMethod(clazz.info.decls enter m, f) + } + + private def cloneInternal(original: Symbol, f: Symbol => Tree): Tree = + cloneInternal(original, f, original.name) + + def clazzMember(name: Name) = clazz.info nonPrivateMember name + def typeInClazz(sym: Symbol) = clazz.thisType memberType sym + + /** Function argument takes the newly created method symbol of + * the same type as `name` in clazz, and returns the tree to be + * added to the template. + */ + def overrideMethod(name: Name)(f: Symbol => Tree): Tree = + overrideMethod(clazzMember(name))(f) + + def overrideMethod(original: Symbol)(f: Symbol => Tree): Tree = + cloneInternal(original, sym => f(sym setFlag OVERRIDE)) + + def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree = + cloneInternal(original, f, nameFn(original.name)) + + def createMethod(name: Name, paramTypes: List[Type], returnType: Type)(f: Symbol => Tree): Tree = + createInternal(name, f, (m: Symbol) => MethodType(m newSyntheticValueParams paramTypes, returnType)) + + def createMethod(name: Name, returnType: Type)(f: Symbol => Tree): Tree = + createInternal(name, f, NullaryMethodType(returnType)) + + def createMethod(original: Symbol)(f: Symbol => Tree): Tree = + createInternal(original.name, f, original.info) + + def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree = + createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident))) + + def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = { + createMethod(name, List(IntClass.tpe), returnType) { m => + val arg0 = Ident(m.firstParam) + val default = DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg0) + val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default + + Match(arg0, cases) + } + } + + // def foo() = constant + def constantMethod(name: Name, value: Any): Tree = { + val constant = Constant(value) + createMethod(name, Nil, constant.tpe)(_ => Literal(constant)) + } + // def foo = constant + def constantNullary(name: Name, value: Any): Tree = { + val constant = Constant(value) + createMethod(name, constant.tpe)(_ => Literal(constant)) + } + } /** There are two key methods in here. * @@ -32,10 +165,12 @@ trait MethodSynthesis { trait MethodSynth { self: Namer => + import NamerErrorGen._ + def enterGetterSetter(tree: ValDef) { val ValDef(mods, name, _, _) = tree if (nme.isSetterName(name)) - context.error(tree.pos, "Names of vals or vars may not end in `_='") + ValOrValWithSetterSuffixError(tree) val getter = Getter(tree).createAndEnterSymbol() @@ -43,7 +178,7 @@ trait MethodSynthesis { if (mods.isLazy) enterLazyVal(tree, getter) else { if (mods.isPrivateLocal) - context.error(tree.pos, "private[this] not allowed for case class parameters") + PrivateThisCaseClassParameterError(tree) // Create the setter if necessary. if (mods.isMutable) Setter(tree).createAndEnterSymbol() @@ -187,26 +322,39 @@ trait MethodSynthesis { override def validate() { assert(derivedSym != NoSymbol, tree) if (derivedSym.isOverloaded) - context.error(derivedSym.pos, derivedSym+" is defined twice") + GetterDefinedTwiceError(derivedSym) super.validate() } - // keep type tree of original abstract field - private def fixTypeTree(dd: DefDef): DefDef = { - dd.tpt match { - case tt: TypeTree if dd.rhs == EmptyTree => - tt setOriginal tree.tpt - case tpt => - tpt setPos tree.tpt.pos.focus - } - dd - } override def derivedTree: DefDef = { - fixTypeTree { - DefDef(derivedSym, - if (mods.isDeferred) EmptyTree - else gen.mkCheckInit(fieldSelection) - ) + // For existentials, don't specify a type for the getter, even one derived + // from the symbol! This leads to incompatible existentials for the field and + // the getter. Let the typer do all the work. You might think "why only for + // existentials, why not always," and you would be right, except: a single test + // fails, but it looked like some work to deal with it. Test neg/t0606.scala + // starts compiling (instead of failing like it's supposed to) because the typer + // expects to be able to identify escaping locals in typedDefDef, and fails to + // spot that brand of them. In other words it's an artifact of the implementation. + val tpt = derivedSym.tpe.finalResultType match { + case ExistentialType(_, _) => TypeTree() + case tp => TypeTree(tp) + } + tpt setPos focusPos(derivedSym.pos) + // keep type tree of original abstract field + if (mods.isDeferred) + tpt setOriginal tree.tpt + + // TODO - reconcile this with the DefDef creator in Trees (which + // at this writing presented no way to pass a tree in for tpt.) + atPos(derivedSym.pos) { + DefDef( + Modifiers(derivedSym.flags), + derivedSym.name.toTermName, + Nil, + Nil, + tpt, + if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection) + ) setSymbol derivedSym } } } @@ -255,8 +403,7 @@ trait MethodSynthesis { if (derivedSym == NoSymbol) { // the namer decides whether to generate these symbols or not. at that point, we don't // have symbolic information yet, so we only look for annotations named "BeanProperty". - context.error(tree.pos, - "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") + BeanPropertyAnnotationLimitationError(tree) } super.validate() } @@ -304,9 +451,9 @@ trait MethodSynthesis { val beans = beanAccessorsFromNames(tree) if (beans.nonEmpty) { if (!name(0).isLetter) - context.error(tree.pos, "`BeanProperty' annotation can be applied only to fields that start with a letter") + BeanPropertyAnnotationFieldWithoutLetterError(tree) else if (mods.isPrivate) // avoids name clashes with private fields in traits - context.error(tree.pos, "`BeanProperty' annotation can be applied only to non-private fields") + BeanPropertyAnnotationPrivateFieldError(tree) // Create and enter the symbols here, add the trees in finishGetterSetter. beans foreach (_.createAndEnterSymbol()) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index e04d89047b..51542ec757 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -73,7 +73,9 @@ trait Namers extends MethodSynthesis { classAndNamerOfModule.clear() } - abstract class Namer(val context: Context) extends MethodSynth { + abstract class Namer(val context: Context) extends MethodSynth with NamerContextErrors { + + import NamerErrorGen._ val typer = newTyper(context) private lazy val innerNamer = @@ -87,7 +89,7 @@ trait Namers extends MethodSynthesis { newNamer(context.makeNewScope(tree, sym)) } def createInnerNamer() = { - newNamer(context.make(context.tree, owner, new Scope)) + newNamer(context.make(context.tree, owner, newScope)) } def createPrimaryConstructorParameterNamer: Namer = { //todo: can we merge this with SCCmode? val classContext = context.enclClass @@ -109,9 +111,10 @@ trait Namers extends MethodSynthesis { protected def owner = context.owner private def contextFile = context.unit.source.file - private def typeErrorHandler[T](pos: Position, alt: T): PartialFunction[Throwable, T] = { + private def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = { case ex: TypeError => - typer.reportTypeError(pos, ex) + // H@ need to ensure that we handle only cyclic references + TypeSigError(tree, ex) alt } // PRIVATE | LOCAL are fields generated for primary constructor arguments @@ -129,10 +132,17 @@ trait Namers extends MethodSynthesis { || vd.symbol.isLazy ) - def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = { + def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = if (sym.isPrivateLocal || !mods.hasAccessBoundary) sym - else sym setPrivateWithin typer.qualifyingClass(tree, mods.privateWithin, true) - } + else sym setPrivateWithin ( + typer.qualifyingClass(tree, mods.privateWithin, true) match { + case None => + NoSymbol + case Some(sym) => + sym + } + ) + def setPrivateWithin(tree: MemberDef, sym: Symbol): Symbol = setPrivateWithin(tree, sym, tree.mods) @@ -195,14 +205,6 @@ trait Namers extends MethodSynthesis { ) ) - private def doubleDefError(pos: Position, sym: Symbol) { - val s1 = if (sym.isModule) "case class companion " else "" - val s2 = if (sym.isSynthetic) "(compiler-generated) " + s1 else "" - val s3 = if (sym.isCase) "case class " + sym.name else "" + sym - - context.error(pos, sym.name + " is already defined as " + s2 + s3) - } - private def allowsOverload(sym: Symbol) = ( sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass ) @@ -221,7 +223,7 @@ trait Namers extends MethodSynthesis { if (!allowsOverload(sym)) { val prev = scope.lookupEntry(sym.name) if ((prev ne null) && prev.owner == scope && conflict(sym, prev.sym)) { - doubleDefError(sym.pos, prev.sym) + DoubleDefError(sym, prev.sym) sym setInfo ErrorType scope unlink prev.sym // let them co-exist... // FIXME: The comment "let them co-exist" is confusing given that the @@ -250,7 +252,7 @@ trait Namers extends MethodSynthesis { returnContext } tree.symbol match { - case NoSymbol => try dispatch() catch typeErrorHandler(tree.pos, this.context) + case NoSymbol => try dispatch() catch typeErrorHandler(tree, this.context) case sym => enterExistingSym(sym) } } @@ -280,7 +282,7 @@ trait Namers extends MethodSynthesis { } private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { - log("[+symbol] " + sym.hasFlagsToString(-1L) + " " + sym) + log("[+symbol] " + sym.debugLocationString) tree.symbol = sym sym } @@ -447,6 +449,7 @@ trait Namers extends MethodSynthesis { } private def checkSelectors(tree: Import): Unit = { + import DuplicatesErrorKinds._ val Import(expr, selectors) = tree val base = expr.tpe @@ -483,8 +486,10 @@ trait Namers extends MethodSynthesis { typeSig(tree) } // for Java code importing Scala objects - else if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) - notAMemberError(tree.pos, expr, from) + else if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + typer.TyperErrorGen.NotAMemberError(tree, expr, from) + typer.infer.setError(tree) + } } // Setting the position at the import means that if there is // more than one hidden name, the second will not be warned. @@ -492,20 +497,21 @@ trait Namers extends MethodSynthesis { checkNotRedundant(tree.pos withPoint fromPos, from, to) } } - def noDuplicates(names: List[Name], message: String) { + + def noDuplicates(names: List[Name], check: DuplicatesErrorKinds.Value) { def loop(xs: List[Name]): Unit = xs match { case Nil => () case hd :: tl => if (hd == nme.WILDCARD || !(tl contains hd)) loop(tl) - else context.error(tree.pos, hd.decode + " " + message) + else DuplicatesError(tree, hd, check) } loop(names filterNot (x => x == null || x == nme.WILDCARD)) } selectors foreach checkSelector // checks on the whole set - noDuplicates(selectors map (_.name), "is renamed twice") - noDuplicates(selectors map (_.rename), "appears twice as a target of a renaming") + noDuplicates(selectors map (_.name), RenamedTwice) + noDuplicates(selectors map (_.rename), AppearsTwice) } def enterCopyMethodOrGetter(tree: Tree, tparams: List[TypeDef]): Symbol = { @@ -620,7 +626,7 @@ trait Namers extends MethodSynthesis { if (mods.isCase) { if (treeInfo.firstConstructorArgs(impl.body).size > MaxFunctionArity) - context.error(tree.pos, "Implementation restriction: case classes cannot have more than " + MaxFunctionArity + " parameters.") + MaxParametersCaseClassError(tree) val m = ensureCompanionObject(tree, caseModuleDef) classOfModuleClass(m.moduleClass) = new WeakReference(tree) @@ -823,7 +829,7 @@ trait Namers extends MethodSynthesis { val tp = tpt.tpe val inheritsSelf = tp.typeSymbol == owner if (inheritsSelf) - context.error(tpt.pos, ""+tp.typeSymbol+" inherits itself") + InheritsItselfError(tpt) if (inheritsSelf || tp.isError) AnyRefClass.tpe else tp @@ -832,7 +838,7 @@ trait Namers extends MethodSynthesis { val parents = typer.parentTypes(templ) map checkParent enterSelf(templ.self) - val decls = new Scope + val decls = newScope val templateNamer = newNamer(context.make(templ, clazz, decls)) templateNamer enterSyms templ.body @@ -918,7 +924,7 @@ trait Namers extends MethodSynthesis { } def thisMethodType(restpe: Type) = { - val checkDependencies = new DependentTypeChecker(context) + val checkDependencies = new DependentTypeChecker(context)(this) checkDependencies check vparamSymss // DEPMETTODO: check not needed when they become on by default checkDependencies(restpe) @@ -994,7 +1000,7 @@ trait Namers extends MethodSynthesis { } mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { - context.error(vparam.pos, "missing parameter type") + MissingParameterOrValTypeError(vparam) vparam.tpt defineType ErrorType } } @@ -1262,7 +1268,7 @@ trait Namers extends MethodSynthesis { val typer1 = typer.constrTyperIf(isBeforeSupercall) if (tpt.isEmpty) { if (rhs.isEmpty) { - context.error(tpt.pos, "missing parameter type"); + MissingParameterOrValTypeError(tpt) ErrorType } else assignTypeToTree(vdef, newTyper(typer1.context.make(vdef, sym)), WildcardType) @@ -1276,7 +1282,7 @@ trait Namers extends MethodSynthesis { val expr1 = typer.typedQualifier(expr) typer checkStable expr1 if (expr1.symbol != null && expr1.symbol.isRootPackage) - context.error(tree.pos, "_root_ cannot be imported") + RootImportError(tree) val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] checkSelectors(newImport) @@ -1290,7 +1296,7 @@ trait Namers extends MethodSynthesis { val result = try getSig - catch typeErrorHandler(tree.pos, ErrorType) + catch typeErrorHandler(tree, ErrorType) result match { case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => typer.deskolemizeTypeParams(tparams)(result) @@ -1337,43 +1343,43 @@ trait Namers extends MethodSynthesis { * - declarations only in mixins or abstract classes (when not @native) */ def validate(sym: Symbol) { - def fail(msg: String) = context.error(sym.pos, msg) + import SymValidateErrors._ + def fail(kind: SymValidateErrors.Value) = SymbolValidationError(sym, kind) + def checkWithDeferred(flag: Int) { if (sym hasFlag flag) - fail("abstract member may not have " + flagsToString(flag) + " modifier") + AbstractMemberWithModiferError(sym, flag) } def checkNoConflict(flag1: Int, flag2: Int) { if (sym hasAllFlags flag1 | flag2) - fail("illegal combination of modifiers: %s and %s for: %s".format( - flagsToString(flag1), flagsToString(flag2), sym)) + IllegalModifierCombination(sym, flag1, flag2) } if (sym.isImplicit) { if (sym.isConstructor) - fail("`implicit' modifier not allowed for constructors") + fail(ImplicitConstr) if (!sym.isTerm) - fail("`implicit' modifier can be used only for values, variables and methods") + fail(ImplicitNotTerm) if (sym.owner.isPackageClass) - fail("`implicit' modifier cannot be used for top-level objects") + fail(ImplicitTopObject) } if (sym.isClass) { if (sym.isAnyOverride && !sym.hasFlag(TRAIT)) - fail("`override' modifier not allowed for classes") - } - else { + fail(OverrideClass) + } else { if (sym.isSealed) - fail("`sealed' modifier can be used only for classes") + fail(SealedNonClass) if (sym.hasFlag(ABSTRACT)) - fail("`abstract' modifier can be used only for classes; it should be omitted for abstract members") + fail(AbstractNonClass) } if (sym.isConstructor && sym.isAnyOverride) - fail("`override' modifier not allowed for constructors") + fail(OverrideConstr) if (sym.isAbstractOverride && !sym.owner.isTrait) - fail("`abstract override' modifier only allowed for members of traits") + fail(AbstractOverride) if (sym.isLazy && sym.hasFlag(PRESUPER)) - fail("`lazy' definitions may not be initialized early") + fail(LazyAndEarlyInit) if (sym.info.typeSymbol == FunctionClass(0) && sym.isValueParameter && sym.owner.isCaseClass) - fail("pass-by-name arguments not allowed for case class parameters") + fail(ByNameParameter) if (sym.isDeferred) { // Is this symbol type always allowed the deferred flag? @@ -1391,7 +1397,7 @@ trait Namers extends MethodSynthesis { if (sym hasAnnotation NativeAttr) sym resetFlag DEFERRED else if (!symbolAllowsDeferred && ownerRequiresConcrete) - fail("only classes can have declared but undefined members" + abstractVarMessage(sym)) + fail(AbstractVar) checkWithDeferred(PRIVATE) checkWithDeferred(FINAL) @@ -1456,14 +1462,14 @@ trait Namers extends MethodSynthesis { // def foo[T, T2](a: T, x: T2)(implicit w: ComputeT2[T, T2]) // moreover, the latter is not an encoding of the former, which hides type // inference of T2, so you can specify T while T2 is purely computed - private class DependentTypeChecker(ctx: Context) extends TypeTraverser { + private class DependentTypeChecker(ctx: Context)(namer: Namer) extends TypeTraverser { private[this] val okParams = mutable.Set[Symbol]() private[this] val method = ctx.owner def traverse(tp: Type) = tp match { case SingleType(_, sym) => if (sym.owner == method && sym.isValueParameter && !okParams(sym)) - ctx.error(sym.pos, "illegal dependent method type" + errorAddendum) + namer.NamerErrorGen.IllegalDependentMethTpeError(sym)(ctx) case _ => mapOver(tp) } @@ -1476,8 +1482,6 @@ trait Namers extends MethodSynthesis { okParams ++= vps } } - private def errorAddendum = - ": parameter appears in the type of another parameter in the same section or an earlier one" } @deprecated("Use underlyingSymbol instead", "2.10.0") @@ -1506,7 +1510,7 @@ trait Namers extends MethodSynthesis { } catch { case e: InvalidCompanions => - ctx.error(original.pos, e.getMessage) + ctx.unit.error(original.pos, e.getMessage) NoSymbol } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 79cb211215..3a3c244d1c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -17,6 +17,7 @@ trait NamesDefaults { self: Analyzer => import global._ import definitions._ + import NamesDefaultsErrorsGen._ val defaultParametersOfMethod = perRunCaches.newWeakMap[Symbol, Set[Symbol]]() withDefaultValue Set() @@ -312,8 +313,7 @@ trait NamesDefaults { self: Analyzer => // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) - - if (typedApp.tpe.isError) setError(tree) + if (typedApp.isErrorTyped) tree else typedApp match { // Extract the typed arguments, restore the call-site evaluation order (using // ValDef's in the block), change the arguments to these local values. @@ -384,6 +384,7 @@ trait NamesDefaults { self: Analyzer => if (missing forall (_.hasDefaultFlag)) { val defaultArgs = missing flatMap (p => { val defGetter = defaultGetter(p, context) + // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 else { var default1 = qual match { @@ -434,12 +435,12 @@ trait NamesDefaults { self: Analyzer => private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = { val savedParams = context.extractUndetparams() - val savedReporting = context.reportAmbiguousErrors + val savedReporting = context.ambiguousErrors - context.reportAmbiguousErrors = false + context.setAmbiguousErrors(false) try fn(savedParams) finally { - context.reportAmbiguousErrors = savedReporting + context.setAmbiguousErrors(savedReporting) //@M note that we don't get here when an ambiguity was detected (during the computation of res), // as errorTree throws an exception context.undetparams = savedParams @@ -488,7 +489,7 @@ trait NamesDefaults { self: Analyzer => // is called, and EmptyTree can only be typed NoType. Thus we need to // disable conforms as a view... try typer.silent(_.typed(arg, subst(paramtpe))) match { - case t: Tree => !t.isErroneous + case SilentResultValue(t) => !t.isErroneous // #4041 case _ => false } catch { @@ -496,9 +497,7 @@ trait NamesDefaults { self: Analyzer => // CyclicReferences. Fix for #3685 case cr @ CyclicReference(sym, _) => (sym.name == param.name) && sym.accessedOrSelf.isVariable && { - context.error(sym.pos, - "variable definition needs type because '%s' is used as a named argument in its body.".format(sym.name)) - typer.infer.setError(arg) + NameClashError(sym, arg)(typer.context) true } } @@ -514,18 +513,17 @@ trait NamesDefaults { self: Analyzer => * after named ones. */ def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = { - import typer.context + implicit val context0 = typer.context // maps indices from (order written by user) to (order of definition) val argPos = Array.fill(args.length)(-1) var positionalAllowed = true val namelessArgs = mapWithIndex(args) { (arg, index) => - def fail(msg: String) = typer.infer.errorTree(arg, msg) arg match { case arg @ AssignOrNamedArg(Ident(name), rhs) => def matchesName(param: Symbol) = !param.isSynthetic && ( (param.name == name) || (param.deprecatedParamName match { case Some(`name`) => - context.unit.deprecationWarning(arg.pos, + context0.unit.deprecationWarning(arg.pos, "the parameter name "+ name +" has been deprecated. Use "+ param.name +" instead.") true case _ => false @@ -539,12 +537,12 @@ trait NamesDefaults { self: Analyzer => // treat the arg as an assignment of type Unit Assign(arg.lhs, rhs) setPos arg.pos } - else fail("unknown parameter name: " + name) + else UnknownParameterNameNamesDefaultError(arg, name) } else if (argPos contains pos) - fail("parameter specified twice: " + name) + DoubleParamNamesDefaultError(arg, name) else if (isAmbiguousAssignment(typer, params(pos), arg)) - fail("reference to " + name + " is ambiguous; it is both a method parameter and a variable in scope.") + AmbiguousReferenceInNamesDefaultError(arg, name) else { // if the named argument is on the original parameter // position, positional after named is allowed. @@ -556,7 +554,7 @@ trait NamesDefaults { self: Analyzer => case _ => argPos(index) = index if (positionalAllowed) arg - else fail("positional after named argument.") + else PositionalAfterNamedNamesDefaultError(arg) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index ed185c27d6..6d31243fd0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -7,8 +7,7 @@ package scala.tools.nsc package typechecker import symtab._ -import Flags.{ CASE => _, _ } - +import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC} /** Translate pattern matching into method calls (these methods form a zero-plus monad), similar in spirit to how for-comprehensions are compiled. * @@ -16,29 +15,16 @@ import Flags.{ CASE => _, _ } * (lifting the body of the case into the monad using `one`). * * Cases are combined into a pattern match using the `orElse` combinator (the implicit failure case is expressed using the monad's `zero`). - * - * The monad `M` in which the pattern match is interpreted is determined by solving `implicitly[MatchingStrategy[M]]` for M. - * Predef provides the default, `OptionMatching` - - * Example translation: TODO - - scrut match { case Person(father@Person(_, fatherName), name) if fatherName == name => } - scrut match { case Person(father, name) => father match {case Person(_, fatherName) => }} - Person.unapply(scrut) >> ((father, name) => (Person.unapply(father) >> (_, fatherName) => check(fatherName == name) >> (_ => body))) - - (a => (Person.unapply(a).>>( - b => Person.unapply(b._1).>>( - c => check(c._2 == b._2).>>( - d => body)))))(scrut) - -TODO: - - optimizer loops on virtpatmat compiler? - - - don't orElse a failure case at the end if there's a default case - - implement spec more closely (see TODO's below) - - fix inlining of methods in nested objects + * TODO: + * - interaction with CPS + * - Array patterns + * - implement spec more closely (see TODO's) + * - DCE + * - use manifests for type testing + * * (longer-term) TODO: + * - user-defined unapplyProd * - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?) * - recover exhaustivity and unreachability checking using a variation on the type-safe builder pattern */ @@ -46,26 +32,90 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => import global._ import definitions._ - class MatchTranslator(typer: Typer) extends MatchCodeGen { - def typed(tree: Tree, mode: Int, pt: Type): Tree = typer.typed(tree, mode, pt) // for MatchCodeGen -- imports don't provide implementations for abstract members + object vpmName { + val one = newTermName("one") + val drop = newTermName("drop") + val flatMap = newTermName("flatMap") + val get = newTermName("get") + val guard = newTermName("guard") + val isEmpty = newTermName("isEmpty") + val orElse = newTermName("orElse") + val outer = newTermName("<outer>") + val runOrElse = newTermName("runOrElse") + val zero = newTermName("zero") + val __match = newTermName("__match") + + def counted(str: String, i: Int) = newTermName(str+i) + } + + object MatchTranslator { + def apply(typer: Typer): MatchTranslation = { + import typer._ + // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer + newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { + case SilentResultValue(ms) => new PureMatchTranslator(typer, ms) + case _ => new OptimizingMatchTranslator(typer) + } + } + } - import typer._ - import typeDebug.{ ptTree, ptBlock, ptLine } + class PureMatchTranslator(val typer: Typer, val matchStrategy: Tree) extends MatchTranslation with TreeMakers with PureCodegen + class OptimizingMatchTranslator(val typer: Typer) extends MatchTranslation with TreeMakers with MatchOptimizations - def solveContextBound(contextBoundTp: Type): (Tree, Type) = { - val solSym = NoSymbol.newTypeParameter(newTypeName("SolveImplicit$")) - val param = solSym.setInfo(contextBoundTp.typeSymbol.typeParams(0).info.cloneInfo(solSym)) // TypeBounds(NothingClass.typeConstructor, baseTp) - val pt = appliedType(contextBoundTp, List(param.tpeHK)) - val savedUndets = context.undetparams +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// talking to userland +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - context.undetparams = param :: context.undetparams - val result = inferImplicit(EmptyTree, pt, false, false, context) - context.undetparams = savedUndets + /** Interface with user-defined match monad? + * if there's a `__match` in scope, we use this as the match strategy, assuming it conforms to MatchStrategy as defined below: + + type Matcher[P[_], M[+_], A] = { + def flatMap[B](f: P[A] => M[B]): M[B] + def orElse[B >: A](alternative: => M[B]): M[B] + } + + abstract class MatchStrategy[P[_], M[+_]] { + // runs the matcher on the given input + def runOrElse[T, U](in: P[T])(matcher: P[T] => M[U]): P[U] + + def zero: M[Nothing] + def one[T](x: P[T]): M[T] + def guard[T](cond: P[Boolean], then: => P[T]): M[T] + def isSuccess[T, U](x: P[T])(f: P[T] => M[U]): P[Boolean] // used for isDefinedAt + } + + * P and M are derived from one's signature (`def one[T](x: P[T]): M[T]`) + + + * if no `__match` is found, we assume the following implementation (and generate optimized code accordingly) + + object __match extends MatchStrategy[({type Id[x] = x})#Id, Option] { + def zero = None + def one[T](x: T) = Some(x) + // NOTE: guard's return type must be of the shape M[T], where M is the monad in which the pattern match should be interpreted + def guard[T](cond: Boolean, then: => T): Option[T] = if(cond) Some(then) else None + def runOrElse[T, U](x: T)(f: T => Option[U]): U = f(x) getOrElse (throw new MatchError(x)) + def isSuccess[T, U](x: T)(f: T => Option[U]): Boolean = !f(x).isEmpty + } + + */ + trait MatchMonadInterface { + val typer: Typer + val matchOwner = typer.context.owner + + def inMatchMonad(tp: Type): Type + def pureType(tp: Type): Type + final def matchMonadResult(tp: Type): Type = + tp.baseType(matchMonadSym).typeArgs match { + case arg :: Nil => arg + case _ => ErrorType + } - (result.tree, result.subst.to(result.subst.from indexOf param)) - } + protected def matchMonadSym: Symbol + } - lazy val (matchingStrategy, matchingMonadType) = solveContextBound(MatchingStrategyClass.typeConstructor) + trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => + import typer.{typed, context, silent, reallyExists} /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. @@ -75,20 +125,25 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * * NOTE: the resulting tree is not type checked, nor are nested pattern matches transformed * thus, you must typecheck the result (and that will in turn translate nested matches) - * this could probably optimized... (but note that the matchingStrategy must be solved for each nested patternmatch) + * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch) */ def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type): Tree = { // we don't transform after typers // (that would require much more sophistication when generating trees, // and the only place that emits Matches after typers is for exception handling anyway) - assert(phase.id <= currentRun.typerPhase.id) + assert(phase.id <= currentRun.typerPhase.id, phase) + + def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) + case _ => tp + } val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) - val scrutSym = freshSym(scrut.pos, scrutType) + val scrutSym = freshSym(scrut.pos, pureType(scrutType)) val okPt = repeatedToSeq(pt) // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, context.owner) + combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner) } @@ -139,6 +194,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // must use type `tp`, which is provided by extractor's result, not the type expected by binder, // as b.info may be based on a Typed type ascription, which has not been taken into account yet by the translation // (it will later result in a type test when `tp` is not a subtype of `b.info`) + // TODO: can we simplify this, together with the Bound case? (extractor.subPatBinders, extractor.subPatTypes).zipped foreach { case (b, tp) => b setInfo tp } // println("changing "+ b +" : "+ b.info +" -> "+ tp); // println("translateExtractorPattern checking parameter type: "+ (patBinder, patBinder.info.widen, extractor.paramType, patBinder.info.widen <:< extractor.paramType)) @@ -215,12 +271,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => and it binds the variable name to that value. **/ case Bound(subpatBinder, p) => - // TreeMaker with empty list of trees only performs the substitution subpatBinder --> patBinder - // println("rebind "+ subpatBinder +" to "+ patBinder) - withSubPats(List(SubstOnlyTreeMaker(Substitution(subpatBinder, CODE.REF(patBinder)))), - // the symbols are markers that may be used to refer to the result of the extractor in which the corresponding tree is nested - // it's the responsibility of the treemaker to replace this symbol by a reference that - // selects that result on the function symbol of the flatMap call that binds to the result of this extractor + // replace subpatBinder by patBinder (as if the Bind was not there) + withSubPats(List(SubstOnlyTreeMaker(subpatBinder, patBinder)), // must be patBinder, as subpatBinder has the wrong info: even if the bind assumes a better type, this is not guaranteed until we cast (patBinder, p) ) @@ -266,7 +318,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => if (guard == EmptyTree) Nil else List(GuardTreeMaker(guard)) - // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by pmgen.one), + // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by codegen.one), // so that user can generate failure when needed -- use implicit conversion to lift into monad on-demand? // to enable this, probably need to move away from Option to a monad specific to pattern-match, // so that we can return Option's from a match without ambiguity whether this indicates failure in the monad, or just some result in the monad @@ -315,7 +367,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val extractorCall = try { context.undetparams = Nil silent(_.typed(Apply(Select(orig, extractor), List(Ident(nme.SELECTOR_DUMMY) setType fun.tpe.finalResultType)), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case extractorCall: Tree => extractorCall // if !extractorCall.containsError() + case SilentResultValue(extractorCall) => extractorCall // if !extractorCall.containsError() case _ => // this fails to resolve overloading properly... // Apply(typedOperator(Select(orig, extractor)), List(Ident(nme.SELECTOR_DUMMY))) // no need to set the type of the dummy arg, it will be replaced anyway @@ -379,34 +431,32 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => protected lazy val expectedLength = lastIndexingBinder - firstIndexingBinder + 1 protected lazy val minLenToCheck = if(lastIsStar) 1 else 0 protected def seqTree(binder: Symbol) = tupleSel(binder)(firstIndexingBinder+1) - protected def tupleSel(binder: Symbol)(i: Int): Tree = pmgen.tupleSel(binder)(i) + protected def tupleSel(binder: Symbol)(i: Int): Tree = codegen.tupleSel(binder)(i) // the trees that select the subpatterns on the extractor's result, referenced by `binder` // require isSeq protected def subPatRefsSeq(binder: Symbol): List[Tree] = { - // only relevant if isSeq: (here to avoid capturing too much in the returned closure) - val indexingIndices = (0 to (lastIndexingBinder-firstIndexingBinder)) - val nbIndexingIndices = indexingIndices.length + val indexingIndices = (0 to (lastIndexingBinder-firstIndexingBinder)) + val nbIndexingIndices = indexingIndices.length - // this error is checked by checkStarPatOK - // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats)) + // this error-condition has already been checked by checkStarPatOK: + // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats)) // there are `firstIndexingBinder` non-seq tuple elements preceding the Seq (((1 to firstIndexingBinder) map tupleSel(binder)) ++ // then we have to index the binder that represents the sequence for the remaining subpatterns, except for... - (indexingIndices map pmgen.index(seqTree(binder))) ++ + (indexingIndices map codegen.index(seqTree(binder))) ++ // the last one -- if the last subpattern is a sequence wildcard: drop the prefix (indexed by the refs on the line above), return the remainder (if(!lastIsStar) Nil else List( if(nbIndexingIndices == 0) seqTree(binder) - else pmgen.drop(seqTree(binder))(nbIndexingIndices)))).toList + else codegen.drop(seqTree(binder))(nbIndexingIndices)))).toList } // the trees that select the subpatterns on the extractor's result, referenced by `binder` // require (nbSubPats > 0 && (!lastIsStar || isSeq)) - protected def subPatRefs(binder: Symbol): List[Tree] = { + protected def subPatRefs(binder: Symbol): List[Tree] = if (nbSubPats == 0) Nil else if (isSeq) subPatRefsSeq(binder) else ((1 to nbSubPats) map tupleSel(binder)).toList - } protected def lengthGuard(binder: Symbol): Option[Tree] = // no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied @@ -427,7 +477,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => } } - // TODO: to be called when there's a def unapplyProd(x: T): Product_N + // TODO: to be called when there's a def unapplyProd(x: T): U + // U must have N members _1,..., _N -- the _i are type checked, call their type Ti, + // // for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it) class ExtractorCallProd(fun: Tree, args: List[Tree]) extends ExtractorCall(args) { // TODO: fix the illegal type bound in pos/t602 -- type inference messes up before we get here: @@ -439,15 +491,15 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // private val extractorTp = if (wellKinded(fun.tpe)) fun.tpe else existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType) // println("ExtractorCallProd: "+ (fun.tpe, existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType))) // println("ExtractorCallProd: "+ (fun.tpe, args map (_.tpe))) - private def extractorTp = fun.tpe + private def constructorTp = fun.tpe def isTyped = fun.isTyped // to which type should the previous binder be casted? - def paramType = extractorTp.finalResultType + def paramType = constructorTp.finalResultType def isSeq: Boolean = rawSubPatTypes.nonEmpty && isRepeatedParamType(rawSubPatTypes.last) - protected def rawSubPatTypes = extractorTp.paramTypes + protected def rawSubPatTypes = constructorTp.paramTypes // binder has type paramType def treeMaker(binder: Symbol, pos: Position): TreeMaker = { @@ -456,31 +508,20 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => } /* TODO: remove special case when the following bug is fixed -scala> :paste -// Entering paste mode (ctrl-D to finish) - class Foo(x: Other) { x._1 } // BUG: can't refer to _1 if its defining class has not been type checked yet case class Other(y: String) - -// Exiting paste mode, now interpreting. - -<console>:8: error: value _1 is not a member of Other - class Foo(x: Other) { x._1 } - ^ - -scala> case class Other(y: String) -defined class Other - -scala> class Foo(x: Other) { x._1 } -defined class Foo */ +-- this is ok: +case class Other(y: String) +class Foo(x: Other) { x._1 } // no error in this order +*/ override protected def tupleSel(binder: Symbol)(i: Int): Tree = { import CODE._ // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component val caseAccs = binder.info.typeSymbol.caseFieldAccessors if (caseAccs isDefinedAt (i-1)) REF(binder) DOT caseAccs(i-1) - else pmgen.tupleSel(binder)(i) + else codegen.tupleSel(binder)(i) } - override def toString(): String = "case class "+ (if (extractorTp eq null) fun else paramType.typeSymbol) +" with arguments "+ args + override def toString(): String = "case class "+ (if (constructorTp eq null) fun else paramType.typeSymbol) +" with arguments "+ args } class ExtractorCallRegular(extractorCallIncludingDummy: Tree, args: List[Tree]) extends ExtractorCall(args) { @@ -495,7 +536,7 @@ defined class Foo */ def treeMaker(patBinderOrCasted: Symbol, pos: Position): TreeMaker = { // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) - val binder = freshSym(pos, resultInMonad) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type + val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder, Substitution(subPatBinders, subPatRefs(binder)))(resultType.typeSymbol == BooleanClass) } @@ -524,12 +565,7 @@ defined class Foo */ // turn an extractor's result type into something `monadTypeToSubPatTypesAndRefs` understands protected lazy val resultInMonad: Type = if(!hasLength(tpe.paramTypes, 1)) ErrorType else { if (resultType.typeSymbol == BooleanClass) UnitClass.tpe - else { - val monadArgs = resultType.baseType(matchingMonadType.typeSymbol).typeArgs - // assert(monadArgs.length == 1, "unhandled extractor type: "+ extractorTp) // TODO: overloaded unapply?? - if(monadArgs.length == 1) monadArgs(0) - else ErrorType - } + else matchMonadResult(resultType) } protected lazy val rawSubPatTypes = @@ -543,81 +579,6 @@ defined class Foo */ override def toString() = extractorCall +": "+ extractorCall.tpe +" (symbol= "+ extractorCall.symbol +")." } - // tack an outer test onto `cond` if binder.info and expectedType warrant it - def maybeWithOuterCheck(binder: Symbol, expectedTp: Type)(cond: Tree): Tree = { import CODE._ - if ( !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass) - && needsOuterTest(expectedTp, binder.info, context.owner)) { - val expectedPrefix = expectedTp.prefix match { - case ThisType(clazz) => THIS(clazz) - case pre => REF(pre.prefix, pre.termSymbol) - } - - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC - val outerCheck = (Select(pmgen._asInstanceOf(binder, expectedTp), outer)) OBJ_EQ expectedPrefix - - // first check cond, since that should ensure we're not selecting outer on null - pmgen.and(cond, outerCheck) - } - else - cond - } - - // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest) - def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt) - def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(pmgen._isInstanceOf(binder, pt)) - - /** Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms: - - A reference to a class C, p.C, or T#C. - This type pattern matches any non-null instance of the given class. - Note that the prefix of the class, if it is given, is relevant for determining class instances. - For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix. - The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case. - - - A singleton type p.type. - This type pattern matches only the value denoted by the path p - (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now - // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time" - - - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern. - This type pattern matches all values that are matched by each of the type patterns Ti. - - - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _. - This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards. - The bounds or alias type of these type variable are determined as described in (§8.3). - - - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO - This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1. - **/ - - // generate the tree for the run-time test that follows from the fact that - // a `scrut` of known type `scrutTp` is expected to have type `expectedTp` - // uses maybeWithOuterCheck to check the type's prefix - def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree = { import CODE._ - // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` - // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") - def genEqualsAndInstanceOf(sym: Symbol): Tree - = pmgen._equals(REF(sym), patBinder) AND pmgen._isInstanceOf(patBinder, pt.widen) - - def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe - - val patBinderTp = patBinder.info.widen - def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt) - - // TODO: [SPEC] type test for Array - // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest) - pt match { - case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here - case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil - case ThisType(sym) => REF(patBinder) OBJ_EQ This(sym) - case ConstantType(Constant(null)) if isRefTp(patBinderTp) => REF(patBinder) OBJ_EQ NULL - case ConstantType(const) => pmgen._equals(Literal(const), patBinder) - case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL) - case _ => typeTest(patBinder, pt) - } - } - /** A conservative approximation of which patterns do not discern anything. * They are discarded during the translation. */ @@ -643,14 +604,74 @@ defined class Foo */ } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// substitution +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + trait TypedSubstitution extends MatchMonadInterface { + object Substitution { + def apply(from: Symbol, to: Tree) = new Substitution(List(from), List(to)) + // requires sameLength(from, to) + def apply(from: List[Symbol], to: List[Tree]) = + if (from nonEmpty) new Substitution(from, to) else EmptySubstitution + } + + class Substitution(val from: List[Symbol], val to: List[Tree]) { + // We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed, + // and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees. + def apply(tree: Tree): Tree = { + // according to -Ystatistics 10% of translateMatch's time is spent in this method... + // since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst + if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree + else (new Transformer { + @inline private def typedIfOrigTyped(to: Tree, origTp: Type): Tree = + if (origTp == null || origTp == NoType) to + // important: only type when actually substing and when original tree was typed + // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors) + else typer.typed(to, EXPRmode, WildcardType) + + override def transform(tree: Tree): Tree = { + def subst(from: List[Symbol], to: List[Tree]): Tree = + if (from.isEmpty) tree + else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate, tree.tpe) + else subst(from.tail, to.tail) + + tree match { + case Ident(_) => subst(from, to) + case _ => super.transform(tree) + } + } + }).transform(tree) + } + + + // the substitution that chains `other` before `this` substitution + // forall t: Tree. this(other(t)) == (this >> other)(t) + def >>(other: Substitution): Substitution = { + val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f) } + new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly + } + override def toString = (from zip to) mkString("Substitution(", ", ", ")") + } + + object EmptySubstitution extends Substitution(Nil, Nil) { + override def apply(tree: Tree): Tree = tree + override def >>(other: Substitution): Substitution = other + } + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // the making of the trees /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + trait TreeMakers extends TypedSubstitution { self: CodegenCore => + def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = + (cases, Nil) - trait TreeMakers { - def inMatchMonad(tp: Type): Type = appliedType(matchingMonadType, List(tp)) - lazy val optimizingCodeGen = matchingMonadType.typeSymbol eq OptionClass + def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = + None abstract class TreeMaker { + /** captures the scope and the value of the bindings in patterns + * important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed) + */ def substitution: Substitution = if (currSub eq null) localSubstitution else currSub @@ -668,7 +689,6 @@ defined class Foo */ // build Tree that chains `next` after the current extractor def chainBefore(next: Tree, pt: Type): Tree - def treesToHoist: List[Tree] = Nil } case class TrivialTreeMaker(tree: Tree) extends TreeMaker { @@ -679,71 +699,30 @@ defined class Foo */ case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker { val localSubstitution: Substitution = EmptySubstitution def chainBefore(next: Tree, pt: Type): Tree = // assert(next eq EmptyTree) - atPos(body.pos)(substitution(pmgen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here + atPos(body.pos)(substitution(codegen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here } - case class SubstOnlyTreeMaker(localSubstitution: Substitution) extends TreeMaker { + case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { + val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) def chainBefore(next: Tree, pt: Type): Tree = substitution(next) } abstract class FunTreeMaker extends TreeMaker { val nextBinder: Symbol - - // for CSE (used iff optimizingCodeGen) - // TODO: factor this out -- don't mutate treemakers - var reused: Boolean = false - def reusedBinders: List[Symbol] = Nil - override def treesToHoist: List[Tree] = { import CODE._ - reusedBinders map { b => VAL(b) === pmgen.mkZero(b.info) } - } } - abstract class FreshFunTreeMaker extends FunTreeMaker { + abstract class CondTreeMaker extends FunTreeMaker { val pos: Position val prevBinder: Symbol val nextBinderTp: Type - lazy val nextBinder = freshSym(pos, nextBinderTp) - lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) - } - - // TODO: factor out optimization-specific stuff into codegen - abstract class CondTreeMaker extends FreshFunTreeMaker { import CODE._ val cond: Tree val res: Tree - // for CSE (used iff optimizingCodeGen) - // must set reused before! - override lazy val reusedBinders = if(reused) List(freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE, nextBinder setFlag MUTABLE) else Nil - def storedCond = reusedBinders(0) - def storedRes = reusedBinders(1) + lazy val nextBinder = freshSym(pos, nextBinderTp) + lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) def chainBefore(next: Tree, pt: Type): Tree = - if (!reused) - atPos(pos)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) - else { // for CSE (used iff optimizingCodeGen) - IF (cond) THEN BLOCK( - storedCond === TRUE, - storedRes === res, - substitution(next).duplicate // TODO: finer-grained dup'ing - ) ELSE pmgen.zero - } - } - - // for CSE (used iff optimizingCodeGen) - case class ReusingCondTreeMaker(dropped_priors: List[(TreeMaker, Option[TreeMaker])]) extends TreeMaker { import CODE._ - lazy val localSubstitution = { - val (from, to) = dropped_priors.collect {case (dropped: CondTreeMaker, Some(prior: CondTreeMaker)) => (dropped.nextBinder, REF(prior.storedRes))}.unzip - val oldSubs = dropped_priors.collect {case (dropped: TreeMaker, _) => dropped.substitution} - oldSubs.foldLeft(Substitution(from, to))(_ >> _) - } - - def chainBefore(next: Tree, pt: Type): Tree = { - val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: CondTreeMaker)) => ctm}.get.storedCond) - - IF (cond) THEN BLOCK( - substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) - ) ELSE pmgen.zero - } + atPos(pos)(codegen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) } /** @@ -754,12 +733,13 @@ defined class Foo */ * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { - def chainBefore(next: Tree, pt: Type): Tree = atPos(extractor.pos)( - if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext(next))) - else pmgen.flatMap(extractor, pmgen.fun(nextBinder, substitution(condAndNext(next)))) - ) - - private def condAndNext(next: Tree): Tree = extraCond map (pmgen.condOptimized(_, next)) getOrElse next + def chainBefore(next: Tree, pt: Type): Tree = { + val condAndNext = extraCond map (codegen.ifThenElseZero(_, next)) getOrElse next + atPos(extractor.pos)( + if (extractorReturnsBoolean) codegen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) + else codegen.flatMap(extractor, nextBinder, substitution(condAndNext)) + ) + } override def toString = "X"+(extractor, nextBinder) } @@ -768,21 +748,42 @@ defined class Foo */ case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._ def chainBefore(next: Tree, pt: Type): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL - val cond = extraCond match { - case None => nullCheck - case Some(c) => nullCheck AND c - } - pmgen.condOptimized(cond, substitution(next)) + val cond = extraCond map (nullCheck AND _) getOrElse nullCheck + codegen.ifThenElseZero(cond, substitution(next)) } override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution) } + // tack an outer test onto `cond` if binder.info and expectedType warrant it + def maybeWithOuterCheck(binder: Symbol, expectedTp: Type)(cond: Tree): Tree = { import CODE._ + if ( !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass) + && needsOuterTest(expectedTp, binder.info, matchOwner)) { + val expectedPrefix = expectedTp.prefix match { + case ThisType(clazz) => THIS(clazz) + case pre => REF(pre.prefix, pre.termSymbol) + } + + // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` + // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC + val outerCheck = (Select(codegen._asInstanceOf(binder, expectedTp), outer)) OBJ_EQ expectedPrefix + + // first check cond, since that should ensure we're not selecting outer on null + codegen.and(cond, outerCheck) + } + else + cond + } + + // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest) + def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt) + private def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._isInstanceOf(binder, pt)) // need to substitute since binder may be used outside of the next extractor call (say, in the body of the case) case class TypeTestTreeMaker(prevBinder: Symbol, nextBinderTp: Type, pos: Position) extends CondTreeMaker { val cond = typeTest(prevBinder, nextBinderTp) - val res = pmgen._asInstanceOf(prevBinder, nextBinderTp) + val res = codegen._asInstanceOf(prevBinder, nextBinderTp) override def toString = "TT"+(prevBinder, nextBinderTp) } @@ -790,8 +791,58 @@ defined class Foo */ case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker { val nextBinderTp = glb(List(patBinder.info.widen, pt)) + /** Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms: + - A reference to a class C, p.C, or T#C. + This type pattern matches any non-null instance of the given class. + Note that the prefix of the class, if it is given, is relevant for determining class instances. + For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix. + The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case. + + - A singleton type p.type. + This type pattern matches only the value denoted by the path p + (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now + // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time" + + - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern. + This type pattern matches all values that are matched by each of the type patterns Ti. + + - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _. + This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards. + The bounds or alias type of these type variable are determined as described in (§8.3). + + - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO + This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1. + **/ + + // generate the tree for the run-time test that follows from the fact that + // a `scrut` of known type `scrutTp` is expected to have type `expectedTp` + // uses maybeWithOuterCheck to check the type's prefix + private def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree = { import CODE._ + // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null` + // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false") + def genEqualsAndInstanceOf(sym: Symbol): Tree + = codegen._equals(REF(sym), patBinder) AND codegen._isInstanceOf(patBinder, pt.widen) + + def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe + + val patBinderTp = patBinder.info.widen + def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt) + + // TODO: [SPEC] type test for Array + // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest) + pt match { + case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here + case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil + case ThisType(sym) => REF(patBinder) OBJ_EQ This(sym) + case ConstantType(Constant(null)) if isRefTp(patBinderTp) => REF(patBinder) OBJ_EQ NULL + case ConstantType(const) => codegen._equals(Literal(const), patBinder) + case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL) + case _ => typeTest(patBinder, pt) + } + } + val cond = typeAndEqualityTest(patBinder, pt) - val res = pmgen._asInstanceOf(patBinder, nextBinderTp) + val res = codegen._asInstanceOf(patBinder, nextBinderTp) override def toString = "TET"+(patBinder, pt) } @@ -801,7 +852,7 @@ defined class Foo */ // NOTE: generate `patTree == patBinder`, since the extractor must be in control of the equals method (also, patBinder may be null) // equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required) - val cond = pmgen._equals(patTree, prevBinder) + val cond = codegen._equals(patTree, prevBinder) val res = CODE.REF(prevBinder) override def toString = "ET"+(prevBinder, patTree) } @@ -836,7 +887,7 @@ defined class Foo */ if (canDuplicate) { altss map {altTreeMakers => combineExtractors(altTreeMakers :+ TrivialTreeMaker(substitution(next).duplicate), pt) - } reduceLeft pmgen.typedOrElse(pt) + } reduceLeft codegen.typedOrElse(pt) } else { val rest = freshSym(pos, functionType(List(), inMatchMonad(pt)), "rest") // rest.info.member(nme.apply).withAnnotation(AnnotationInfo(ScalaInlineClass.tpe, Nil, Nil)) @@ -848,7 +899,7 @@ defined class Foo */ ) BLOCK( VAL(rest) === Function(Nil, substitution(next)), - combinedAlts reduceLeft pmgen.typedOrElse(pt) + combinedAlts reduceLeft codegen.typedOrElse(pt) ) } ) @@ -857,14 +908,239 @@ defined class Foo */ case class GuardTreeMaker(guardTree: Tree) extends TreeMaker { val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = pmgen.flatMapGuard(substitution(guardTree), next) + def chainBefore(next: Tree, pt: Type): Tree = codegen.flatMapGuard(substitution(guardTree), next) override def toString = "G("+ guardTree +")" } + def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) + + // a foldLeft to accumulate the localSubstitution left-to-right + // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution + def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = { + var accumSubst: Substitution = initial + treeMakers foreach { maker => + maker incorporateOuterSubstitution accumSubst + accumSubst = maker.substitution + } + removeSubstOnly(treeMakers) + } + + // calls propagateSubstitution on the treemakers + def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){ + val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + + emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{ + val (matcher, hasDefault, toHoist) = + if (casesUnOpt nonEmpty) { + // when specified, need to propagate pt explicitly (type inferencer can't handle it) + val optPt = + if (isFullyDefined(pt)) inMatchMonad(pt) + else NoType + + // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + // exhaustivity and reachability must be checked before optimization as well + val hasDefault = casesUnOpt.nonEmpty && { + val nonTrivLast = casesUnOpt.last + nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] + } + + val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt) + + val combinedCases = + cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) + + (combinedCases, hasDefault, toHoist) + } else (codegen.zero, false, Nil) + + val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) + if (toHoist isEmpty) expr + else Block(toHoist, expr) + } + } + + // combineExtractors changes the current substitution's of the tree makers in `treeMakers` + // requires propagateSubstitution(treeMakers) has been called + def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree = + treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt)) + + // TODO: do this during tree construction, but that will require tracking the current owner in treemakers + // TODO: assign more fine-grained positions + // fixes symbol nesting, assigns positions + private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { + currentOwner = origOwner + + override def traverse(t: Tree) { + if (t != EmptyTree && t.pos == NoPosition) { + t.setPos(pos) + } + t match { + case Function(_, _) if t.symbol == NoSymbol => + t.symbol = currentOwner.newAnonymousFunctionValue(t.pos) + // println("new symbol for "+ (t, t.symbol.ownerChain)) + case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) => + // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain)) + t.symbol.owner = currentOwner + case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2) + // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain)) + if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree?? + assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner, d.symbol.lazyAccessor) + d.symbol.lazyAccessor.owner = currentOwner + } + if(d.symbol.moduleClass ne NoSymbol) + d.symbol.moduleClass.owner = currentOwner + + d.symbol.owner = currentOwner + // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) => + // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain)) + case _ => + } + super.traverse(t) + } + + // override def apply + // println("before fixerupper: "+ xTree) + // currentRun.trackerFactory.snapshot() + // println("after fixerupper") + // currentRun.trackerFactory.snapshot() + } + } + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// generate actual trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + trait CodegenCore extends MatchMonadInterface { + private var ctr = 0 + def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1; + // assert(owner ne null) + // assert(owner ne NoSymbol) + NoSymbol.newTermSymbol(vpmName.counted(prefix, ctr), pos) setInfo repackExistential(tp) + } + + // codegen relevant to the structure of the translation (how extractors are combined) + trait AbsCodegen { + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + def one(res: Tree, bodyPt: Type, matchPt: Type): Tree + def zero: Tree + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree + def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree + + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree + def flatMapGuard(cond: Tree, next: Tree): Tree + + def fun(arg: Symbol, body: Tree): Tree + def ifThenElseZero(c: Tree, then: Tree): Tree + def _equals(checker: Tree, binder: Symbol): Tree + def _asInstanceOf(b: Symbol, tp: Type): Tree + def mkZero(tp: Type): Tree + + def tupleSel(binder: Symbol)(i: Int): Tree + def index(tgt: Tree)(i: Int): Tree + def drop(tgt: Tree)(n: Int): Tree + def and(a: Tree, b: Tree): Tree + def _isInstanceOf(b: Symbol, tp: Type): Tree + } + + def codegen: AbsCodegen + + def typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt)) + + abstract class CommonCodegen extends AbsCodegen { import CODE._ + def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body) + def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree) + def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder + def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i)) + def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) + def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya + def and(a: Tree, b: Tree): Tree = a AND b + def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero + + // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) + def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp) + if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX) + else gen.mkAsInstanceOf(t, tpX, true, false) + } + + def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false) + // { val tpX = repackExistential(tp) + // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } + // else gen.mkIsInstanceOf(REF(b), tpX, true, false) + // } + + def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp) + if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX) + else gen.mkAsInstanceOf(REF(b), tpX, true, false) + } + + // duplicated out of frustration with cast generation + def mkZero(tp: Type): Tree = { + tp.typeSymbol match { + case UnitClass => Literal(Constant()) + case BooleanClass => Literal(Constant(false)) + case FloatClass => Literal(Constant(0.0f)) + case DoubleClass => Literal(Constant(0.0d)) + case ByteClass => Literal(Constant(0.toByte)) + case ShortClass => Literal(Constant(0.toShort)) + case IntClass => Literal(Constant(0)) + case LongClass => Literal(Constant(0L)) + case CharClass => Literal(Constant(0.toChar)) + case _ => gen.mkAsInstanceOf(Literal(Constant(null)), tp, any = true, wrapInApply = false) // the magic incantation is true/false here + } + } + } + } + + trait PureMatchMonadInterface extends MatchMonadInterface { + val matchStrategy: Tree + + def inMatchMonad(tp: Type): Type = appliedType(oneSig, List(tp)).finalResultType + def pureType(tp: Type): Type = appliedType(oneSig, List(tp)).paramTypes.head + protected def matchMonadSym = oneSig.finalResultType.typeSymbol + + import CODE._ + def __match(n: Name): SelectStart = matchStrategy DOT n + + private lazy val oneSig: Type = + typer.typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message + } + + trait PureCodegen extends CodegenCore with PureMatchMonadInterface { + def codegen: AbsCodegen = pureCodegen + + object pureCodegen extends CommonCodegen { import CODE._ + //// methods in MatchingStrategy (the monad companion) -- used directly in translation + // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) + // __match.one(`res`) + def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res) + // __match.zero + def zero: Tree = __match(vpmName.zero) + // __match.guard(`c`, `then`) + def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then) + + //// methods in the monad instance -- used directly in translation + // `prev`.flatMap(`b` => `next`) + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) + // `thisCase`.orElse(`elseCase`) + def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase) + // __match.guard(`cond`, `res`).flatMap(`nextBinder` => `next`) + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next) + // __match.guard(`guardTree`, ()).flatMap((_: P[Unit]) => `next`) + def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), pureType(UnitClass.tpe), next) + } + } + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// OPTIMIZATIONS +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // decisions, decisions /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + trait TreeMakerApproximation extends TreeMakers { self: CodegenCore => object Test { var currId = 0 } @@ -876,7 +1152,7 @@ defined class Foo */ private val reusedBy = new collection.mutable.HashSet[Test] var reuses: Option[Test] = None def registerReuseBy(later: Test): Unit = { - assert(later.reuses.isEmpty) + assert(later.reuses.isEmpty, later.reuses) reusedBy += later later.reuses = Some(this) } @@ -957,25 +1233,16 @@ defined class Foo */ override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id } - /** a flow-sensitive, generalised, common sub-expression elimination - * reuse knowledge from performed tests - * the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality) - * when a sub-expression is share, it is stored in a mutable variable - * the variable is floated up so that its scope includes all of the program that shares it - * we generalize sharing to implication, where b reuses a if a => b and priors(a) => priors(b) (the priors of a sub expression form the path through the decision tree) - * - * intended to be generalised to exhaustivity/reachability checking - */ - def doCSE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = { + def approximateMatch(root: Symbol, cases: List[List[TreeMaker]]): List[List[Test]] = { // a variable in this set should never be replaced by a tree that "does not consist of a selection on a variable in this set" (intuitively) - val pointsToBound = collection.mutable.HashSet(prevBinder) + val pointsToBound = collection.mutable.HashSet(root) // the substitution that renames variables to variables in pointsToBound var normalize: Substitution = EmptySubstitution // replaces a variable (in pointsToBound) by a selection on another variable in pointsToBound // TODO check: - // pointsToBound -- accumSubst.from == Set(prevBinder) && (accumSubst.from.toSet -- pointsToBound) isEmpty + // pointsToBound -- accumSubst.from == Set(root) && (accumSubst.from.toSet -- pointsToBound) isEmpty var accumSubst: Substitution = EmptySubstitution val trees = new collection.mutable.HashSet[Tree] @@ -1031,12 +1298,28 @@ defined class Foo */ | GuardTreeMaker(_) | ProductExtractorTreeMaker(_, Some(_), _) => Havoc case AlternativesTreeMaker(_, _, _) => Havoc // TODO: can do better here - case SubstOnlyTreeMaker(_) => Top + case SubstOnlyTreeMaker(_, _) => Top case BodyTreeMaker(_, _) => Havoc }, tm) } - val testss = cases.map { _ map approximateTreeMaker } + cases.map { _ map approximateTreeMaker } + } + } + +//// + trait CommonSubconditionElimination extends TreeMakerApproximation { self: OptimizedCodegen => + /** a flow-sensitive, generalised, common sub-expression elimination + * reuse knowledge from performed tests + * the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality) + * when a sub-expression is share, it is stored in a mutable variable + * the variable is floated up so that its scope includes all of the program that shares it + * we generalize sharing to implication, where b reuses a if a => b and priors(a) => priors(b) (the priors of a sub expression form the path through the decision tree) + * + * intended to be generalised to exhaustivity/reachability checking + */ + def doCSE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = { + val testss = approximateMatch(prevBinder, cases) // interpret: val dependencies = new collection.mutable.LinkedHashMap[Test, Set[Cond]] @@ -1067,7 +1350,11 @@ defined class Foo */ // then, collapse these contiguous sequences of reusing tests // store the result of the final test and the intermediate results in hoisted mutable variables (TODO: optimize: don't store intermediate results that aren't used) // replace each reference to a variable originally bound by a collapsed test by a reference to the hoisted variable - testss map { tests => + val reused = new collection.mutable.HashMap[TreeMaker, ReusedCondTreeMaker] + var okToCall = false + val reusedOrOrig = (tm: TreeMaker) => {assert(okToCall); reused.getOrElse(tm, tm)} + + val res = testss map { tests => var currDeps = Set[Cond]() val (sharedPrefix, suffix) = tests span { test => (test.cond eq Top) || (for( @@ -1079,18 +1366,67 @@ defined class Foo */ } val collapsedTreeMakers = if (sharedPrefix.nonEmpty) { // even sharing prefixes of length 1 brings some benefit (overhead-percentage for compiler: 26->24%, lib: 19->16%) - for (test <- sharedPrefix; reusedTest <- test.reuses; if reusedTest.treeMaker.isInstanceOf[FunTreeMaker]) - reusedTest.treeMaker.asInstanceOf[FunTreeMaker].reused = true + for (test <- sharedPrefix; reusedTest <- test.reuses) reusedTest.treeMaker match { + case reusedCTM: CondTreeMaker => reused(reusedCTM) = ReusedCondTreeMaker(reusedCTM) + case _ => + } + // println("sharedPrefix: "+ sharedPrefix) for (lastShared <- sharedPrefix.reverse.dropWhile(_.cond eq Top).headOption; lastReused <- lastShared.reuses) - yield ReusingCondTreeMaker(sharedPrefix map (t => (t.treeMaker, t.reuses map (_.treeMaker)))) :: suffix.map(_.treeMaker) + yield ReusingCondTreeMaker(sharedPrefix, reusedOrOrig) :: suffix.map(_.treeMaker) } else None collapsedTreeMakers getOrElse tests.map(_.treeMaker) // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above) } + okToCall = true // TODO: remove (debugging) + + res mapConserve (_ mapConserve reusedOrOrig) + } + + object ReusedCondTreeMaker { + def apply(orig: CondTreeMaker) = new ReusedCondTreeMaker(orig.prevBinder, orig.nextBinder, orig.cond, orig.res, orig.pos) + } + class ReusedCondTreeMaker(prevBinder: Symbol, val nextBinder: Symbol, cond: Tree, res: Tree, pos: Position) extends TreeMaker { import CODE._ + lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) + lazy val storedCond = freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE + lazy val treesToHoist: List[Tree] = { + nextBinder setFlag MUTABLE + List(storedCond, nextBinder) map { b => VAL(b) === codegen.mkZero(b.info) } + } + + // TODO: finer-grained duplication + def chainBefore(next: Tree, pt: Type): Tree = // assert(codegen eq optimizedCodegen) + atPos(pos)(optimizedCodegen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + } + + case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ + lazy val dropped_priors = sharedPrefix map (t => (toReused(t.treeMaker), t.reuses map (test => toReused(test.treeMaker)))) + lazy val localSubstitution = { + val (from, to) = dropped_priors.collect { + case (dropped: CondTreeMaker, Some(prior: ReusedCondTreeMaker)) => + (dropped.nextBinder, REF(prior.nextBinder)) + }.unzip + val oldSubs = dropped_priors.collect { + case (dropped: TreeMaker, _) => + dropped.substitution + } + oldSubs.foldLeft(Substitution(from, to))(_ >> _) + } + + def chainBefore(next: Tree, pt: Type): Tree = { + val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond) + + IF (cond) THEN BLOCK( + substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) + ) ELSE codegen.zero + } } + } + + //// DCE + trait DeadCodeElimination extends TreeMakers { self: CodegenCore => // TODO: non-trivial dead-code elimination // e.g., the following match should compile to a simple instanceof: // case class Ident(name: String) @@ -1099,21 +1435,10 @@ defined class Foo */ // do minimal DCE cases } + } - - def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) - - // a foldLeft to accumulate the localSubstitution left-to-right - // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution - def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = { - var accumSubst: Substitution = initial - treeMakers foreach { maker => - maker incorporateOuterSubstitution accumSubst - accumSubst = maker.substitution - } - removeSubstOnly(treeMakers) - } - + //// SWITCHES + trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore => object SwitchablePattern { def unapply(pat: Tree) = pat match { case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches case _ => false @@ -1128,23 +1453,35 @@ defined class Foo */ // } // } - def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (!optimizingCodeGen) None else { + private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + + override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { def sequence[T](xs: List[Option[T]]): Option[List[T]] = if (xs exists (_.isEmpty)) None else Some(xs.flatten) + def isSwitchableTpe(tpe: Type): Boolean = + switchableTpes contains tpe + def switchableConstToInt(x: Tree): Tree = { + val Literal(const) = x + const.tag match { + case IntTag => x + case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue)) + } + } + val caseDefs = cases map { makers => removeSubstOnly(makers) match { // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches) case (btm@BodyTreeMaker(body, _)) :: Nil => Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) // constant - case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => import CODE._ - Some(CaseDef(const, EmptyTree, btm.substitution(body))) + case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => + Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body))) // alternatives case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) val caseConstants = altss map { case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil => - Some(const) + Some(switchableConstToInt(const)) case _ => None } @@ -1158,200 +1495,58 @@ defined class Foo */ } } - sequence(caseDefs) map { caseDefs => - import CODE._ - val matcher = BLOCK( - VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't - Match(REF(scrutSym), caseDefs) // match on scrutSym, not scrut to avoid duplicating scrut - ) - - // matcher filter (tree => tree.tpe == null) foreach println - // treeBrowser browse matcher - matcher // set type to avoid recursion in typedMatch - } - } - - def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = - doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) - - // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){ - val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them - - emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{ - var toHoist = List[Tree]() - val (matcher, hasDefault) = - if (casesUnOpt nonEmpty) { - // when specified, need to propagate pt explicitly (type inferencer can't handle it) - val optPt = - if (isFullyDefined(pt)) inMatchMonad(pt) - else NoType - - // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one - // exhaustivity and reachability must be checked before optimization as well - val hasDefault = casesUnOpt.nonEmpty && { - val nonTrivLast = casesUnOpt.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] + if (!isSwitchableTpe(scrut.tpe)) + None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't + else { + sequence(caseDefs) map { caseDefs => + import CODE._ + val caseDefsWithDefault = { + def isDefault(x: CaseDef): Boolean = x match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case _ => false } - - val cases = - if (optimizingCodeGen) optimizeCases(scrutSym, casesUnOpt, pt) - else casesUnOpt - - val combinedCases = - cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt)) - - toHoist = (for (treeMakers <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList - - (pmgen.fun(scrutSym, combinedCases), hasDefault) - } else (pmgen.zero, false) - - val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType, hasDefault) - if (toHoist isEmpty) expr - else Block(toHoist, expr) - } - } - - // combineExtractors changes the current substitution's of the tree makers in `treeMakers` - // requires propagateSubstitution(treeMakers) has been called - def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree = - treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt)) - - - - // TODO: do this during tree construction, but that will require tracking the current owner in treemakers - // TODO: assign more fine-grained positions - // fixes symbol nesting, assigns positions - private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser { - currentOwner = origOwner - - override def traverse(t: Tree) { - if (t != EmptyTree && t.pos == NoPosition) { - t.setPos(pos) - } - t match { - case Function(_, _) if t.symbol == NoSymbol => - t.symbol = currentOwner.newAnonymousFunctionValue(t.pos) - // println("new symbol for "+ (t, t.symbol.ownerChain)) - case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) => - // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain)) - t.symbol.owner = currentOwner - case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2) - // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain)) - if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree?? - assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner) - d.symbol.lazyAccessor.owner = currentOwner + val hasDefault = caseDefs exists isDefault + if (hasDefault) caseDefs else { + val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } + caseDefs :+ default } - if(d.symbol.moduleClass ne NoSymbol) - d.symbol.moduleClass.owner = currentOwner - - d.symbol.owner = currentOwner - // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) => - // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain)) - case _ => + } + val matcher = BLOCK( + if (scrut.tpe != IntClass.tpe) { + scrutSym setInfo IntClass.tpe + VAL(scrutSym) === (scrut DOT nme.toInt) + } else { + VAL(scrutSym) === scrut + }, + Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut + ) + // matcher filter (tree => tree.tpe == null) foreach println + // treeBrowser browse matcher + matcher // set type to avoid recursion in typedMatch } - super.traverse(t) - } - - // override def apply - // println("before fixerupper: "+ xTree) - // currentRun.trackerFactory.snapshot() - // println("after fixerupper") - // currentRun.trackerFactory.snapshot() - } - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// substitution -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - object Substitution { - def apply(from: Symbol, to: Tree) = new Substitution(List(from), List(to)) - // requires sameLength(from, to) - def apply(from: List[Symbol], to: List[Tree]) = - if (from nonEmpty) new Substitution(from, to) else EmptySubstitution - } - - class Substitution(val from: List[Symbol], val to: List[Tree]) { - def apply(tree: Tree): Tree = typedSubst(tree, from, to) - - // the substitution that chains `other` before `this` substitution - // forall t: Tree. this(other(t)) == (this >> other)(t) - def >>(other: Substitution): Substitution = { - val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f) } - new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly } - override def toString = (from zip to) mkString("Substitution(", ", ", ")") } - - object EmptySubstitution extends Substitution(Nil, Nil) { - override def apply(tree: Tree): Tree = tree - override def >>(other: Substitution): Substitution = other - } - - - def matchingMonadType: Type - def typedSubst(tree: Tree, from: List[Symbol], to: List[Tree]): Tree - def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x"): Symbol - def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree - def typeTest(binder: Symbol, pt: Type): Tree - - // codegen relevant to the structure of the translation (how extractors are combined) - trait AbsCodeGen { import CODE.UNIT - def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree - def flatMap(a: Tree, b: Tree): Tree - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree - def flatMapGuard(cond: Tree, next: Tree): Tree - def fun(arg: Symbol, body: Tree): Tree - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree - def zero: Tree - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree - def condOptimized(c: Tree, then: Tree): Tree - def _equals(checker: Tree, binder: Symbol): Tree - def _asInstanceOf(b: Symbol, tp: Type): Tree - def mkZero(tp: Type): Tree - } - - def pmgen: AbsCodeGen - def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// generate actual trees -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - trait MatchCodeGen extends TreeMakers { - lazy val pmgen: CommonCodeGen with MatchingStrategyGen with MonadInstGen = - if (optimizingCodeGen) (new CommonCodeGen with OptimizedCodeGen {}) - else (new CommonCodeGen with MatchingStrategyGen with MonadInstGen {}) - - import CODE._ - - trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => - // methods in MatchingStrategy (the monad companion) -- used directly in translation - def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutTp, resTp) APPLY (scrut) APPLY (matcher) // matchingStrategy.runOrElse(scrut)(matcher) - // *only* used to wrap the RHS of a body (isDefinedAt synthesis relies on this) - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (matchingStrategy DOT vpmName.one) (_asInstanceOf(res, bodyPt, force = true)) // matchingStrategy.one(res), like one, but blow this one away for isDefinedAt (since it's the RHS of a case) - def zero: Tree = matchingStrategy DOT vpmName.zero // matchingStrategy.zero - def guard(c: Tree, then: Tree, tp: Type): Tree = genTypeApply((matchingStrategy DOT vpmName.guard), repackExistential(tp)) APPLY (c, then) // matchingStrategy.guard[tp](c, then) - } + trait OptimizedMatchMonadInterface extends MatchMonadInterface { + override def inMatchMonad(tp: Type): Type = optionType(tp) + override def pureType(tp: Type): Type = tp + override protected def matchMonadSym = OptionClass + } - trait MonadInstGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => - // methods in the monad instance -- used directly in translation - def flatMap(a: Tree, b: Tree): Tree = (a DOT vpmName.flatMap)(b) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase) + trait OptimizedCodegen extends CodegenCore with TypedSubstitution with OptimizedMatchMonadInterface { + override def codegen: AbsCodegen = optimizedCodegen - // TODO: the trees generated by flatMapCond and flatMapGuard may need to be distinguishable by exhaustivity checking -- they aren't right now - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, - nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), fun(nextBinder, next)) - def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, UnitClass.tpe), UnitClass.tpe, next) - } + // trait AbsOptimizedCodegen extends AbsCodegen { + // def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree + // } + // def optimizedCodegen: AbsOptimizedCodegen // when we know we're targetting Option, do some inlining the optimizer won't do - // `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard - // this is a special instance of the advanced inlining optimization that takes a method call on - // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases - // this trait overrides ALL of the methods of MatchingStrategyGen with MonadInstGen - trait OptimizedCodeGen extends CommonCodeGen with MatchingStrategyGen with MonadInstGen { + // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard + // this is a special instance of the advanced inlining optimization that takes a method call on + // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases + object optimizedCodegen extends CommonCodegen /*with AbsOptimizedCodegen*/ { import CODE._ lazy val zeroSym = freshSym(NoPosition, optionType(NothingClass.tpe), "zero") /** Inline runOrElse and get rid of Option allocations @@ -1363,23 +1558,22 @@ defined class Foo */ @inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass) lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE - override def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean) = { - val Function(List(x: ValDef), body) = matcher + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = { matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245 BLOCK( - VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... - VAL(x.symbol) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for x.symbol in body -- the owner structure is repaired by fixerUpper + VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... + VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above VAL(keepGoing) === TRUE, - body, + matcher, if(hasDefault) REF(matchRes) - else (IF (REF(keepGoing)) THEN MATCHERROR(REF(x.symbol)) ELSE REF(matchRes)) + else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes)) ) } // only used to wrap the RHS of a body - override def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { + def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { BLOCK( if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true) @@ -1388,194 +1582,57 @@ defined class Foo */ ) } - override def zero: Tree = REF(zeroSym) - - // guard is only used by flatMapCond and flatMapGuard, which are overridden - override def guard(c: Tree, then: Tree, tp: Type): Tree = throw new NotImplementedError("guard is never called by optimizing codegen") + def zero: Tree = REF(zeroSym) - override def flatMap(opt: Tree, fun: Tree): Tree = fun match { - case Function(List(x: ValDef), body) => - val tp = inMatchMonad(x.symbol.tpe) - val vs = freshSym(opt.pos, tp, "o") - val isEmpty = tp member vpmName.isEmpty - val get = tp member vpmName.get - val v = VAL(vs) === opt + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { + val tp = inMatchMonad(b.tpe) + val prevSym = freshSym(prev.pos, tp, "o") + val isEmpty = tp member vpmName.isEmpty + val get = tp member vpmName.get - BLOCK( - v, - IF (vs DOT isEmpty) THEN zero ELSE typedSubst(body, List(x.symbol), List(vs DOT get)) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) - ) - case _ => println("huh?") - (opt DOT vpmName.flatMap)(fun) + BLOCK( + VAL(prevSym) === prev, + IF (prevSym DOT isEmpty) THEN zero ELSE Substitution(b, prevSym DOT get)(next) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) + ) } - override def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { + def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { BLOCK( thisCase, IF (REF(keepGoing)) THEN elseCase ELSE zero // leave trailing zero for now, otherwise typer adds () anyway ) } - override def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = IF (cond) THEN BLOCK( VAL(nextBinder) === res, next ) ELSE zero - override def flatMapGuard(guardTree: Tree, next: Tree): Tree = - IF (guardTree) THEN next ELSE zero - } - - @inline private def typedIfOrigTyped(to: Tree, origTp: Type): Tree = - if (origTp == null || origTp == NoType) to - // important: only type when actually substing and when original tree was typed - // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors) - else typed(to, EXPRmode, WildcardType) - - // We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed, - // and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees. - def typedSubst(tree: Tree, from: List[Symbol], to: List[Tree]): Tree = { - // according to -Ystatistics 10% of translateMatch's time is spent in this method... - // since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst - if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree - else (new Transformer { - override def transform(tree: Tree): Tree = { - def subst(from: List[Symbol], to: List[Tree]): Tree = - if (from.isEmpty) tree - else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate, tree.tpe) - else subst(from.tail, to.tail) - - tree match { - case Ident(_) => subst(from, to) - case _ => super.transform(tree) - } - } - }).transform(tree) - } - - var ctr = 0 - def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1; - // assert(owner ne null) - // assert(owner ne NoSymbol) - new TermSymbol(NoSymbol, pos, vpmName.counted(prefix, ctr)) setInfo repackExistential(tp) - } - - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) - case _ => tp - } + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + IF (cond) THEN BLOCK( + condSym === TRUE, + nextBinder === res, + next + ) ELSE zero - object vpmName { - val one = newTermName("one") - val drop = newTermName("drop") - val flatMap = newTermName("flatMap") - val get = newTermName("get") - val guard = newTermName("guard") - val isEmpty = newTermName("isEmpty") - val orElse = newTermName("orElse") - val outer = newTermName("<outer>") - val runOrElse = newTermName("runOrElse") - val zero = newTermName("zero") - - def counted(str: String, i: Int) = newTermName(str+i) + def flatMapGuard(guardTree: Tree, next: Tree): Tree = + IF (guardTree) THEN next ELSE zero } + } - def typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt)) - - trait CommonCodeGen extends AbsCodeGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => - def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body) - def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree) - def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder - def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i)) - def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) - def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya - def and(a: Tree, b: Tree): Tree = a AND b - def condOptimized(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero - - // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp) - if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX) - else gen.mkAsInstanceOf(t, tpX, true, false) - } - - def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false) - // { val tpX = repackExistential(tp) - // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } - // else gen.mkIsInstanceOf(REF(b), tpX, true, false) - // } - - def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp) - if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX) - else gen.mkAsInstanceOf(REF(b), tpX, true, false) - } - - // duplicated out of frustration with cast generation - def mkZero(tp: Type): Tree = { - tp.typeSymbol match { - case UnitClass => Literal(Constant()) - case BooleanClass => Literal(Constant(false)) - case FloatClass => Literal(Constant(0.0f)) - case DoubleClass => Literal(Constant(0.0d)) - case ByteClass => Literal(Constant(0.toByte)) - case ShortClass => Literal(Constant(0.toShort)) - case IntClass => Literal(Constant(0)) - case LongClass => Literal(Constant(0L)) - case CharClass => Literal(Constant(0.toChar)) - case _ => gen.mkAsInstanceOf(Literal(Constant(null)), tp, any = true, wrapInApply = false) // the magic incantation is true/false here - } - } + trait MatchOptimizations extends CommonSubconditionElimination + with DeadCodeElimination + with SwitchEmission + with OptimizedCodegen { self: TreeMakers => + override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = { + val optCases = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) + val toHoist = ( + for (treeMakers <- optCases) + yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} + ).flatten.flatten.toList + (optCases, toHoist) } - - def matchingStrategy: Tree } } - -// object noShadowedUntyped extends Traverser { -// override def traverse(t: Tree) { -// if ((t.tpe ne null) && (t.tpe ne NoType)) okTree = t -// else if(okTree ne null) println("untyped subtree "+ t +" in typed tree"+ okTree +" : "+ okTree.tpe) -// super.traverse(t) -// } -// var okTree: Tree = null -// } -// private def c(t: Tree): Tree = noShadowedUntyped(t) - - // def approximateTreeMaker(tm: TreeMaker): List[Test] = tm match { - // case ExtractorTreeMaker(extractor, _, _) => HavocTest - // case FilteredExtractorTreeMaker(extractor, lenGuard, _, _) => HavocTest - // case ProductExtractorTreeMaker(testedBinder, lenGuard, _) => TopTest // TODO: (testedBinder ne null) and lenGuard - // - // // cond = typeTest(prevBinder, nextBinderTp) - // // res = pmgen._asInstanceOf(prevBinder, nextBinderTp) - // case TypeTestTreeMaker(testedBinder, pt, _) => - // - // // cond = typeAndEqualityTest(patBinder, pt) - // // res = pmgen._asInstanceOf(patBinder, nextBinderTp) - // case TypeAndEqualityTestTreeMaker(_, testedBinder, pt, _) => - // - // // cond = pmgen._equals(patTree, prevBinder) - // // res = CODE.REF(prevBinder) - // case EqualityTestTreeMaker(testedBinder, rhs, _) => - // - // case AlternativesTreeMaker(_, alts: *) => - // - // case GuardTreeMaker(guardTree) => - // } - - // // TODO: it's not exactly sound to represent an unapply-call by its symbol... also need to consider the prefix, like the outer-test (can this be captured as the path to this test?) - // type ExtractorRepr = Symbol - // - // // TODO: we're undoing tree-construction that we ourselves performed earlier -- how about not-doing so we don't have to undo? - // private def findBinderArgOfApply(extractor: Tree, unappSym: Symbol): Symbol = { - // class CollectTreeTraverser[T](pf: PartialFunction[Tree => T]) extends Traverser { - // val hits = new ListBuffer[T] - // override def traverse(t: Tree) { - // if (pf.isDefinedAt(t)) hits += pf(t) - // super.traverse(t) - // } - // } - // val trav = new CollectTreeTraverser{ case Apply(unapp, List(arg)) if unapp.symbol eq unappSym => arg.symbol} - // trav.traverse(extractor) - // trav.hits.headOption getOrElse NoSymbol - // } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 112aa47114..e313edb3f6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -276,6 +276,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R * of class `clazz` are met. */ def checkOverride(member: Symbol, other: Symbol) { + debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString)) + def memberTp = self.memberType(member) def otherTp = self.memberType(other) def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType @@ -924,9 +926,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) { - (tps zip tparams) foreach { - case (tp, tparam) => validateVariance(tp, variance * tparam.variance) - } + foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance)) } validateVariance(base.info, CoVariance) @@ -955,7 +955,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R // Forward reference checking --------------------------------------------------- class LevelInfo(val outer: LevelInfo) { - val scope: Scope = if (outer eq null) new Scope else new Scope(outer.scope) + val scope: Scope = if (outer eq null) newScope else newNestedScope(outer.scope) var maxindex: Int = Int.MinValue var refpos: Position = _ var refsym: Symbol = _ @@ -1034,10 +1034,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R /** Symbols which limit the warnings we can issue since they may be value types */ val isMaybeValue = Set(AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) - // Whether def equals(other: Any) is overridden - def isUsingDefaultEquals = { + // Whether def equals(other: Any) is overridden or synthetic + def isUsingWarnableEquals = { val m = receiver.info.member(nme.equals_) - (m == Object_equals) || (m == Any_equals) + (m == Object_equals) || (m == Any_equals) || (m.isSynthetic && m.owner.isCase) } // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere. def isUsingDefaultScalaOp = { @@ -1045,7 +1045,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) } // Whether the operands+operator represent a warnable combo (assuming anyrefs) - def isWarnable = isReferenceOp || (isUsingDefaultEquals && isUsingDefaultScalaOp) + // Looking for comparisons performed with ==/!= in combination with either an + // equals method inherited from Object or a case class synthetic equals (for + // which we know the logic.) + def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info) def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass @@ -1240,11 +1243,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } /* Check whether argument types conform to bounds of type parameters */ - private def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type], pos: Position): Unit = - try typer.infer.checkBounds(pos, pre, owner, tparams, argtps, "") + private def checkBounds(tree0: Tree, pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type]): Unit = + try typer.infer.checkBounds(tree0, pre, owner, tparams, argtps, "") catch { case ex: TypeError => - unit.error(pos, ex.getMessage()); + unit.error(tree0.pos, ex.getMessage()) if (settings.explaintypes.value) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds) (argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ)) @@ -1374,22 +1377,22 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R false } - private def checkTypeRef(tp: Type, pos: Position) = tp match { + private def checkTypeRef(tp: Type, tree: Tree) = tp match { case TypeRef(pre, sym, args) => - checkDeprecated(sym, pos) + checkDeprecated(sym, tree.pos) if(sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) if (!tp.isHigherKinded) - checkBounds(pre, sym.owner, sym.typeParams, args, pos) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) case _ => } - private def checkAnnotations(tpes: List[Type], pos: Position) = tpes foreach (tp => checkTypeRef(tp, pos)) + private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach (tp => checkTypeRef(tp, tree)) private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f private def applyRefchecksToAnnotations(tree: Tree): Unit = { def applyChecks(annots: List[AnnotationInfo]) = { - checkAnnotations(annots map (_.atp), tree.pos) + checkAnnotations(annots map (_.atp), tree) transformTrees(annots flatMap (_.args)) } @@ -1404,7 +1407,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case tpt@TypeTree() => if(tpt.original != null) { tpt.original foreach { - case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check()) // #2416 + case dc@TypeTreeWithDeferredRefCheck() => + applyRefchecksToAnnotations(dc.check()) // #2416 case _ => } } @@ -1450,7 +1454,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R unit.error(tree.pos, "too many dimensions for array creation") Literal(Constant(null)) } else { - localTyper.getManifestTree(tree.pos, etpe, false) + localTyper.getManifestTree(tree, etpe, false) } } val newResult = localTyper.typedPos(tree.pos) { @@ -1490,7 +1494,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def checkSuper(mix: Name) = // term should have been eliminated by super accessors - assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY)) + assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix)) transformCaseApply(tree, qual match { @@ -1578,13 +1582,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case ExistentialType(tparams, tpe) => existentialParams ++= tparams case t: TypeRef => - checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree.pos) + checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree) case _ => } tree case TypeApply(fn, args) => - checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe), tree.pos) + checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) transformCaseApply(tree, ()) case x @ Apply(_, _) => @@ -1641,7 +1645,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R result } catch { case ex: TypeError => - if (settings.debug.value) ex.printStackTrace(); + if (settings.debug.value) ex.printStackTrace() unit.error(tree.pos, ex.getMessage()) tree } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 4e4fbe35cb..0ab09b4fec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -51,12 +51,21 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } private def checkPackedConforms(tree: Tree, pt: Type): Tree = { + def typeError(typer: analyzer.Typer, pos: Position, found: Type, req: Type) { + if (!found.isErroneous && !req.isErroneous) { + val msg = analyzer.ErrorUtils.typeErrorMsg(found, req, typer.infer.isPossiblyMissingArgs(found, req)) + typer.context.error(pos, analyzer.withAddendum(pos)(msg)) + if (settings.explaintypes.value) + explainTypes(found, req) + } + } + if (tree.tpe exists (_.typeSymbol.isExistentialSkolem)) { val packed = localTyper.packedType(tree, NoSymbol) if (!(packed <:< pt)) { val errorContext = localTyper.context.make(localTyper.context.tree) - errorContext.reportGeneralErrors = true - analyzer.newTyper(errorContext).infer.typeError(tree.pos, packed, pt) + errorContext.setReportErrors() + typeError(analyzer.newTyper(errorContext), tree.pos, packed, pt) } } tree @@ -444,7 +453,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (referencingClass.isSubClass(sym.owner.enclClass) || referencingClass.thisSym.isSubClass(sym.owner.enclClass) || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) { - assert(referencingClass.isClass) + assert(referencingClass.isClass, referencingClass) referencingClass } else if(referencingClass.owner.enclClass != NoSymbol) hostForAccessorOf(sym, referencingClass.owner.enclClass) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 4e986dc5aa..cf90577959 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -36,158 +36,13 @@ trait SyntheticMethods extends ast.TreeDSL { import definitions._ import CODE._ - private object util { - private type CM[T] = ClassManifest[T] - - def ValOrDefDef(sym: Symbol, body: Tree) = - if (sym.isLazy) ValDef(sym, body) - else DefDef(sym, body) - - /** To avoid unchecked warnings on polymorphic classes. - */ - def clazzTypeToTest(clazz: Symbol) = clazz.tpe.normalize match { - case TypeRef(_, sym, args) if args.nonEmpty => newExistentialType(sym.typeParams, clazz.tpe) - case tp => tp - } - - def makeMethodPublic(method: Symbol): Symbol = ( - method setPrivateWithin NoSymbol resetFlag AccessFlags - ) - - def methodArg(method: Symbol, idx: Int): Tree = Ident(method.paramss.head(idx)) - - private def applyTypeInternal(manifests: List[CM[_]]): Type = { - val symbols = manifests map manifestToSymbol - val container :: args = symbols - val tparams = container.typeConstructor.typeParams - - // Overly conservative at present - if manifests were more usable - // this could do a lot more. - require(symbols forall (_ ne NoSymbol), "Must find all manifests: " + symbols) - require(container.owner.isPackageClass, "Container must be a top-level class in a package: " + container) - require(tparams.size == args.size, "Arguments must match type constructor arity: " + tparams + ", " + args) - require(args forall (_.typeConstructor.typeParams.isEmpty), "Arguments must be unparameterized: " + args) - - typeRef(container.typeConstructor.prefix, container, args map (_.tpe)) - } - - def manifestToSymbol(m: CM[_]): Symbol = m match { - case x: scala.reflect.AnyValManifest[_] => getMember(ScalaPackageClass, newTermName("" + x)) - case _ => getClassIfDefined(m.erasure.getName) - } - def companionType[T](implicit m: CM[T]) = - getRequiredModule(m.erasure.getName).tpe - - // Use these like `applyType[List, Int]` or `applyType[Map, Int, String]` - def applyType[M](implicit m1: CM[M]): Type = - applyTypeInternal(List(m1)) - - def applyType[M[X1], X1](implicit m1: CM[M[_]], m2: CM[X1]): Type = - applyTypeInternal(List(m1, m2)) - - def applyType[M[X1, X2], X1, X2](implicit m1: CM[M[_,_]], m2: CM[X1], m3: CM[X2]): Type = - applyTypeInternal(List(m1, m2, m3)) - - def applyType[M[X1, X2, X3], X1, X2, X3](implicit m1: CM[M[_,_,_]], m2: CM[X1], m3: CM[X2], m4: CM[X3]): Type = - applyTypeInternal(List(m1, m2, m3, m4)) - } - import util._ - - class MethodSynthesis(val clazz: Symbol, localTyper: Typer) { - private def isOverride(method: Symbol) = - clazzMember(method.name).alternatives exists (sym => (sym != method) && !sym.isDeferred) - - private def setMethodFlags(method: Symbol): Symbol = { - val overrideFlag = if (isOverride(method)) OVERRIDE else 0L - - method setFlag (overrideFlag | SYNTHETIC) resetFlag DEFERRED - } - - private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = { - setMethodFlags(method) - clazz.info.decls enter method - logResult("finishMethod")(localTyper typed ValOrDefDef(method, f(method))) - } - - private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus) - m setInfo info - finishMethod(m, f) - } - private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = { - val m = clazz.newMethod(name.toTermName, clazz.pos.focus) - m setInfo infoFn(m) - finishMethod(m, f) - } - private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { - val m = original.cloneSymbol(clazz) setPos clazz.pos.focus - m.name = name - finishMethod(m, f) - } - - private def cloneInternal(original: Symbol, f: Symbol => Tree): Tree = - cloneInternal(original, f, original.name) - - def clazzMember(name: Name) = clazz.info nonPrivateMember name match { - case NoSymbol => log("In " + clazz + ", " + name + " not found: " + clazz.info) ; NoSymbol - case sym => sym - } - def typeInClazz(sym: Symbol) = clazz.thisType memberType sym - - /** Function argument takes the newly created method symbol of - * the same type as `name` in clazz, and returns the tree to be - * added to the template. - */ - def overrideMethod(name: Name)(f: Symbol => Tree): Tree = - overrideMethod(clazzMember(name))(f) - - def overrideMethod(original: Symbol)(f: Symbol => Tree): Tree = - cloneInternal(original, sym => f(sym setFlag OVERRIDE)) - - def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree = - cloneInternal(original, f, nameFn(original.name)) - - def createMethod(name: Name, paramTypes: List[Type], returnType: Type)(f: Symbol => Tree): Tree = - createInternal(name, f, (m: Symbol) => MethodType(m newSyntheticValueParams paramTypes, returnType)) - - def createMethod(name: Name, returnType: Type)(f: Symbol => Tree): Tree = - createInternal(name, f, NullaryMethodType(returnType)) - - def createMethod(original: Symbol)(f: Symbol => Tree): Tree = - createInternal(original.name, f, original.info) - - def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree = - createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident))) - - def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = { - createMethod(name, List(IntClass.tpe), returnType) { m => - val arg0 = methodArg(m, 0) - val default = DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg0) - val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default - - Match(arg0, cases) - } - } - - // def foo() = constant - def constantMethod(name: Name, value: Any): Tree = { - val constant = Constant(value) - createMethod(name, Nil, constant.tpe)(_ => Literal(constant)) - } - // def foo = constant - def constantNullary(name: Name, value: Any): Tree = { - val constant = Constant(value) - createMethod(name, constant.tpe)(_ => Literal(constant)) - } - } - /** Add the synthetic methods to case classes. */ def addSyntheticMethods(templ: Template, clazz0: Symbol, context: Context): Template = { if (phase.erasedTypes) return templ - val synthesizer = new MethodSynthesis( + val synthesizer = new ClassMethodSynthesis( clazz0, newTyper( if (reporter.hasErrors) context makeSilent false else context ) ) @@ -212,11 +67,12 @@ trait SyntheticMethods extends ast.TreeDSL { // like Manifests and Arrays which are not robust and infer things // which they shouldn't. val accessorLub = ( - if (opt.experimental) + if (opt.experimental) { global.weakLub(accessors map (_.tpe.finalResultType))._1 match { case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) case tp => tp } + } else AnyClass.tpe ) @@ -258,11 +114,10 @@ trait SyntheticMethods extends ast.TreeDSL { /** The canEqual method for case classes. * def canEqual(that: Any) = that.isInstanceOf[This] */ - def canEqualMethod: Tree = { - createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => - methodArg(m, 0) IS_OBJ clazzTypeToTest(clazz) - ) - } + def canEqualMethod: Tree = ( + createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => + Ident(m.firstParam) IS_OBJ classExistentialType(clazz)) + ) /** The equality method for case classes. * 0 args: @@ -276,8 +131,8 @@ trait SyntheticMethods extends ast.TreeDSL { * } */ def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => - val arg0 = methodArg(m, 0) - val thatTest = gen.mkIsInstanceOf(arg0, clazzTypeToTest(clazz), true, false) + val arg0 = Ident(m.firstParam) + val thatTest = gen.mkIsInstanceOf(arg0, classExistentialType(clazz), true, false) val thatCast = gen.mkCast(arg0, clazz.tpe) def argsBody: Tree = { @@ -331,7 +186,7 @@ trait SyntheticMethods extends ast.TreeDSL { Object_hashCode -> (() => constantMethod(nme.hashCode_, clazz.name.decode.hashCode)), Object_toString -> (() => constantMethod(nme.toString_, clazz.name.decode)) // Not needed, as reference equality is the default. - // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ methodArg(m, 0))) + // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ Ident(m.firstParam))) ) /** If you serialize a singleton and then deserialize it twice, @@ -381,7 +236,7 @@ trait SyntheticMethods extends ast.TreeDSL { for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body ; if isRewrite(ddef.symbol)) { val original = ddef.symbol val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc => - makeMethodPublic(newAcc) + newAcc.makePublic newAcc resetFlag (ACCESSOR | PARAMACCESSOR) ddef.rhs.duplicate } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index b0500776fe..ed263cbbef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -142,11 +142,11 @@ abstract class TreeCheckers extends Analyzer { result } def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = { - val unit0 = currentRun.currentUnit + val unit0 = currentUnit currentRun.currentUnit = unit body currentRun.advanceUnit - assertFn(currentRun.currentUnit == unit, "currentUnit is " + currentRun.currentUnit + ", but unit is " + unit) + assertFn(currentUnit == unit, "currentUnit is " + currentUnit + ", but unit is " + unit) currentRun.currentUnit = unit0 } def check(unit: CompilationUnit) { diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index fe3ceafa2d..4f4087a953 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -39,8 +39,6 @@ trait TypeDiagnostics { import definitions._ import global.typer.{ infer, context } - private def currentUnit = currentRun.currentUnit - /** The common situation of making sure nothing is erroneous could be * nicer if Symbols, Types, and Trees all implemented some common interface * in which isErroneous and similar would be placed. @@ -93,37 +91,6 @@ trait TypeDiagnostics { } } - def notAMemberMessage(pos: Position, qual: Tree, name: Name) = { - val owner = qual.tpe.typeSymbol - val target = qual.tpe.widen - def targetKindString = if (owner.isTypeParameterOrSkolem) "type parameter " else "" - def nameString = decodeWithKind(name, owner) - /** Illuminating some common situations and errors a bit further. */ - def addendum = { - val companion = { - if (name.isTermName && owner.isPackageClass) { - target.member(name.toTypeName) match { - case NoSymbol => "" - case sym => "\nNote: %s exists, but it has no companion object.".format(sym) - } - } - else "" - } - val semicolon = ( - if (posPrecedes(qual.pos, pos)) - "\npossible cause: maybe a semicolon is missing before `"+nameString+"'?" - else - "" - ) - companion + semicolon - } - - withAddendum(qual.pos)( - if (name == nme.CONSTRUCTOR) target + " does not have a constructor" - else nameString + " is not a member of " + targetKindString + target + addendum - ) - } - /** An explanatory note to be added to error messages * when there's a problem with abstract var defs */ def abstractVarMessage(sym: Symbol): String = @@ -131,9 +98,6 @@ trait TypeDiagnostics { "\n(Note that variables need to be initialized to be defined)" else "" - def notAMemberError(pos: Position, qual: Tree, name: Name) = - context.error(pos, notAMemberMessage(pos, qual, name)) - /** Only prints the parameter names if they're not synthetic, * since "x$1: Int" does not offer any more information than "Int". */ @@ -154,21 +118,6 @@ trait TypeDiagnostics { def alternativesString(tree: Tree) = alternatives(tree) map (x => " " + methodTypeErrorString(x)) mkString ("", " <and>\n", "\n") - def missingParameterTypeMsg(fun: Tree, vparam: ValDef, pt: Type) = { - def anonMessage = ( - "\nThe argument types of an anonymous function must be fully known. (SLS 8.5)" + - "\nExpected type was: " + pt.toLongString - ) - val suffix = - if (!vparam.mods.isSynthetic) "" - else " for expanded function" + (fun match { - case Function(_, Match(_, _)) => anonMessage - case _ => " " + fun - }) - - "missing parameter type" + suffix - } - /** The symbol which the given accessor represents (possibly in part). * This is used for error messages, where we want to speak in terms * of the actual declaration or definition, not in terms of the generated setters @@ -202,34 +151,6 @@ trait TypeDiagnostics { else defaultMessage } - def notEnoughArgumentsMsg(fun: Tree, missing: List[Symbol]): String = { - val suffix = { - if (missing.isEmpty) "" - else { - val keep = missing take 3 map (_.name) - ".\nUnspecified value parameter%s %s".format( - if (missing.tail.isEmpty) "" else "s", - if (missing drop 3 nonEmpty) (keep :+ "...").mkString(", ") - else keep.mkString("", ", ", ".") - ) - } - } - - "not enough arguments for " + treeSymTypeMsg(fun) + suffix - } - - def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = { - def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")") - - def resType = if (pt isWildcard) "" else " with expected result type " + pt - def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt - def locals = alternatives(tree) flatMap (_.typeParams) - - withDisambiguation(locals, allTypes: _*) { - treeSymTypeMsg(tree) + msg + asParams(argtpes) + resType - } - } - def disambiguate(ss: List[String]) = ss match { case Nil => Nil case s :: ss => s :: (ss map { case `s` => "(some other)"+s ; case x => x }) @@ -446,8 +367,8 @@ trait TypeDiagnostics { trait TyperDiagnostics { self: Typer => - private def contextError(pos: Position, msg: String) = context.error(pos, msg) - private def contextError(pos: Position, err: Throwable) = context.error(pos, err) + private def contextError(context0: Analyzer#Context, pos: Position, msg: String) = context0.error(pos, msg) + private def contextError(context0: Analyzer#Context, pos: Position, err: Throwable) = context0.error(pos, err) private def contextWarning(pos: Position, msg: String) = context.unit.warning(pos, msg) def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) = @@ -466,14 +387,8 @@ trait TypeDiagnostics { // Error suppression will squash some of these warnings unless we circumvent it. // It is presumed if you are using a -Y option you would really like to hear // the warnings you've requested. - if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && exprOK) { - val saved = context.reportGeneralErrors - try { - context.reportGeneralErrors = true - context.warning(tree.pos, "dead code following this construct") - } - finally context.reportGeneralErrors = saved - } + if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && exprOK) + context.warning(tree.pos, "dead code following this construct", true) tree } @@ -485,8 +400,8 @@ trait TypeDiagnostics { } } - def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded - def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive" + private def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded + private def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive" /** Returns Some(msg) if the given tree is untyped apparently due * to a cyclic reference, and None otherwise. @@ -500,15 +415,18 @@ trait TypeDiagnostics { "\nIf applicable, you may wish to try moving some members into another object." ) } - + /** Report a type error. * * @param pos0 The position where to report the error * @param ex The exception that caused the error */ - def reportTypeError(pos: Position, ex: TypeError) { + def reportTypeError(context0: Context, pos: Position, ex: TypeError) { if (ex.pos == NoPosition) ex.pos = pos - if (!context.reportGeneralErrors) throw ex + // TODO: should be replaced by throwErrors + // but it seems that throwErrors excludes some of the errors that should actually be + // buffered, causing TypeErrors to fly around again. This needs some more investigation. + if (!context0.reportErrors) throw ex if (settings.debug.value) ex.printStackTrace() ex match { @@ -517,12 +435,12 @@ trait TypeDiagnostics { case Import(expr, _) => expr.pos case _ => ex.pos } - contextError(pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) if (sym == ObjectClass) throw new FatalError("cannot redefine root "+sym) case _ => - contextError(ex.pos, ex) + contextError(context0, ex.pos, ex) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 216ad6cd4c..ef69e1525e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -12,14 +12,12 @@ package scala.tools.nsc package typechecker -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import scala.tools.nsc.util.BatchSourceFile import mutable.ListBuffer import symtab.Flags._ import util.Statistics import util.Statistics._ -import scala.tools.util.StringOps.{ countAsString, countElementsAsString } -import scala.tools.util.EditDistance.similarString // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -60,7 +58,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { super.traverse(tree) } } -/* needed for experimental version where eraly types can be type arguments +/* needed for experimental version where early types can be type arguments class EarlyMap(clazz: Symbol) extends TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(NoPrefix, sym, List()) if (sym hasFlag PRESUPER) => @@ -71,6 +69,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } */ + sealed abstract class SilentResult[+T] + case class SilentTypeError(err: AbsTypeError) extends SilentResult[Nothing] { } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { } + def newTyper(context: Context): Typer = new NormalTyper(context) private class NormalTyper(context : Context) extends Typer(context) @@ -80,9 +82,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { private def isPastTyper = phase.id > currentRun.typerPhase.id - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } + import TyperErrorGen._ val infer = new Inferencer(context0) { override def isCoercible(tp: Type, pt: Type): Boolean = undoLog undo { // #3281 @@ -97,20 +100,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case MethodType(params, _) => val argResultsBuff = new ListBuffer[SearchResult]() val argBuff = new ListBuffer[Tree]() + var paramFailed = false def mkPositionalArg(argTree: Tree, paramName: Name) = argTree def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree))) var mkArg: (Tree, Name) => Tree = mkPositionalArg - def errorMessage(paramName: Name, paramTp: Type) = - paramTp.typeSymbol match { - case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) - case _ => - "could not find implicit value for "+ - (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type " - else "parameter "+paramName+": ")+paramTp - } - // DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1)) // // apply the substitutions (undet type param -> type) that were determined @@ -120,15 +115,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { for(ar <- argResultsBuff) paramTp = paramTp.subst(ar.subst.from, ar.subst.to) - val res = inferImplicit(fun, paramTp, true, false, context) + val res = if (paramFailed) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, false, context) argResultsBuff += res if (res != SearchFailure) { argBuff += mkArg(res.tree, param.name) } else { mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args - if (!param.hasDefault) - context.error(fun.pos, errorMessage(param.name, param.tpe)) + if (!param.hasDefault && !paramFailed) { + context.errBuffer.find(_.kind == ErrorKinds.Divergent) match { + case Some(divergentImplicit) => + // DivergentImplicit error has higher priority than "no implicit found" + // no need to issue the problem again if we are still in silent mode + if (context.reportErrors) { + context.issue(divergentImplicit) + context.condBufferFlush(_.kind == ErrorKinds.Divergent) + } + case None => + NoImplicitFoundError(fun, param) + } + paramFailed = true + } /* else { TODO: alternative (to expose implicit search failure more) --> resolve argument, do type inference, keep emitting positional args, infer type params based on default value for arg @@ -149,6 +156,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ErrorType => fun } + + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = + inferView(tree, from, to, reportAmbiguous, true) /** Infer an implicit conversion (``view'') between two types. * @param tree The tree which needs to be converted. @@ -157,8 +167,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @param reportAmbiguous Should ambiguous implicit errors be reported? * False iff we search for a view to find out * whether one type is coercible to another. + * @param saveErrors Should ambiguous and divergent implicit errors that were buffered + * during the inference of a view be put into the original buffer. + * False iff we don't care about them. */ - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = { + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { debuglog("infer view from "+from+" to "+to)//debug if (isPastTyper) EmptyTree else from match { @@ -167,7 +180,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case PolyType(_, _) => EmptyTree case _ => def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context) + val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context, saveErrors) if (result.subst != EmptyTreeTypeSubstituter) result.subst traverse tree result.tree } @@ -205,22 +218,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @return ... */ def checkStable(tree: Tree): Tree = - if (treeInfo.isExprSafeToInline(tree)) tree - else errorTree( - tree, - "stable identifier required, but "+tree+" found."+ - (if (isStableExceptVolatile(tree)) { - val tpe = tree.symbol.tpe match { - case PolyType(_, rtpe) => rtpe - case t => t - } - "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." - } else "")) + if (treeInfo.isExprSafeToInline(tree)) tree else UnstableTreeError(tree) /** Would tree be a stable (i.e. a pure expression) if the type * of its symbol was not volatile? */ - private def isStableExceptVolatile(tree: Tree) = { + protected def isStableExceptVolatile(tree: Tree) = { tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && { val savedTpe = tree.symbol.info val savedSTABLE = tree.symbol getFlag STABLE @@ -234,21 +237,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } /** Check that `tpt` refers to a non-refinement class type */ - def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean) { - def errorNotClass(found: AnyRef) = error(tpt.pos, "class type required but "+found+" found") - def check(tpe: Type): Unit = tpe.normalize match { + def checkClassType(tpt: Tree, existentialOK: Boolean, stablePrefix: Boolean): Boolean = { + def errorNotClass(found: AnyRef) = { ClassTypeRequiredError(tpt, found); false } + def check(tpe: Type): Boolean = tpe.normalize match { case TypeRef(pre, sym, _) if sym.isClass && !sym.isRefinementClass => - if (stablePrefix && !isPastTyper) { - if (!pre.isStable) - error(tpt.pos, "type "+pre+" is not a stable prefix") - // A type projection like X#Y can get by the stable check if the - // prefix is singleton-bounded, so peek at the tree too. - else tpt match { + if (stablePrefix && !isPastTyper) + if (!pre.isStable) { + TypeNotAStablePrefixError(tpt, pre) + false + } else + // A type projection like X#Y can get by the stable check if the + // prefix is singleton-bounded, so peek at the tree too. + tpt match { case SelectFromTypeTree(qual, _) if !isSingleType(qual.tpe) => errorNotClass(tpt) - case _ => ; - } - } - case ErrorType => ; + case _ => true + } + else + true + case ErrorType => true case PolyType(_, restpe) => check(restpe) case ExistentialType(_, restpe) if existentialOK => check(restpe) case AnnotatedType(_, underlying, _) => check(underlying) @@ -264,17 +270,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @return <code>true</code> if <code>tp</code> is not a subtype of itself. */ def checkNonCyclic(pos: Position, tp: Type): Boolean = { - def checkNotLocked(sym: Symbol): Boolean = { + def checkNotLocked(sym: Symbol) = { sym.initialize - sym.lockOK || {error(pos, "cyclic aliasing or subtyping involving "+sym); false} + sym.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false } } tp match { case TypeRef(pre, sym, args) => - (checkNotLocked(sym)) && ( - !sym.isNonClassType || - checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym) // @M! info for a type ref to a type parameter now returns a polytype - // @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym) - ) + checkNotLocked(sym) && + ((!sym.isNonClassType) || checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym)) + // @M! info for a type ref to a type parameter now returns a polytype + // @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym) + case SingleType(pre, sym) => checkNotLocked(sym) /* @@ -293,10 +299,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try { - lockedSym.lock { - throw new TypeError("illegal cyclic reference involving " + lockedSym) - } - checkNonCyclic(pos, tp) + if (!lockedSym.lock(CyclicReferenceError(pos, lockedSym))) false + else checkNonCyclic(pos, tp) } finally { lockedSym.unlock() } @@ -312,26 +316,24 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def checkParamsConvertible(pos: Position, tpe: Type) { - tpe match { - case MethodType(formals, restpe) => - /* - if (formals.exists(_.typeSymbol == ByNameParamClass) && formals.length != 1) - error(pos, "methods with `=>`-parameter can be converted to function values only if they take no other parameters") - if (formals exists (isRepeatedParamType(_))) - error(pos, "methods with `*`-parameters cannot be converted to function values"); - */ - if (restpe.isDependent) - error(pos, "method with dependent type "+tpe+" cannot be converted to function value") - checkParamsConvertible(pos, restpe) - case _ => - } + def checkParamsConvertible(tree: Tree, tpe0: Type) { + def checkParamsConvertible0(tpe: Type) = + tpe match { + case MethodType(formals, restpe) => + /* + if (formals.exists(_.typeSymbol == ByNameParamClass) && formals.length != 1) + error(pos, "methods with `=>`-parameter can be converted to function values only if they take no other parameters") + if (formals exists (isRepeatedParamType(_))) + error(pos, "methods with `*`-parameters cannot be converted to function values"); + */ + if (restpe.isDependent) + DependentMethodTpeConversionToFunctionError(tree, tpe) + checkParamsConvertible(tree, restpe) + case _ => + } + checkParamsConvertible0(tpe0) } - def checkStarPatOK(pos: Position, mode: Int) = - if ((mode & STARmode) == 0 && !isPastTyper) - error(pos, "star patterns must correspond with varargs parameters") - /** Check that type of given tree does not contain local or private * components. */ @@ -362,13 +364,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def locals[T <: Tree](scope: Scope, pt: Type, tree: T): T = check(NoSymbol, scope, pt, tree) - def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { + private def check[T <: Tree](owner: Symbol, scope: Scope, pt: Type, tree: T): T = { this.owner = owner this.scope = scope hiddenSymbols = List() val tp1 = apply(tree.tpe) if (hiddenSymbols.isEmpty) tree setType tp1 - else if (hiddenSymbols exists (_.isErroneous)) setError(tree) + else if (hiddenSymbols exists (_.isErroneous)) HiddenSymbolWithError(tree) else if (isFullyDefined(pt)) tree setType pt else if (tp1.typeSymbol.isAnonymousClass) check(owner, scope, pt, tree setType tp1.typeSymbol.classBound) @@ -376,10 +378,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tree setType packSymbols(hiddenSymbols.reverse, tp1) else if (!phase.erasedTypes) { // privates val badSymbol = hiddenSymbols.head - error(tree.pos, - (if (badSymbol.isPrivate) "private " else "") + badSymbol + - " escapes its defining scope as part of type "+tree.tpe) - setError(tree) + SymbolEscapesScopeError(tree, badSymbol) } else tree } @@ -441,28 +440,66 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { /** The qualifying class * of a this or super with prefix <code>qual</code>. + * packageOk is equal false when qualifying class symbol */ - def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean): Symbol = + def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean = false): Option[Symbol] = context.enclClass.owner.ownerChain.find(o => qual.isEmpty || o.isClass && o.name == qual) match { case Some(c) if packageOK || !c.isPackageClass => - c + Some(c) case _ => - error( - tree.pos, - if (qual.isEmpty) tree+" can be used only in a class, object, or template" - else qual+" is not an enclosing class") - NoSymbol + QualifyingClassError(tree, qual) + None } /** The typer for an expression, depending on where we are. If we are before a superclass * call, this is a typer over a constructor context; otherwise it is the current typer. */ - def constrTyperIf(inConstr: Boolean): Typer = + @inline + final def constrTyperIf(inConstr: Boolean): Typer = if (inConstr) { - assert(context.undetparams.isEmpty) + assert(context.undetparams.isEmpty, context.undetparams) newTyper(context.makeConstructorContext) } else this + @inline + final def withCondConstrTyper[T](inConstr: Boolean)(f: Typer => T): T = + if (inConstr) { + assert(context.undetparams.isEmpty, context.undetparams) + val c = context.makeConstructorContext + typerWithLocalContext(c)(f) + } else { + f(this) + } + + @inline + final def typerWithCondLocalContext[T](c: => Context)(cond: Boolean)(f: Typer => T): T = + if (cond) typerWithLocalContext(c)(f) else f(this) + + @inline + final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { + val res = f(newTyper(c)) + if (c.hasErrors) + context.updateBuffer(c.flushAndReturnBuffer()) + res + } + + @inline + final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = { + val res = f(newTyper(c)) + if (c.hasErrors) + context.issue(c.errBuffer.head) + res + } + + @inline + final def withSavedContext[T](c: Context)(f: => T) = { + val savedErrors = c.flushAndReturnBuffer() + val res = f + c.updateBuffer(savedErrors) + res + } + + /** The typer for a label definition. If this is part of a template we * first have to enter the label definition. */ @@ -573,9 +610,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { inferExprAlternative(tree, pt) val sym = tree.symbol - def fail() = errorTree(tree, sym.kindString + " " + sym.fullName + " is not a value") + def fail() = NotAValueError(tree, sym) - if (tree.tpe.isError) tree + if (tree.isErrorTyped) tree else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1) if (sym.isValue) checkStable(tree) else fail() @@ -629,15 +666,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def silent[T](op: Typer => T, - reportAmbiguousErrors: Boolean = context.reportAmbiguousErrors, - newtree: Tree = context.tree): Any /* in fact, TypeError or T */ = { + reportAmbiguousErrors: Boolean = context.ambiguousErrors, + newtree: Tree = context.tree): SilentResult[T] = { val rawTypeStart = startCounter(rawTypeFailed) val findMemberStart = startCounter(findMemberFailed) val subtypeStart = startCounter(subtypeFailed) val failedSilentStart = startTimer(failedSilentNanos) try { - if (context.reportGeneralErrors || - reportAmbiguousErrors != context.reportAmbiguousErrors || + if (context.reportErrors || + reportAmbiguousErrors != context.ambiguousErrors || newtree != context.tree) { val context1 = context.makeSilent(reportAmbiguousErrors, newtree) context1.undetparams = context.undetparams @@ -648,40 +685,29 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - result + if (context1.hasErrors) SilentTypeError(context1.errBuffer.head) + else SilentResultValue(result) } else { - op(this) + assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") + withSavedContext(context){ + val res = op(this) + val errorsToReport = context.flushAndReturnBuffer() + if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) + } } } catch { case ex: CyclicReference => throw ex case ex: TypeError => + // fallback in case TypeError is still thrown + // @H this happens for example in cps annotation checker stopCounter(rawTypeFailed, rawTypeStart) stopCounter(findMemberFailed, findMemberStart) stopCounter(subtypeFailed, subtypeStart) stopTimer(failedSilentNanos, failedSilentStart) - ex + SilentTypeError(TypeErrorWrapper(ex)) } } - /** Utility method: Try op1 on tree. If that gives an error try op2 instead. - */ - def tryBoth(tree: Tree)(op1: (Typer, Tree) => Tree)(op2: (Typer, Tree) => Tree): Tree = - silent(op1(_, tree)) match { - case result1: Tree => - result1 - case ex1: TypeError => - silent(op2(_, resetAllAttrs(tree))) match { - case result2: Tree => -// println("snd succeeded: "+result2) - result2 - case ex2: TypeError => - reportTypeError(tree.pos, ex1) - setError(tree) - } - } - - def isCodeType(tpe: Type) = tpe.typeSymbol isNonBottomSubClass CodeClass - /** Perform the following adaptations of expression, pattern or type `tree` wrt to * given mode `mode` and given prototype `pt`: * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do @@ -724,8 +750,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (context.undetparams nonEmpty) { // (9) -- should revisit dropped condition `(mode & POLYmode) == 0` // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition? - context.undetparams = - inferExprInstance(tree, context.extractUndetparams(), pt, + context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, // approximate types that depend on arguments since dependency on implicit argument is like dependency on type parameter mt.approximate, // if we are looking for a manifest, instantiate type to Nothing anyway, @@ -736,18 +761,28 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { useWeaklyCompatible = true) // #3808 } - val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree)) - if (original != EmptyTree && pt != WildcardType) - typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match { - case result: Tree => result - case ex: TypeError => - debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) - val tree1 = typed(resetAllAttrs(original), mode, WildcardType) - tree1.tpe = addAnnotations(tree1, tree1.tpe) - if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) - } - else - typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + // avoid throwing spurious DivergentImplicit errors + if (context.hasErrors) + return setError(tree) + + withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree)){ typer1 => + if (original != EmptyTree && pt != WildcardType) + typer1.silent(tpr => { + val withImplicitArgs = tpr.applyImplicitArgs(tree) + if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway + else tpr.typed(withImplicitArgs, mode, pt) + }) match { + case SilentResultValue(result) => + result + case _ => + debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) + val tree1 = typed(resetAllAttrs(original), mode, WildcardType) + tree1.tpe = addAnnotations(tree1, tree1.tpe) + if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) + } + else + typer1.typed(typer1.applyImplicitArgs(tree), mode, pt) + } } def instantiateToMethodType(mt: MethodType): Tree = { @@ -758,11 +793,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (!meth.isConstructor && !meth.isMacro && isFunctionType(pt)) { // (4.2) debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt) - checkParamsConvertible(tree.pos, tree.tpe) + checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree) // println("eta "+tree+" ---> "+tree0+":"+tree0.tpe+" undet: "+context.undetparams+ " mode: "+Integer.toHexString(mode)) - if (meth.typeParams.nonEmpty) { + if (context.undetparams.nonEmpty) { // #2624: need to infer type arguments for eta expansion of a polymorphic method // context.undetparams contains clones of meth.typeParams (fresh ones were generated in etaExpand) // need to run typer on tree0, since etaExpansion sets the tpe's of its subtrees to null @@ -775,9 +810,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else if (!meth.isConstructor && mt.params.isEmpty) { // (4.3) adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt, original) } else if (context.implicitsEnabled) { - errorTree(tree, "missing arguments for " + meth + meth.locationString + - (if (meth.isConstructor) "" - else ";\nfollow this method with `_' if you want to treat it as a partially applied function")) + MissingArgsForMethodTpeError(tree, meth) } else { setError(tree) } @@ -792,8 +825,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // or raw type (tree.symbol.isJavaDefined && context.unit.isJava), types must be of kind *, // and thus parameterized types must be applied to their type arguments // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't? - errorTree(tree, tree.symbol + " takes type parameters") - tree setType tree.tpe + MissingTypeParametersError(tree) } else if ( // (7.1) @M: check kind-arity // @M: removed check for tree.hasSymbol and replace tree.symbol by tree.tpe.symbol (TypeTree's must also be checked here, and they don't directly have a symbol) (inHKMode(mode)) && @@ -808,9 +840,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // Note that we treat Any and Nothing as kind-polymorphic. // We can't perform this check when typing type arguments to an overloaded method before the overload is resolved // (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1). - errorTree(tree, tree.tpe + " takes " + countElementsAsString(tree.tpe.typeParams.length, "type parameter") + - ", expected: " + countAsString(pt.typeParams.length)) - tree setType tree.tpe + KindArityMismatchError(tree, pt) } else tree match { // (6) case TypeTree() => tree case _ => TypeTree(tree.tpe) setOriginal (tree) setPos (tree.pos) @@ -836,12 +866,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tree } } else { - errorTree(tree, tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method") + CaseClassConstructorError(tree) } } def insertApply(): Tree = { - assert(!inHKMode(mode)) //@M + assert(!inHKMode(mode), modeString(mode)) //@M val qual = adaptToName(tree, nme.apply) match { case id @ Ident(_) => val pre = if (id.symbol.owner.isPackageClass) id.symbol.owner.thisType @@ -922,7 +952,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { applyPossible) insertApply() else if (!context.undetparams.isEmpty && !inPolyMode(mode)) { // (9) - assert(!inHKMode(mode)) //@M + assert(!inHKMode(mode), modeString(mode)) //@M if (inExprModeButNot(mode, FUNmode) && pt.typeSymbol == UnitClass) instantiateExpectingUnit(tree, mode) else @@ -960,7 +990,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!context.undetparams.isEmpty) { return instantiate(tree, mode, pt) } - if (context.implicitsEnabled && !tree.tpe.isError && !pt.isError) { + if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { // (14); the condition prevents chains of views debuglog("inferring view from " + tree.tpe + " to " + pt) val coercion = inferView(tree, tree.tpe, pt, true) @@ -977,8 +1007,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.echo(tree.pos, msg) debuglog(msg) - return newTyper(context.makeImplicit(context.reportAmbiguousErrors)).typed( + val silentContext = context.makeImplicit(context.ambiguousErrors) + val res = newTyper(silentContext).typed( new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) + if (silentContext.hasErrors) context.issue(silentContext.errBuffer.head) else return res } } } @@ -986,31 +1018,34 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { log("error tree = " + tree) if (settings.explaintypes.value) explainTypes(tree.tpe, pt) } - try { - typeErrorTree(tree, tree.tpe, pt) - } catch { - case ex: TypeError => - if (isPastTyper && pt.existentialSkolems.nonEmpty) { - // Ignore type errors raised in later phases that are due to mismatching types with existential skolems - // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. - // Here's my hypothsis why this happens. The pattern matcher defines a variable of type - // - // val x: T = expr - // - // where T is the type of expr, but T contains existential skolems ts. - // In that case, this value definition does not typecheck. - // The value definition - // - // val x: T forSome { ts } = expr - // - // would typecheck. Or one can simply leave out the type of the `val`: - // - // val x = expr - context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) - adapt(tree, mode, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) - } else - throw ex + + val found = tree.tpe + val req = pt + if (!found.isErroneous && !req.isErroneous) { + if (!context.reportErrors && isPastTyper && req.existentialSkolems.nonEmpty) { + // Ignore type errors raised in later phases that are due to mismatching types with existential skolems + // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. + // Here's my hypothsis why this happens. The pattern matcher defines a variable of type + // + // val x: T = expr + // + // where T is the type of expr, but T contains existential skolems ts. + // In that case, this value definition does not typecheck. + // The value definition + // + // val x: T forSome { ts } = expr + // + // would typecheck. Or one can simply leave out the type of the `val`: + // + // val x = expr + context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) + adapt(tree, mode, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) + } else { + // create an actual error + AdaptTypeError(tree, found, req) + } } + setError(tree) } } } @@ -1027,7 +1062,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def instantiateExpectingUnit(tree: Tree, mode: Int): Tree = { val savedUndetparams = context.undetparams silent(_.instantiate(tree, mode, UnitClass.tpe)) match { - case t: Tree => t + case SilentResultValue(t) => t case _ => context.undetparams = savedUndetparams val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant()))) @@ -1053,15 +1088,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // Note: implicit arguments are still inferred (this kind of "chaining" is allowed) ) } + + def adaptToMember(qual: Tree, searchTemplate: Type): Tree = + adaptToMember(qual, searchTemplate, true, true) + def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean): Tree = + adaptToMember(qual, searchTemplate, reportAmbiguous, true) - def adaptToMember(qual: Tree, searchTemplate: Type): Tree = { + def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { if (isAdaptableWithView(qual)) { qual.tpe.widen.normalize match { case et: ExistentialType => qual setType et.skolemizeExistential(context.owner, qual) // open the existential case _ => } - inferView(qual, qual.tpe, searchTemplate, true) match { + inferView(qual, qual.tpe, searchTemplate, reportAmbiguous, saveErrors) match { case EmptyTree => qual case coercion => if (settings.logImplicitConv.value) @@ -1083,13 +1123,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * If no conversion is found, return `qual` unchanged. * */ - def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type): Tree = { + def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") - adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe)) + adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) if (pt != WildcardType) { silent(_ => doAdapt(pt)) match { - case result: Tree if result != qual => + case SilentResultValue(result) if result != qual => result case _ => debuglog("fallback on implicits in adaptToArguments: "+qual+" . "+name) @@ -1099,30 +1139,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { doAdapt(pt) } - /** Try o apply an implicit conversion to `qual` to that it contains - * a method `name`. If that's ambiguous try taking arguments into account using `adaptToArguments`. + /** Try to apply an implicit conversion to `qual` so that it contains + * a method `name`. If that's ambiguous try taking arguments into + * account using `adaptToArguments`. */ - def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int): Tree = { - try { - adaptToMember(qual, HasMember(name)) - } catch { - case ex: TypeError => - // this happens if implicits are ambiguous; try again with more context info. - // println("last ditch effort: "+qual+" . "+name) + def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Int, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def onError(reportError: => Tree): Tree = { context.tree match { - case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => // try handling the arguments - // println("typing args: "+args) + case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => silent(_.typedArgs(args, mode)) match { - case args: List[_] => - adaptToArguments(qual, name, args.asInstanceOf[List[Tree]], WildcardType) - case _ => - throw ex + case SilentResultValue(xs) => + val args = xs.asInstanceOf[List[Tree]] + if (args exists (_.isErrorTyped)) + reportError + else + adaptToArguments(qual, name, args, WildcardType, reportAmbiguous, saveErrors) + case _ => + reportError } case _ => - // println("not in an apply: "+context.tree+"/"+tree) - throw ex + reportError } } + silent(_.adaptToMember(qual, HasMember(name), false)) match { + case SilentResultValue(res) => res + case SilentTypeError(err) => onError({if (reportAmbiguous) { context.issue(err) }; setError(tree)}) + } } /** Try to apply an implicit conversion to `qual` to that it contains a @@ -1162,7 +1204,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // If first parent is a trait, make it first mixin and add its superclass as first parent while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { val supertpt1 = typedType(supertpt) - if (!supertpt1.tpe.isError) { + if (!supertpt1.isErrorTyped) { mixins = supertpt1 :: mixins supertpt = TypeTree(supertpt1.tpe.parents.head) setPos supertpt.pos.focus } @@ -1201,19 +1243,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { }) val outercontext = context.outer - assert(clazz != NoSymbol) + assert(clazz != NoSymbol, templ) val cscope = outercontext.makeNewScope(constr, outercontext.owner) val cbody2 = newTyper(cscope) // called both during completion AND typing. .typePrimaryConstrBody(clazz, cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) + superCall match { case Apply(_, _) => val sarg = treeInfo.firstArgument(superCall) if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - error(sarg.pos, firstParent+" is a trait; does not take constructor arguments") - if (!supertparams.isEmpty) supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus + ConstrArgsInTraitParentTpeError(sarg, firstParent) + if (!supertparams.isEmpty) + supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) } val preSuperVals = treeInfo.preSuperFields(templ.body) @@ -1223,7 +1268,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) case _ => - if (!supertparams.isEmpty) error(supertpt.pos, "missing type arguments") + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) } /* experimental: early types as type arguments val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) @@ -1256,8 +1302,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } catch { case ex: TypeError => - templ.tpe = null - reportTypeError(templ.pos, ex) + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + ParentTypesError(templ, ex) List(TypeTree(AnyRefClass.tpe)) } @@ -1276,30 +1323,29 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * </ul> */ def validateParentClasses(parents: List[Tree], selfType: Type) { + val pending = ListBuffer[AbsTypeError]() def validateParentClass(parent: Tree, superclazz: Symbol) { - if (!parent.tpe.isError) { + if (!parent.isErrorTyped) { val psym = parent.tpe.typeSymbol.initialize checkClassType(parent, false, true) if (psym != superclazz) { if (psym.isTrait) { val ps = psym.info.parents if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol)) - error(parent.pos, "illegal inheritance; super"+superclazz+ - "\n is not a subclass of the super"+ps.head.typeSymbol+ - "\n of the mixin " + psym) + pending += ParentSuperSubclassError(parent, superclazz, ps.head.typeSymbol, psym) } else { - error(parent.pos, psym+" needs to be a trait to be mixed in") + pending += ParentNotATraitMixinError(parent, psym) } } - if (psym.isFinal) { - error(parent.pos, "illegal inheritance from final "+psym) - } + if (psym.isFinal) + pending += ParentFinalInheritanceError(parent, psym) + if (psym.isSealed && !phase.erasedTypes) { // AnyVal is sealed, but we have to let the value classes through manually if (context.unit.source.file == psym.sourceFile || isValueClass(context.owner)) psym addChild context.owner else - error(parent.pos, "illegal inheritance from sealed "+psym+": " + context.unit.source.file.canonicalPath + " != " + psym.sourceFile.canonicalPath) + pending += ParentSealedInheritanceError(parent, psym) } if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && @@ -1311,17 +1357,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { //Console.println(context.owner);//DEBUG //Console.println(context.owner.unsafeTypeParams);//DEBUG //Console.println(List.fromArray(context.owner.info.closure));//DEBUG - error(parent.pos, "illegal inheritance;\n self-type "+ - selfType+" does not conform to "+parent + - "'s selftype "+parent.tpe.typeOfThis) + pending += ParentSelfTypeConformanceError(parent, selfType) if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) } if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) - error(parent.pos, psym+" is inherited twice") + pending += ParentInheritedTwiceError(parent, psym) } } - - if (!parents.isEmpty && !parents.head.tpe.isError) + if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol) /* @@ -1331,13 +1374,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ", baseclasses = "+(context.owner.info.baseClasses map (_.fullName))+ ", lin = "+(context.owner.info.baseClasses map (context.owner.thisType.baseType))) */ + pending.foreach(ErrorUtils.issueTypeError) } def checkFinitary(classinfo: ClassInfoType) { val clazz = classinfo.typeSymbol + for (tparam <- clazz.typeParams) { if (classinfo.expansiveRefs(tparam) contains tparam) { - error(tparam.pos, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive") val newinfo = ClassInfoType( classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefClass.tpe))), classinfo.decls, @@ -1348,6 +1392,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => newinfo } } + FinitaryError(tparam) } } } @@ -1359,12 +1404,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedClassDef(cdef: ClassDef): Tree = { // attributes(cdef) val clazz = cdef.symbol - val typedMods = removeAnnotations(cdef.mods) - assert(clazz != NoSymbol) + val typedMods = typedModifiers(cdef.mods) + assert(clazz != NoSymbol, cdef) reenterTypeParams(cdef.tparams) val tparams1 = cdef.tparams mapConserve (typedTypeDef) - val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope)) - .typedTemplate(cdef.impl, parentTypes(cdef.impl)) + val impl1 = typerReportAnyContextErrors(context.make(cdef.impl, clazz, newScope)) { + _.typedTemplate(cdef.impl, parentTypes(cdef.impl)) + } val impl2 = finishMethodSynthesis(impl1, clazz, context) if ((clazz != ClassfileAnnotationClass) && (clazz isNonBottomSubClass ClassfileAnnotationClass)) @@ -1395,19 +1441,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { linkedClass.info.decl(nme.CONSTRUCTOR).alternatives foreach (_.initialize) val clazz = mdef.symbol.moduleClass - val typedMods = removeAnnotations(mdef.mods) + val typedMods = typedModifiers(mdef.mods) assert(clazz != NoSymbol, mdef) - - val typer0 = newTyper(context.make(mdef.impl, clazz, new Scope)) - val impl1 = typer0.typedTemplate(mdef.impl, { - parentTypes(mdef.impl) ++ ( - if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) Nil - else { - clazz.makeSerializable() - List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus) - } - ) - }) + val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) { + _.typedTemplate(mdef.impl, { + parentTypes(mdef.impl) ++ ( + if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) Nil + else { + clazz.makeSerializable() + List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus) + } + ) + }) + } val impl2 = finishMethodSynthesis(impl1, clazz, context) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType @@ -1495,8 +1541,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { /** Remove definition annotations from modifiers (they have been saved * into the symbol's ``annotations'' in the type completer / namer) + * + * However reification does need annotation definitions to proceed. + * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case. + * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations + * that involve locally defined annotations. See more about that in Reifiers.scala. + * + * That's why the original tree gets saved into ``original'' field of AnnotationInfo (happens elsewhere). + * The field doesn't get pickled/unpickled and exists only during a single compilation run. + * This simultaneously allows us to reify annotations and to preserve backward compatibility. */ - def removeAnnotations(mods: Modifiers): Modifiers = + def typedModifiers(mods: Modifiers): Modifiers = mods.copy(annotations = Nil) setPositions mods.positions /** @@ -1507,7 +1562,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // attributes(vdef) val sym = vdef.symbol.initialize val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) - val typedMods = removeAnnotations(vdef.mods) + val typedMods = typedModifiers(vdef.mods) // complete lazy annotations val annots = sym.annotations @@ -1516,14 +1571,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym.hasAnnotation(definitions.VolatileAttr)) { if (!sym.isMutable) - error(vdef.pos, "values cannot be volatile") + VolatileValueError(vdef) else if (sym.isFinal) - error(vdef.pos, "final vars cannot be volatile") + FinalVolatileVarError(vdef) } val rhs1 = if (vdef.rhs.isEmpty) { if (sym.isVariable && sym.owner.isTerm && !isPastTyper) - error(vdef.pos, "local variables must be initialized") + LocalVarUninitializedError(vdef) vdef.rhs } else { val tpt2 = if (sym.hasDefault) { @@ -1569,18 +1624,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (call, List()) } val (superConstr, superArgs) = decompose(rhs) - assert(superConstr.symbol ne null)//debug + assert(superConstr.symbol ne null, superConstr)//debug + val pending = ListBuffer[AbsTypeError]() // an object cannot be allowed to pass a reference to itself to a superconstructor // because of initialization issues; bug #473 for (arg <- superArgs ; tree <- arg) { val sym = tree.symbol if (sym != null && (sym.info.baseClasses contains clazz)) { if (sym.isModule) - error(tree.pos, "super constructor cannot be passed a self reference unless parameter is declared by-name") + pending += SuperConstrReferenceError(tree) tree match { case This(qual) => - error(tree.pos, "super constructor arguments cannot reference unconstructed `this`") + pending += SuperConstrArgsThisReferenceError(tree) case _ => () } } @@ -1613,6 +1669,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } } + pending.foreach(ErrorUtils.issueTypeError) } /** Check if a structurally defined method violates implementation restrictions. @@ -1661,7 +1718,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { lookupVariable(name.toString.substring(1), enclClass) match { case Some(repl) => silent(_.typedTypeConstructor(stringParser(repl).typ())) match { - case tpt: Tree => + case SilentResultValue(tpt) => val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) @@ -1711,19 +1768,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) if (isRepeatedParamType(vparam1.symbol.tpe)) - error(vparam1.pos, "*-parameter must come last") + StarParamNotLastError(vparam1) var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt)) checkNonCyclic(ddef, tpt1) ddef.tpt.setType(tpt1.tpe) - val typedMods = removeAnnotations(ddef.mods) + val typedMods = typedModifiers(ddef.mods) var rhs1 = if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors if (!meth.isPrimaryConstructor && (!meth.owner.isClass || meth.owner.isModuleClass || meth.owner.isAnonOrRefinementClass)) - error(ddef.pos, "constructor definition not allowed here") + InvalidConstructorDefError(ddef) typed(ddef.rhs) } else if (meth.isMacro) { EmptyTree @@ -1738,30 +1795,26 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!isPastTyper && meth.owner.isClass && meth.paramss.exists(ps => ps.exists(_.hasDefaultFlag) && isRepeatedParamType(ps.last.tpe))) - error(meth.pos, "a parameter section with a `*'-parameter is not allowed to have default arguments") + StarWithDefaultError(meth) if (!isPastTyper) { val allParams = meth.paramss.flatten for (p <- allParams) { for (n <- p.deprecatedParamName) { if (allParams.exists(p1 => p1.name == n || (p != p1 && p1.deprecatedParamName.exists(_ == n)))) - error(p.pos, "deprecated parameter name "+ n +" has to be distinct from any other parameter name (deprecated or not).") + DeprecatedParamNameError(p, n) } } } if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) - treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } - def typedTypeDef(tdef: TypeDef): TypeDef = { - def typeDefTyper = { - if(tdef.tparams isEmpty) Typer.this - else newTyper(context.makeNewScope(tdef, tdef.symbol)) + def typedTypeDef(tdef: TypeDef): TypeDef = + typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty){ + _.typedTypeDef0(tdef) } - typeDefTyper.typedTypeDef0(tdef) - } // call typedTypeDef instead // a TypeDef with type parameters must always be type checked in a new scope @@ -1769,7 +1822,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tdef.symbol.initialize reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef - val typedMods = removeAnnotations(tdef.mods) + val typedMods = typedModifiers(tdef.mods) // complete lazy annotations val annots = tdef.symbol.annotations @@ -1783,10 +1836,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { checkNonCyclic(tdef.symbol) if (tdef.symbol.owner.isType) rhs1.tpe match { - case TypeBounds(lo1, hi1) => - if (!(lo1 <:< hi1)) - error(tdef.pos, "lower bound "+lo1+" does not conform to upper bound "+hi1) - case _ => + case TypeBounds(lo1, hi1) if (!(lo1 <:< hi1)) => LowerBoundError(tdef, lo1, hi1) + case _ => () } treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType } @@ -1906,9 +1957,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = { // verify no _* except in last position for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x) - error(x.pos, "_* may only come last") + StarPositionInPatternError(x) - val pat1: Tree = typedPattern(cdef.pat, pattpe) + val pat1 = typedPattern(cdef.pat, pattpe) // When case classes have more than two parameter lists, the pattern ends // up typed as a method. We only pattern match on the first parameter // list, so substitute the final result type of the method, i.e. the type @@ -1949,10 +2000,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ def typedFunction(fun: Function, mode: Int, pt: Type): Tree = { val numVparams = fun.vparams.length - val codeExpected = !forMSIL && (pt.typeSymbol isNonBottomSubClass CodeClass) - if (numVparams > definitions.MaxFunctionArity) - return errorTree(fun, "implementation restricts functions to " + definitions.MaxFunctionArity + " parameters") + return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = if ((isFunctionType(pt) @@ -1967,10 +2016,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) - val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt) - + val (clazz, argpts, respt) = decompose(pt) if (argpts.lengthCompare(numVparams) != 0) - errorTree(fun, "wrong number of parameters; expected = " + argpts.length) + WrongNumberOfParametersError(fun, argpts) else { val vparamSyms = map2(fun.vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { @@ -1978,9 +2026,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (isFullyDefined(argpt)) argpt else { fun match { - case etaExpansion(vparams, fn, args) if !codeExpected => + case etaExpansion(vparams, fn, args) => silent(_.typed(fn, forFunMode(mode), pt)) match { - case fn1: Tree if context.undetparams.isEmpty => + case SilentResultValue(fn1) if context.undetparams.isEmpty => // if context,undetparams is not empty, the function was polymorphic, // so we need the missing arguments to infer its type. See #871 //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) @@ -1991,7 +2039,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } case _ => } - error(vparam.pos, missingParameterTypeMsg(fun, vparam, pt)) + MissingParameterTypeError(fun, vparam, pt) ErrorType } if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus @@ -2005,18 +2053,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // for (vparam <- vparams) { // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () // } - var body = typed(fun.body, respt) + val body1 = typed(fun.body, respt) val formals = vparamSyms map (_.tpe) - val restpe = packedType(body, fun.symbol).deconst.resultType + val restpe = packedType(body1, fun.symbol).deconst.resultType val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) // body = checkNoEscaping.locals(context.scope, restpe, body) - val fun1 = treeCopy.Function(fun, vparams, body).setType(funtpe) - if (codeExpected) lifted(fun1) else fun1 - } + treeCopy.Function(fun, vparams, body1).setType(funtpe) } - - def lifted(tree: Tree): Tree = typedPos(tree.pos) { - Apply(Select(Ident(CodeModule), nme.lift_), List(tree)) } def typedRefinement(stats: List[Tree]) { @@ -2025,7 +2068,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.toCheck += { () => // go to next outer context which is not silent, see #3614 var c = context - while (!c.reportGeneralErrors) c = c.outer + while (c.bufferErrors) c = c.outer val stats1 = newTyper(c).typedStats(stats, NoSymbol) for (stat <- stats1 if stat.isDef) { val member = stat.symbol @@ -2041,11 +2084,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Some(imp1: Import) => imp1 case None => log("unhandled import: "+imp+" in "+unit); imp } - private def isWarnablePureExpression(tree: Tree) = tree match { case EmptyTree | Literal(Constant(())) => false case _ => - (treeInfo isExprSafeToInline tree) && { + !tree.isErrorTyped && (treeInfo isExprSafeToInline tree) && { val sym = tree.symbol (sym == null) || !(sym.isModule || sym.isLazy) || { debuglog("'Pure' but side-effecting expression in statement position: " + tree) @@ -2059,9 +2101,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def includesTargetPos(tree: Tree) = tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos) val localTarget = stats exists includesTargetPos + val statsErrors = scala.collection.mutable.LinkedHashSet[AbsTypeError]() def typedStat(stat: Tree): Tree = { if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat)) - errorTree(stat, "only declarations allowed here") + OnlyDeclarationsError(stat) else stat match { case imp @ Import(_, _) => @@ -2074,20 +2117,25 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // the targetposition stat } else { - val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this - else newTyper(context.make(stat, exprOwner)) + val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { + context.flushBuffer() + this + } else newTyper(context.make(stat, exprOwner)) // XXX this creates a spurious dead code warning if an exception is thrown // in a constructor, even if it is the only thing in the constructor. val result = checkDead(localTyper.typed(stat, EXPRmode | BYVALmode, WildcardType)) + if (treeInfo.isSelfOrSuperConstrCall(result)) { context.inConstructorSuffix = true if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) - error(stat.pos, "called constructor's definition must precede calling constructor's definition") + ConstructorsOrderError(stat) } + if (isWarnablePureExpression(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) + statsErrors ++= localTyper.context.errBuffer result } } @@ -2121,9 +2169,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // error for this is issued in RefChecks.checkDefaultsInOverloaded if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag && !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { - error(e.sym.pos, e1.sym+" is defined twice"+ - {if(!settings.debug.value) "" else " in "+unit.toString}+ - {if (e.sym.isMacro && e1.sym.isMacro) " \n(note that macros cannot be overloaded)" else ""}) + DefDefinedTwiceError(e.sym, e1.sym) scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } e1 = scope.lookupNextEntry(e1) @@ -2168,14 +2214,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { }) ::: newStats.toList } } - val result = stats mapConserve typedStat - if (phase.erasedTypes) result - else checkNoDoubleDefsAndAddSynthetics(result) + + val stats1 = withSavedContext(context) { + val result = stats mapConserve typedStat + context.flushBuffer() + result + } + context.updateBuffer(statsErrors) + if (phase.erasedTypes) stats1 + else checkNoDoubleDefsAndAddSynthetics(stats1) } def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { val typedMode = onlyStickyModes(mode) | newmode - val t = constrTyperIf((mode & SCCmode) != 0).typed(arg, typedMode, pt) + val t = withCondConstrTyper((mode & SCCmode) != 0)(_.typed(arg, typedMode, pt)) checkDead.inMode(typedMode, t) } @@ -2244,8 +2296,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty - def errTree = setError(treeCopy.Apply(tree, fun0, args)) - def errorTree(msg: String) = { error(tree.pos, msg); errTree } + def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) + def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } var fun = fun0 if (fun.hasSymbol && fun.symbol.isOverloaded) { @@ -2308,8 +2360,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { arg1 } context.undetparams = undetparams - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) - doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + if (context.hasErrors) + setError(tree) + else { + inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) + doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + } case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes @@ -2329,7 +2385,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) match { - case t: Tree => + case SilentResultValue(t) => // Depending on user options, may warn or error here if // a Unit or tuple was inserted. Some(t) filter (tupledTree => @@ -2337,7 +2393,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { || tupledTree.symbol == null || checkValidAdaptation(tupledTree, args) ) - case ex => + case _ => context.undetparams = savedUndetparams None } @@ -2352,21 +2408,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) - if (mt.isErroneous) errTree - else if (inPatternMode(mode)) + if (mt.isErroneous) duplErrTree + else if (inPatternMode(mode)) { // #2064 - errorTree("wrong number of arguments for "+ treeSymTypeMsg(fun)) - else if (lencmp > 0) { - tryTupleApply getOrElse errorTree("too many arguments for "+treeSymTypeMsg(fun)) + duplErrorTree(WrongNumberOfArgsError(tree, fun)) + } else if (lencmp > 0) { + tryTupleApply getOrElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun)) } else if (lencmp == 0) { // we don't need defaults. names were used, so this application is transformed // into a block (@see transformNamedApplication in NamesDefaults) val (namelessArgs, argPos) = removeNames(Typer.this)(args, params) if (namelessArgs exists (_.isErroneous)) { - errTree + duplErrTree } else if (!isIdentity(argPos) && !sameLength(formals, params)) // !isIdentity indicates that named arguments are used to re-order arguments - errorTree("when using named arguments, the vararg parameter has to be specified exactly once") + duplErrorTree(MultipleVarargError(tree)) else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) { // if there's no re-ordering, and fun is not transformed, no need to transform // more than an optimization, e.g. important in "synchronized { x = update-x }" @@ -2380,7 +2436,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) - if (fun1.isErroneous) errTree + if (fun1.isErroneous) duplErrTree else { assert(isNamedApplyBlock(fun1), fun1) val NamedApplyInfo(qual, targs, previousArgss, _) = context.namedApplyBlockInfo.get._2 @@ -2397,17 +2453,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val lencmp2 = compareLengths(allArgs, formals) if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) { - errorTree("module extending its companion class cannot use default constructor arguments") + duplErrorTree(ModuleUsingCompanionClassDefaultArgsErrror(tree)) } else if (lencmp2 > 0) { removeNames(Typer.this)(allArgs, params) // #3818 - errTree + duplErrTree } else if (lencmp2 == 0) { // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() val note = "Error occurred in an application involving default arguments." if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { - tryTupleApply getOrElse errorTree(notEnoughArgumentsMsg(fun, missing)) + tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) } } } @@ -2466,13 +2522,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { atPos(tree.pos)(gen.mkNil setType restpe) else constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe)) - } else if (needsInstantiation(tparams, formals, args)) { //println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info))) inferExprInstance(fun, tparams) doTypedApply(tree, fun, args, mode, pt) } else { - assert(!inPatternMode(mode)) // this case cannot arise for patterns + assert(!inPatternMode(mode), modeString(mode)) // this case cannot arise for patterns val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) val strictTargs = map2(lenientTargs, tparams)((targ, tparam) => if (targ == WildcardType) tparam.tpe else targ) //@M TODO: should probably be .tpeHK @@ -2488,11 +2543,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!argtparams.isEmpty) { val strictPt = formal.instantiateTypeParams(tparams, strictTargs) inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) - } - arg1 + arg1 + } else arg1 } val args1 = map2(args, formals)(typedArgToPoly) - if (args1 exists (_.tpe.isError)) errTree + if (args1 exists {_.isErrorTyped}) duplErrTree else { debuglog("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" @@ -2509,12 +2564,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { doTypedApply(tree, fun setType fun.tpe.widen, args, mode, pt) case ErrorType => - setError(treeCopy.Apply(tree, fun, args)) + if (!tree.isErrorTyped) setError(tree) else tree + // @H change to setError(treeCopy.Apply(tree, fun, args)) /* --- begin unapply --- */ case otpe if inPatternMode(mode) && unapplyMember(otpe).exists => if (args.length > MaxTupleArity) - error(fun.pos, "too many arguments for unapply pattern, maximum = "+MaxTupleArity) + return duplErrorTree(TooManyArgsPatternError(fun)) // def freshArgType(tp: Type): (List[Symbol], Type) = tp match { @@ -2522,11 +2578,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (Nil, param.tpe) case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) + // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) case OverloadedType(_, _) => - error(fun.pos, "cannot resolve overloaded unapply") + OverloadedUnapplyError(fun) (Nil, ErrorType) case _ => - error(fun.pos, "an unapply method must accept a single argument.") + UnapplyWithSingleArgError(fun) (Nil, ErrorType) } @@ -2542,7 +2599,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { freeVars foreach unapplyContext.scope.enter val typer1 = newTyper(unapplyContext) - val pattp = typer1.infer.inferTypedPattern(tree.pos, unappFormal, arg.tpe) + val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe) // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) @@ -2553,8 +2610,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // setType null is necessary so that ref will be stabilized; see bug 881 val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg))) - if (fun1.tpe.isErroneous) errTree - else { + if (fun1.tpe.isErroneous) { + duplErrTree + } else { val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) val formals1 = formalTypes(formals0, args.length) if (sameLength(formals1, args)) { @@ -2567,15 +2625,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val itype = glb(List(pt1, arg.tpe)) arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) UnApply(fun1, args1) setPos tree.pos setType itype - } - else { - errorTree("wrong number of arguments for "+treeSymTypeMsg(fun)) - } + } else + duplErrorTree(WrongNumberArgsPatternError(tree, fun)) } /* --- end unapply --- */ case _ => - errorTree(fun.tpe+" does not take parameters") + duplErrorTree(ApplyWithoutArgsError(tree, fun)) } } @@ -2587,8 +2643,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedAnnotation(ann: Tree, mode: Int = EXPRmode, selfsym: Symbol = NoSymbol, annClass: Symbol = AnnotationClass, requireJava: Boolean = false): AnnotationInfo = { lazy val annotationError = AnnotationInfo(ErrorType, Nil, Nil) var hasError: Boolean = false - def error(pos: Position, msg: String) = { - context.error(pos, msg) + val pending = ListBuffer[AbsTypeError]() + + def reportAnnotationError(err: AbsTypeError) = { + pending += err hasError = true annotationError } @@ -2604,13 +2662,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case tpe => null } } - def fail(msg: String) = { error(tr.pos, msg) ; None } - if (const == null) - fail("annotation argument needs to be a constant; found: " + tr) - else if (const.value == null) - fail("annotation argument cannot be null") - else + if (const == null) { + reportAnnotationError(AnnotationNotAConstantError(tr)); None + } else if (const.value == null) { + reportAnnotationError(AnnotationArgNullError(tr)); None + } else Some(LiteralAnnotArg(const)) } @@ -2619,16 +2676,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ def tree2ConstArg(tree: Tree, pt: Type): Option[ClassfileAnnotArg] = tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) if (pt.typeSymbol == ArrayClass) => - error(tree.pos, "Array constants have to be specified using the `Array(...)' factory method") - None + reportAnnotationError(ArrayConstantsError(tree)); None case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => val annInfo = typedAnnotation(ann, mode, NoSymbol, pt.typeSymbol, true) - if (annInfo.atp.isErroneous) { - // recursive typedAnnotation call already printed an error, so don't call "error" - hasError = true - None - } else Some(NestedAnnotArg(annInfo)) + if (annInfo.atp.isErroneous) { hasError = true; None } + else Some(NestedAnnotArg(annInfo)) // use of Array.apply[T: ClassManifest](xs: T*): Array[T] // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar) @@ -2643,13 +2696,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // BT = Int, .., String, Class[_], JavaAnnotClass // T = BT | Array[BT] // So an array literal as argument can only be valid if pt is Array[_] - error(tree.pos, "found array constant, expected argument of type "+ pt) + reportAnnotationError(ArrayConstantsTypeMismatchError(tree, pt)) None } - else - tryConst(tree, pt) + else tryConst(tree, pt) - case Typed(t, _) => tree2ConstArg(t, pt) + case Typed(t, _) => + tree2ConstArg(t, pt) case tree => tryConst(tree, pt) @@ -2669,13 +2722,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Select(New(tpt), nme.CONSTRUCTOR) => (fun, outerArgss) case _ => - error(fun.pos, "unexpected tree in annotation: "+ fun) + reportAnnotationError(UnexpectedTreeAnnotation(fun)) (setError(fun), outerArgss) } extract(ann, List()) } - if (fun.isErroneous) annotationError + val res = if (fun.isErroneous) annotationError else { val typedFun @ Select(New(tpt), _) = typed(fun, forFunMode(mode), WildcardType) val annType = tpt.tpe @@ -2685,9 +2738,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // annotation to be saved as java classfile annotation val isJava = typedFun.symbol.owner.isJavaDefined if (!annType.typeSymbol.isNonBottomSubClass(annClass)) { - error(tpt.pos, "expected annotation of type "+ annClass.tpe +", found "+ annType) + reportAnnotationError(AnnotationTypeMismatchError(tpt, annClass.tpe, annType)) } else if (argss.length > 1) { - error(ann.pos, "multiple argument lists on classfile annotation") + reportAnnotationError(MultipleArgumentListForAnnotationError(ann)) } else { val args = if (argss.head.length == 1 && !isNamed(argss.head.head)) @@ -2703,10 +2756,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val sym = if (isJava) annScope.lookup(name) else typedFun.tpe.params.find(p => p.name == name).getOrElse(NoSymbol) if (sym == NoSymbol) { - error(arg.pos, "unknown annotation argument name: " + name) + reportAnnotationError(UnknownAnnotationNameError(arg, name)) (nme.ERROR, None) } else if (!names.contains(sym)) { - error(arg.pos, "duplicate value for annotation argument " + name) + reportAnnotationError(DuplicateValueAnnotationError(arg, name)) (nme.ERROR, None) } else { names -= sym @@ -2715,21 +2768,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (sym.name, annArg) } case arg => - error(arg.pos, "classfile annotation arguments have to be supplied as named arguments") + reportAnnotationError(ClassfileAnnotationsAsNamedArgsError(arg)) (nme.ERROR, None) } for (sym <- names) { // make sure the flags are up to date before erroring (jvm/t3415 fails otherwise) sym.initialize if (!sym.hasAnnotation(AnnotationDefaultAttr) && !sym.hasDefaultFlag) - error(ann.pos, "annotation " + annType.typeSymbol.fullName + " is missing argument " + sym.name) + reportAnnotationError(AnnotationMissingArgError(ann, annType, sym)) } if (hasError) annotationError - else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos) + else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos) } } else if (requireJava) { - error(ann.pos, "nested classfile annotations must be defined in java; found: "+ annType) + reportAnnotationError(NestedAnnotationError(ann, annType)) } else { val typedAnn = if (selfsym == NoSymbol) { typed(ann, mode, annClass.tpe) @@ -2766,7 +2819,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def annInfo(t: Tree): AnnotationInfo = t match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => - AnnotationInfo(annType, args, List()).setPos(t.pos) + AnnotationInfo(annType, args, List()).setOriginal(ann).setPos(t.pos) case Block(stats, expr) => context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+ @@ -2781,7 +2834,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { annInfo(fun) case _ => - error(t.pos, "unexpected tree after typing annotation: "+ typedAnn) + reportAnnotationError(UnexpectedTreeAnnotationError(t, typedAnn)) } if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) @@ -2791,11 +2844,43 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else annInfo(typedAnn) } } + + if (hasError) { + pending.foreach(ErrorUtils.issueTypeError) + annotationError + } else res } def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? sym.isTypeParameter && sym.owner.isJavaDefined + + /** If we map a set of hidden symbols to their existential bounds, we + * have a problem: the bounds may themselves contain references to the + * hidden symbols. So this recursively calls existentialBound until + * the typeSymbol is not amongst the symbols being hidden. + */ + def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = { + def safeBound(t: Type): Type = + if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t + + def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match { + case tp @ RefinedType(parents, decls) => + val parents1 = parents mapConserve safeBound + if (parents eq parents1) tp + else copyRefinedType(tp, parents1, decls) + case tp => tp + } + (hidden map { s => + // Hanging onto lower bound in case anything interesting + // happens with it. + (s, s.existentialBound match { + case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) + case _ => hiBound(s) + }) + }).toMap + } + /** Given a set `rawSyms` of term- and type-symbols, and a type * `tp`, produce a set of fresh type parameters and a type so that * it can be abstracted to an existential type. Every type symbol @@ -2813,12 +2898,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * only the type of the Ident is changed. */ protected def existentialTransform[T](rawSyms: List[Symbol], tp: Type)(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 => newTypeName(x + ".type") + case x => nme.singletonName(x) } - val bound = sym.existentialBound + val bound = allBounds(sym) val sowner = if (isRawParameter(sym)) context.owner else sym.owner val quantified = sowner.newExistential(name, sym.pos) @@ -2863,7 +2949,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { existentialAbstraction(captured.toList, tpe) } - /** convert skolems to existentials */ + /** convert local symbols and skolems to existentials */ def packedType(tree: Tree, owner: Symbol): Type = { def defines(tree: Tree, sym: Symbol) = sym.isExistentialSkolem && sym.unpackLocation == tree || @@ -2890,7 +2976,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym.isAliasType && containsLocal(tp)) apply(tp.normalize) else { if (pre.isVolatile) - context.error(tree.pos, "Inferred type "+tree.tpe+" contains type selection from volatile type "+pre) + InferTypeWithVolatileTypeSelectionError(tree, pre) mapOver(tp) } case _ => @@ -2907,8 +2993,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { localSyms += sym remainingSyms += sym } else { - unit.error(tree.pos, - "can't existentially abstract over parameterized type " + tp) + AbstractExistentiallyOverParamerizedTpeError(tree, tp) } } } @@ -2977,10 +3062,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { new DeSkolemizeMap mapOver tp } - def typedClassOf(tree: Tree, tpt: Tree) = { - checkClassType(tpt, true, false) - atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) - } + def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) = + if (!checkClassType(tpt, true, false) && noGen) tpt + else atPos(tree.pos)(gen.mkClassOf(tpt.tpe)) protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Int): Tree = { for (wc <- tree.whereClauses) @@ -2989,7 +3073,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val whereClauses1 = typedStats(tree.whereClauses, context.owner) for (vd @ ValDef(_, _, _, _) <- tree.whereClauses) if (vd.symbol.tpe.isVolatile) - error(vd.pos, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + AbstractionFromVolatileTypeError(vd) val tpt1 = typedType(tree.tpt, mode) existentialTransform(tree.whereClauses map (_.symbol), tpt1.tpe)((tparams, tp) => TypeTree(newExistentialType(tparams, tp)) setOriginal tree @@ -3012,7 +3096,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // Martin, I'm using fake trees, because, if you use args or arg.map(typedType), // inferPolyAlternatives loops... -- I have no idea why :-( // ...actually this was looping anyway, see bug #278. - return errorTree(fun, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + return TypedApplyWrongNumberOfTpeParametersError(fun, fun) typedTypeApply(tree, mode, fun, args1) case SingleType(_, _) => @@ -3020,12 +3104,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case PolyType(tparams, restpe) if tparams.nonEmpty => if (sameLength(tparams, args)) { val targs = args map (_.tpe) - checkBounds(tree.pos, NoPrefix, NoSymbol, tparams, targs, "") + checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "") if (fun.symbol == Predef_classOf) - typedClassOf(tree, args.head) + typedClassOf(tree, args.head, true) else { if (!isPastTyper && fun.symbol == Any_isInstanceOf && !targs.isEmpty) - checkCheckable(tree.pos, targs.head, "") + checkCheckable(tree, targs.head, "") val resultpe = restpe.instantiateTypeParams(tparams, targs) //@M substitution in instantiateParams needs to be careful! //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int] @@ -3036,12 +3120,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.TypeApply(tree, fun, args) setType resultpe } } else { - errorTree(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + TypedApplyWrongNumberOfTpeParametersError(tree, fun) } case ErrorType => setError(treeCopy.TypeApply(tree, fun, args)) case _ => - errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.") + TypedApplyDoesNotTakeTpeParametersError(tree, fun) } @inline final def deindentTyping() = context.typingIndentLevel -= 2 @@ -3110,6 +3194,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // this annotation did not need it if (ainfo.isErroneous) + // Erroneous annotations were already reported in typedAnnotation arg1 // simply drop erroneous annotations else { ann.tpe = atype @@ -3149,7 +3234,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vble = context.owner.newValue(name, tree.pos) if (vble.name.toTermName != nme.WILDCARD) { if ((mode & ALTmode) != 0) - error(tree.pos, "illegal variable in pattern alternative") + VariableInPatternAlternativeError(tree) vble = namer.enterInScope(vble) } val body1 = typed(body, mode, pt) @@ -3176,18 +3261,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedAssign(lhs: Tree, rhs: Tree): Tree = { val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType) val varsym = lhs1.symbol - def failMsg = - if (varsym != null && varsym.isValue) "reassignment to val" - else "assignment to non variable" - def fail = { - if (!lhs1.tpe.isError) - error(tree.pos, failMsg) + // see #2494 for double error message example + def fail() = + if (lhs1.isErrorTyped) lhs1 + else AssignmentError(tree, varsym) - setError(tree) - } if (varsym == null) - return fail + return fail() if (treeInfo.mayBeVarGetter(varsym)) { treeInfo.methPart(lhs1) match { @@ -3203,7 +3284,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe } - else fail + else fail() } def typedIf(cond: Tree, thenp: Tree, elsep: Tree) = { @@ -3250,7 +3331,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val owntype = elimAnonymousClass(owntype0) if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype)) - (new MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { + (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { case Block(vd :: Nil, tree@Match(selector, cases)) => val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt) @@ -3273,12 +3354,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { enclMethod.owner.isConstructor || context.enclClass.enclMethod == enclMethod // i.e., we are in a constructor of a local class ) { - errorTree(tree, "return outside method definition") + ReturnOutsideOfDefError(tree) } else { val DefDef(_, name, _, _, restpt, _) = enclMethod.tree - if (restpt.tpe eq null) - errorTree(tree, enclMethod.owner + " has return statement; needs result type") - else { + if (restpt.tpe eq null) { + ReturnWithoutTypeError(tree, enclMethod.owner) + } else { context.enclMethod.returnsSeen = true val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) // Warn about returning a value if no value can be returned. @@ -3297,12 +3378,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedNew(tpt: Tree) = { val tpt1 = { val tpt0 = typedTypeConstructor(tpt) - checkClassType(tpt0, false, true) - if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { - context.undetparams = cloneSymbols(tpt0.symbol.typeParams) - TypeTree().setOriginal(tpt0) - .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347. - } else tpt0 + if (checkClassType(tpt0, false, true)) + if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { + context.undetparams = cloneSymbols(tpt0.symbol.typeParams) + TypeTree().setOriginal(tpt0) + .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347. + } else tpt0 + else tpt0 } /** If current tree <tree> appears in <val x(: T)? = <tree>> @@ -3321,17 +3403,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tp = tpt1.tpe val sym = tp.typeSymbol.initialize if (sym.isAbstractType || sym.hasAbstractFlag) - error(tree.pos, sym + " is abstract; cannot be instantiated") + IsAbstractError(tree, sym) else if (!( tp == sym.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable // sym.thisSym.tpe == tp.typeOfThis (except for objects) || narrowRhs(tp) <:< tp.typeOfThis || phase.erasedTypes )) { - error(tree.pos, sym + - " cannot be instantiated because it does not conform to its self-type "+ - tp.typeOfThis) - } - treeCopy.New(tree, tpt1).setType(tp) + DoesNotConformToSelfTypeError(tree, sym, tp.typeOfThis) + } else + treeCopy.New(tree, tpt1).setType(tp) } def typedEta(expr1: Tree): Tree = expr1.tpe match { @@ -3369,22 +3449,28 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ErrorType => expr1 case _ => - errorTree(expr1, "_ must follow method; cannot follow " + expr1.tpe) + UnderscoreEtaError(expr1) } /** * @param args ... * @return ... */ - def tryTypedArgs(args: List[Tree], mode: Int, other: TypeError): List[Tree] = { + def tryTypedArgs(args: List[Tree], mode: Int): Option[List[Tree]] = { val c = context.makeSilent(false) c.retyping = true try { - newTyper(c).typedArgs(args, mode) + val res = newTyper(c).typedArgs(args, mode) + if (c.hasErrors) None else Some(res) } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => - null + case ex: CyclicReference => + throw ex + case te: TypeError => + // @H some of typer erros can still leak, + // for instance in continuations + None + } finally { + c.flushBuffer() } } @@ -3393,10 +3479,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { val start = startTimer(failedApplyNanos) - silent(_.doTypedApply(tree, fun, args, mode, pt)) match { - case t: Tree => - t - case ex: TypeError => + + def onError(typeError: AbsTypeError): Tree = { stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. @@ -3419,27 +3503,39 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) case _ => Nil }) - def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos) - val retry = fun :: tree :: args exists errorInResult + def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == typeError.errPos) + + val retry = (typeError.errPos != null) && (fun :: tree :: args exists errorInResult) printTyping { val funStr = ptTree(fun) + " and " + (args map ptTree mkString ", ") if (retry) "second try: " + funStr - else "no second try: " + funStr + " because error not in result: " + ex.pos+"!="+tree.pos + else "no second try: " + funStr + " because error not in result: " + typeError.errPos+"!="+tree.pos } if (retry) { val Select(qual, name) = fun - val args1 = tryTypedArgs(args, forArgMode(fun, mode), ex) - val qual1 = - if ((args1 ne null) && !pt.isError) adaptToArguments(qual, name, args1, pt) - else qual - if (qual1 ne qual) { - val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos - return typed1(tree1, mode | SNDTRYmode, pt) + tryTypedArgs(args, forArgMode(fun, mode)) match { + case Some(args1) => + assert((args1.length == 0) || !args1.head.tpe.isErroneous, "try typed args is ok") + val qual1 = + if (!pt.isError) adaptToArguments(qual, name, args1, pt, true, true) + else qual + if (qual1 ne qual) { + val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos + return typed1(tree1, mode | SNDTRYmode, pt) + } + case _ => () } } - reportTypeError(tree.pos, ex) + issue(typeError) setError(treeCopy.Apply(tree, fun, args)) } + + silent(_.doTypedApply(tree, fun, args, mode, pt)) match { + case SilentResultValue(t) => + t + case SilentTypeError(err) => + onError(err) + } } def typedApply(fun: Tree, args: List[Tree]) = { @@ -3451,10 +3547,28 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val funpt = if (isPatternMode) pt else WildcardType val appStart = startTimer(failedApplyNanos) val opeqStart = startTimer(failedOpEqNanos) + + def onError(reportError: => Tree): Tree = { + fun match { + case Select(qual, name) + if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => + val qual1 = typedQualifier(qual) + if (treeInfo.isVariableOrGetter(qual1)) { + stopTimer(failedOpEqNanos, opeqStart) + convertToAssignment(fun, qual1, name, args) + } else { + stopTimer(failedApplyNanos, appStart) + reportError + } + case _ => + stopTimer(failedApplyNanos, appStart) + reportError + } + } silent(_.typed(fun, forFunMode(mode), funpt), - if ((mode & EXPRmode) != 0) false else context.reportAmbiguousErrors, + if ((mode & EXPRmode) != 0) false else context.ambiguousErrors, if ((mode & EXPRmode) != 0) tree else context.tree) match { - case fun1: Tree => + case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 incCounter(typedApplyCount) def isImplicitMethod(tpe: Type) = tpe match { @@ -3481,7 +3595,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { //if (fun2.hasSymbol && fun2.symbol.name == nme.apply && fun2.symbol.owner == ArrayClass) { // But this causes cyclic reference for Array class in Cleanup. It is easy to overcome this // by calling ArrayClass.info here (or some other place before specialize). - if (fun2.symbol == Array_apply) { + if (fun2.symbol == Array_apply && !res.isErrorTyped) { val checked = gen.mkCheckInit(res) // this check is needed to avoid infinite recursion in Duplicators // (calling typed1 more than once for the same tree) @@ -3489,30 +3603,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else res } else res - case ex: TypeError => - fun match { - case Select(qual, name) - if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => - val qual1 = typedQualifier(qual) - if (treeInfo.isVariableOrGetter(qual1)) { - stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args, ex) - } - else { - stopTimer(failedApplyNanos, appStart) - reportTypeError(fun.pos, ex) - setError(tree) - } - case _ => - stopTimer(failedApplyNanos, appStart) - reportTypeError(fun.pos, ex) - setError(tree) - } + case SilentTypeError(err) => + onError({issue(err); setError(tree)}) } } } - def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree], ex: TypeError): Tree = { + def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { val prefix = name.subName(0, name.length - nme.EQL.length) def mkAssign(vble: Tree): Tree = Assign( @@ -3553,25 +3650,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Apply(fn, indices) => treeInfo.methPart(fn) match { case Select(table, nme.apply) => mkUpdate(table, indices) - case _ => errorTree(qual, "Unexpected tree during assignment conversion.") + case _ => UnexpectedTreeAssignmentConversionError(qual) } } typed1(tree1, mode, pt) -/* - debuglog("retry assign: "+tree1) - silent(_.typed1(tree1, mode, pt)) match { - case t: Tree => - t - case _ => - reportTypeError(tree.pos, ex) - setError(tree) - } -*/ } - def qualifyingClassSym(qual: Name): Symbol = - if (tree.symbol != NoSymbol) tree.symbol else qualifyingClass(tree, qual, false) - def typedSuper(qual: Tree, mix: TypeName) = { val qual1 = typed(qual) @@ -3596,12 +3680,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // println(mix) // the reference to super class got lost during erasure restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class") + ErrorType } else { - error(tree.pos, mix+" does not name a parent class of "+clazz) + MixinMissingParentClassNameError(tree, mix, clazz) + ErrorType } - ErrorType } else if (!ps.tail.isEmpty) { - error(tree.pos, "ambiguous parent class qualifier") + AmbiguousParentClassError(tree) ErrorType } else { ps.head @@ -3618,16 +3703,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { findMixinSuper(clazz.tpe) } - treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) - } + treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) + } def typedThis(qual: Name) = { - val clazz = qualifyingClassSym(qual) - if (clazz == NoSymbol) setError(tree) - else { - tree setSymbol clazz setType clazz.thisType.underlying - if (isStableContext(tree, mode, pt)) tree setType clazz.thisType - tree + val qualifyingClassSym = if (tree.symbol != NoSymbol) Some(tree.symbol) else qualifyingClass(tree, qual) + qualifyingClassSym match { + case Some(clazz) => + tree setSymbol clazz setType clazz.thisType.underlying + if (isStableContext(tree, mode, pt)) tree setType clazz.thisType + tree + case None => tree } } @@ -3659,12 +3745,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) { val qual1 = if (member(qual, name) != NoSymbol) qual - else adaptToMemberWithArgs(tree, qual, name, mode) - if (qual1 ne qual) return typed(treeCopy.Select(tree, qual1, name), mode, pt) - } + else adaptToMemberWithArgs(tree, qual, name, mode, true, true) + if (qual1 ne qual) + return typed(treeCopy.Select(tree, qual1, name), mode, pt) + } if (!reallyExists(sym)) { - if (context.owner.toplevelClass.isJavaDefined && name.isTypeName) { + if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } if (tree1 != EmptyTree) return typed1(tree1, mode, pt) } @@ -3691,7 +3778,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ) } - def makeErrorTree = { + def makeInteractiveErrorTree = { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) @@ -3700,24 +3787,31 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (name == nme.ERROR && forInteractive) - return makeErrorTree + return makeInteractiveErrorTree if (!qual.tpe.widen.isErroneous) { if ((mode & QUALmode) != 0) { val lastTry = missingHook(qual.tpe.typeSymbol, name) if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) } - notAMemberError(tree.pos, qual, name) + NotAMemberError(tree, qual, name) } - if (forInteractive) makeErrorTree else setError(tree) + if (forInteractive) makeInteractiveErrorTree else setError(tree) } else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) } - val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual) - val result = stabilize(tree2, pre2, mode, pt) + val (result, accessibleError) = silent(_.makeAccessible(tree1, sym, qual.tpe, qual)) match { + case SilentTypeError(err) => + if (err.kind != ErrorKinds.Access) { + context issue err + return setError(tree) + } else (tree1, Some(err)) + case SilentResultValue(treeAndPre) => + (stabilize(treeAndPre._1, treeAndPre._2, mode, pt), None) + } def isPotentialNullDeference() = { !isPastTyper && @@ -3736,16 +3830,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { result, (TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect // will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one? - checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") + checkBounds(qual, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "") qual // you only get to see the wrapped tree after running this check :-p }) setType qual.tpe setPos qual.pos, name) - case accErr: Inferencer#AccessError => - val qual1 = - try adaptToMemberWithArgs(tree, qual, name, mode) - catch { case _: TypeError => qual } - if (qual1 ne qual) typed(Select(qual1, name) setPos tree.pos, mode, pt) - else accErr.emit() + case _ if accessibleError.isDefined => + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, false, false) + if (!qual1.isErrorTyped && (qual1 ne qual)) + typed(Select(qual1, name) setPos tree.pos, mode, pt) + else { + issue(accessibleError.get) + setError(tree) + } case _ => result } @@ -3753,7 +3849,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // getClass, we have to catch it immediately so expressions // like x.getClass().newInstance() are typed with the type of x. val isRefinableGetClass = ( - selection.symbol.name == nme.getClass_ + !selection.isErrorTyped + && selection.symbol.name == nme.getClass_ && selection.tpe.params.isEmpty // TODO: If the type of the qualifier is inaccessible, we can cause private types // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this @@ -3761,7 +3858,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { && qual.tpe.typeSymbol.isPublic ) if (isRefinableGetClass) - selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) + selection setType MethodType(Nil, erasure.getClassReturnType(qual.tpe)) else selection } @@ -3775,8 +3872,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * (2) Change imported symbols to selections */ def typedIdent(name: Name): Tree = { - def ambiguousError(msg: String) = - error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg) + var errorContainer: AbsTypeError = null + @inline + def ambiguousError(msg: String) = { + assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") + errorContainer = AmbiguousIdentError(tree, name, msg) + } + @inline + def identError(tree: AbsTypeError) = { + assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") + errorContainer = tree + } var defSym: Symbol = tree.symbol // the directly found symbol var pre: Type = NoPrefix // the prefix type of defSym, if a class member @@ -3896,7 +4002,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ambiguousError( "it is imported twice in the same scope by\n"+imports.head + "\nand "+imports1.head) } - while (!imports1.isEmpty && + while (errorContainer == null && !imports1.isEmpty && (!imports.head.isExplicitImport(name) || imports1.head.depth == imports.head.depth)) { var impSym1 = imports1.head.importedSymbol(name) @@ -3930,74 +4036,49 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (inaccessibleSym eq NoSymbol) { // Avoiding some spurious error messages: see SI-2388. if (reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)) () - else { - // This laborious determination arrived at to keep the tests working. - val calcSimilar = ( - name.length > 2 && ( - startingIdentContext.reportGeneralErrors - || startingIdentContext.enclClassOrMethod.reportGeneralErrors - ) - ) - // avoid calculating if we're in "silent" mode. - // name length check to limit unhelpful suggestions for e.g. "x" and "b1" - val similar = { - if (!calcSimilar) "" - else { - val allowed = ( - startingIdentContext.enclosingContextChain - flatMap (ctx => ctx.scope.toList ++ ctx.imports.flatMap(_.allImportedSymbols)) - filter (sym => sym.isTerm == name.isTermName) - filterNot (sym => sym.isPackage || sym.isSynthetic || sym.hasMeaninglessName) - ) - val allowedStrings = ( - allowed.map("" + _.name).distinct.sorted - filterNot (s => (s contains '$') || (s contains ' ')) - ) - similarString("" + name, allowedStrings) - } - } - error(tree.pos, "not found: "+decodeWithKind(name, context.owner) + similar) - } - } - else new AccessError( - tree, inaccessibleSym, context.enclClass.owner.thisType, - inaccessibleExplanation - ).emit() + else identError(SymbolNotFoundError(tree, name, context.owner, startingIdentContext)) + } else + identError(InferErrorGen.AccessError( + tree, inaccessibleSym, context.enclClass.owner.thisType, context.enclClass.owner, + inaccessibleExplanation + )) defSym = context.owner.newErrorSymbol(name) } } } - if (defSym.owner.isPackageClass) - pre = defSym.owner.thisType + if (errorContainer != null) { + ErrorUtils.issueTypeError(errorContainer) + setError(tree) + } else { + if (defSym.owner.isPackageClass) + pre = defSym.owner.thisType - // Inferring classOf type parameter from expected type. - if (defSym.isThisSym) { - typed1(This(defSym.owner) setPos tree.pos, mode, pt) - } - // Inferring classOf type parameter from expected type. Otherwise an - // actual call to the stubbed classOf method is generated, returning null. - else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) - typedClassOf(tree, TypeTree(pt.typeArgs.head)) - else { - val tree1 = ( - if (qual == EmptyTree) tree - // atPos necessary because qualifier might come from startContext - else atPos(tree.pos)(Select(qual, name)) - ) - val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) - // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? - stabilize(tree2, pre2, mode, pt) match { - case accErr: Inferencer#AccessError => accErr.emit() - case result => result + // Inferring classOf type parameter from expected type. + if (defSym.isThisSym) { + typed1(This(defSym.owner) setPos tree.pos, mode, pt) + } + // Inferring classOf type parameter from expected type. Otherwise an + // actual call to the stubbed classOf method is generated, returning null. + else if (isPredefMemberNamed(defSym, nme.classOf) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) + typedClassOf(tree, TypeTree(pt.typeArgs.head)) + else { + val tree1 = ( + if (qual == EmptyTree) tree + // atPos necessary because qualifier might come from startContext + else atPos(tree.pos)(Select(qual, name)) + ) + val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) + // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? + stabilize(tree2, pre2, mode, pt) } } } def typedCompoundTypeTree(templ: Template) = { val parents1 = templ.parents mapConserve (typedType(_, mode)) - if (parents1 exists (_.tpe.isError)) tree setType ErrorType + if (parents1 exists (_.isErrorTyped)) tree setType ErrorType else { - val decls = new Scope + val decls = newScope //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id) val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos) newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ.body) @@ -4007,10 +4088,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedAppliedTypeTree(tpt: Tree, args: List[Tree]) = { val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) - if (tpt1.tpe.isError) { - setError(tree) + if (tpt1.isErrorTyped) { + tpt1 } else if (!tpt1.hasSymbol) { - errorTree(tree, tpt1.tpe+" does not take type parameters") + AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams if (sameLength(tparams, args)) { @@ -4042,16 +4123,16 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap // we can't simply use original in refchecks because it does not contains types // (and the only typed trees we have have been mangled so they're not quite the original tree anymore) - checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") + checkBounds(result, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "") result // you only get to see the wrapped tree after running this check :-p } setType (result.tpe) setPos(result.pos) else result } else if (tparams.isEmpty) { - errorTree(tree, tpt1.tpe+" does not take type parameters") + AppliedTypeNoParametersError(tree, tpt1.tpe) } else { //Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}") if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug - errorTree(tree, "wrong number of type arguments for "+tpt1.tpe+", should be "+tparams.length) + AppliedTypeWrongNumberOfArgsError(tree, tpt1, tparams) } } } @@ -4098,8 +4179,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val typer1 = newTyper(context.makeNewScope(tree, context.owner)) for (useCase <- comment.useCases) { typer1.silent(_.typedUseCase(useCase)) match { - case ex: TypeError => - unit.warning(useCase.pos, ex.msg) + case SilentTypeError(err) => + unit.warning(useCase.pos, err.errMsg) case _ => } for (useCaseSym <- useCase.defined) { @@ -4114,17 +4195,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { typedAnnotated(constr, typed(arg, mode, pt)) case tree @ Block(_, _) => - newTyper(context.makeNewScope(tree, context.owner)) - .typedBlock(tree, mode, pt) + typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + _.typedBlock(tree, mode, pt) + } case Alternative(alts) => val alts1 = alts mapConserve (alt => typed(alt, mode | ALTmode, pt)) treeCopy.Alternative(tree, alts1) setType pt case Star(elem) => - checkStarPatOK(tree.pos, mode) - val elem1 = typed(elem, mode, pt) - treeCopy.Star(tree, elem1) setType makeFullyDefined(pt) + if ((mode & STARmode) == 0 && !isPastTyper) + StarPatternWithVarargParametersError(tree) + treeCopy.Star(tree, typed(elem, mode, pt)) setType makeFullyDefined(pt) case Bind(name, body) => typedBind(name, body) @@ -4141,8 +4223,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case tree @ Function(_, _) => if (tree.symbol == NoSymbol) tree.symbol = context.owner.newAnonymousFunctionValue(tree.pos) - - newTyper(context.makeNewScope(tree, tree.symbol)).typedFunction(tree, mode, pt) + typerWithLocalContext(context.makeNewScope(tree, tree.symbol))(_.typedFunction(tree, mode, pt)) case Assign(lhs, rhs) => typedAssign(lhs, rhs) @@ -4181,17 +4262,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Typed(expr, Function(List(), EmptyTree)) => typedEta(checkDead(typed1(expr, mode, pt))) - case Typed(expr, tpt @ Ident(tpnme.WILDCARD_STAR)) => - val expr0 = typed(expr, onlyStickyModes(mode), WildcardType) + case Typed(expr0, tpt @ Ident(tpnme.WILDCARD_STAR)) => + val expr = typed(expr0, onlyStickyModes(mode), WildcardType) def subArrayType(pt: Type) = if (isValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) else { val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) newExistentialType(List(tparam), arrayType(tparam.tpe)) } - val (expr1, baseClass) = expr0.tpe.typeSymbol match { - case ArrayClass => (adapt(expr0, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(expr0, onlyStickyModes(mode), seqType(pt)), SeqClass) + + val (expr1, baseClass) = expr.tpe.typeSymbol match { + case ArrayClass => (adapt(expr, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) + case _ => (adapt(expr, onlyStickyModes(mode), seqType(pt)), SeqClass) } expr1.tpe.baseType(baseClass) match { case TypeRef(_, _, List(elemtp)) => @@ -4203,11 +4285,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case Typed(expr, tpt) => val tpt1 = typedType(tpt, mode) val expr1 = typed(expr, onlyStickyModes(mode), tpt1.tpe.deconst) - val owntype = - if (isPatternMode) inferTypedPattern(tpt1.pos, tpt1.tpe, pt) - else tpt1.tpe - //Console.println(typed pattern: "+tree+":"+", tp = "+tpt1.tpe+", pt = "+pt+" ==> "+owntype)//DEBUG - treeCopy.Typed(tree, expr1, tpt1) setType owntype + val ownType = if (isPatternMode) inferTypedPattern(tpt1, tpt1.tpe, pt) else tpt1.tpe + treeCopy.Typed(tree, expr1, tpt1) setType ownType case TypeApply(fun, args) => // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) @@ -4259,12 +4338,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // convert new Array^N[T](len) for N > 1 to evidence[ClassManifest[T]].newArrayN(len) val Some((level, manifType)) = erasure.GenericArray.unapply(tpt.tpe) if (level > MaxArrayDims) - error(tree.pos, "cannot create a generic multi-dimensional array of more than "+MaxArrayDims+" dimensions") - val newArrayApp = atPos(tree.pos) { - val manif = getManifestTree(tree.pos, manifType, false) - new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args) + MultiDimensionalArrayError(tree) + else { + val newArrayApp = atPos(tree.pos) { + val manif = getManifestTree(tree, manifType, false) + new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args) + } + typed(newArrayApp, mode, pt) } - typed(newArrayApp, mode, pt) case tree1 => tree1 } @@ -4295,18 +4376,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tree1 = // temporarily use `filter` and an alternative for `withFilter` if (name == nme.withFilter) silent(_ => typedSelect(qual1, name)) match { - case result1: Tree => - result1 - case ex1: TypeError => + case SilentResultValue(result) => + result + case _ => silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { - case result2: Tree => + case SilentResultValue(result2) => unit.deprecationWarning( tree.pos, "`withFilter' method does not yet exist on "+qual1.tpe.widen+ ", using `filter' method instead") result2 - case ex2: TypeError => - reportTypeError(tree.pos, ex1) - setError(tree) + case SilentTypeError(err) => + WithFilterError(tree, err) } } else @@ -4339,8 +4419,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case SelectFromTypeTree(qual, selector) => val qual1 = typedType(qual, mode) - if (qual1.tpe.isVolatile) error(tree.pos, "illegal type selection from volatile type "+qual.tpe) - typedSelect(qual1, selector) + if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual) + else typedSelect(qual1, selector) case CompoundTypeTree(templ) => typedCompoundTypeTree(templ) @@ -4354,7 +4434,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) case etpt @ ExistentialTypeTree(_, _) => - newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode) + typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + _.typedExistentialTypeTree(etpt, mode) + } case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) case tpt @ TypeTree() => @@ -4366,7 +4448,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // whatever type to tree; we just have to survive until a real error message is issued. tree setType AnyClass.tpe case Import(expr, selectors) => - assert(forInteractive) // should not happen in normal circumstances. + assert(forInteractive, "!forInteractive") // should not happen in normal circumstances. tree setType tree.symbol.tpe case _ => abort("unexpected tree: " + tree.getClass + "\n" + tree)//debug @@ -4404,7 +4486,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ptLine("typing %s: pt = %s".format(ptTree(tree), pt), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, - "silent" -> !context.reportGeneralErrors, + "silent" -> context.bufferErrors, "context.owner" -> context.owner ) ) @@ -4424,16 +4506,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { tree1, tree1.tpe.widen, pt, context.undetparamsString) ) //DEBUG } - -// for (t <- tree1.tpe) assert(t != WildcardType) -// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe) if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) result } catch { case ex: TypeError => tree.tpe = null + // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. printTyping("caught %s: while typing %s".format(ex, tree)) //DEBUG - reportTypeError(tree.pos, ex) + + reportTypeError(context, tree.pos, ex) setError(tree) case ex: Exception => if (settings.debug.value) // @M causes cyclic reference error @@ -4455,14 +4536,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def expandMacro(tree: Tree): Tree = try { - macroExpand(tree) match { - case t: Tree => t - case t => errorTree(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) + def expandMacro(tree: Tree): Tree = + macroExpand(tree, context) match { + case Some(t: Tree) => t + case Some(t) => MacroExpandError(tree, t) + case None => setError(tree) // error already reported } - } catch { - case ex: MacroExpandError => errorTree(tree, ex.msg) - } def atOwner(owner: Symbol): Typer = newTyper(context.make(context.tree, owner)) @@ -4547,22 +4626,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // to see are those in the signatures. These do not need a unique object as a prefix. // The situation is different for new's and super's, but scalac does not look deep // enough to see those. See #3938 - error(tree.pos, restpe.prefix+" is not a legal prefix for a constructor") - } - - //@M fix for #2208 - // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef - if(result.tpe.typeArgs.isEmpty) { - // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { - // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not - // designed to deal with the cycles in the scala package (ScalaObject extends - // AnyRef, but the AnyRef type alias is entered after the scala package is - // loaded and completed, so that ScalaObject is unpickled while AnyRef is not - // yet defined ) - result setType(restpe) - } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 - // during uncurry (after refchecks), all types are normalized - result + ConstructorPrefixError(tree, restpe) + } else { + //@M fix for #2208 + // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef + if (result.tpe.typeArgs.isEmpty) { + // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { + // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not + // designed to deal with the cycles in the scala package (ScalaObject extends + // AnyRef, but the AnyRef type alias is entered after the scala package is + // loaded and completed, so that ScalaObject is unpickled while AnyRef is not + // yet defined ) + result setType(restpe) + } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 + // during uncurry (after refchecks), all types are normalized + result + } } } @@ -4586,11 +4665,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { true, false, context) } - def getManifestTree(pos: Position, tp: Type, full: Boolean): Tree = { + def getManifestTree(tree: Tree, tp: Type, full: Boolean): Tree = { val manifestOpt = findManifest(tp, full) if (manifestOpt.tree.isEmpty) { - error(pos, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp) - Literal(Constant(null)) + MissingManifestError(tree, full, tp) } else { manifestOpt.tree } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index a7cd89621c..19b8632ed7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -25,7 +25,7 @@ trait Unapplies extends ast.TreeDSL /** returns type list for return type of the extraction */ def unapplyTypeList(ufn: Symbol, ufntpe: Type) = { - assert(ufn.isMethod) + assert(ufn.isMethod, ufn) //Console.println("utl "+ufntpe+" "+ufntpe.typeSymbol) ufn.name match { case nme.unapply => unapplyTypeListFromReturnType(ufntpe) diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index 1db6c38b4d..24c9926ad8 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -71,13 +71,35 @@ object DocStrings { * Every section starts with a `@` and extends to the next `@`, or * to the end of the comment string, but excluding the final two * characters which terminate the comment. + * + * Also take usecases into account - they need to expand until the next + * usecase or the end of the string, as they might include other sections + * of their own */ def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = findAll(str, 0) (idx => str(idx) == '@' && p(idx)) match { case List() => List() - case idxs => idxs zip (idxs.tail ::: List(str.length - 2)) + case idxs => { + val idxs2 = mergeUsecaseSections(str, idxs) + idxs2 zip (idxs2.tail ::: List(str.length - 2)) + } } - + + /** + * Merge sections following an usecase into the usecase comment, so they + * can override the parent symbol's sections + */ + def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = { + idxs.find(str.substring(_).startsWith("@usecase")) match { + case Some(firstUC) => + val commentSections = idxs.take(idxs.indexOf(firstUC)) + val usecaseSections = idxs.drop(idxs.indexOf(firstUC)).filter(str.substring(_).startsWith("@usecase")) + commentSections ::: usecaseSections + case None => + idxs + } + } + /** Does interval `iv` start with given `tag`? */ def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean = diff --git a/src/compiler/scala/tools/nsc/util/SourceFile.scala b/src/compiler/scala/tools/nsc/util/SourceFile.scala index 4405b3457b..e1ae96da8c 100644 --- a/src/compiler/scala/tools/nsc/util/SourceFile.scala +++ b/src/compiler/scala/tools/nsc/util/SourceFile.scala @@ -34,7 +34,7 @@ abstract class SourceFile { * For regular source files, simply return the argument. */ def positionInUltimateSource(position: Position) = position - override def toString(): String = file.name /* + ":" + content.length */ + override def toString() = file.name def dbg(offset: Int) = (new OffsetPosition(this, offset)).dbgString def path = file.path @@ -61,7 +61,7 @@ object NoSourceFile extends SourceFile { def length = -1 def offsetToLine(offset: Int) = -1 def lineToOffset(index : Int) = -1 - override def toString = "NoSourceFile" + override def toString = "<no source file>" } object NoFile extends VirtualFile("<no file>", "<no file>") |