From 4224d2a7b0a6beb47760fc323e9c946813f6bdb0 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 28 Jan 2012 07:49:20 +0100 Subject: -Yshow-symkinds: prints kinds next to symbol names This very situational option proved to be very useful when debugging https://issues.scala-lang.org/browse/SI-5415 With the help of -Yshow-symkinds, it became possible to distinguish a free var from a module symbol, which gave precise indication of the root of the bug. --- src/compiler/scala/reflect/internal/Symbols.scala | 74 ++++++++++++++-------- .../scala/reflect/internal/TreePrinters.scala | 18 ++++-- src/compiler/scala/reflect/internal/Types.scala | 2 +- .../internal/settings/MutableSettings.scala | 1 + src/compiler/scala/reflect/runtime/Settings.scala | 1 + src/compiler/scala/reflect/runtime/ToolBoxes.scala | 20 ++++-- .../scala/tools/nsc/settings/ScalaSettings.scala | 1 + 7 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 9f8476a6fe..408ff9593a 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1898,36 +1898,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 @@ -1950,12 +1957,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 "" diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index 3a0717d344..63e4c9f1fa 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -27,10 +27,20 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => /** 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 = { + def suffix(t: Tree) = { + var suffix = "" + if (t.hasSymbol && settings.uniqid.value) suffix += ("#" + t.symbol.id) + if (t.hasSymbol && settings.Yshowsymkinds.value) suffix += ("#" + t.symbol.abbreviatedKindString) + suffix + } + + t match { + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), quotedName(name)) + suffix(t) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), quotedName(name)) + suffix(t) + case Ident(name) => quotedName(name) + suffix(t) + case _ => t.toString + } } class TreePrinter(out: PrintWriter) extends super.TreePrinter { diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 35d26493f8..fab10f7896 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1205,7 +1205,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) "" 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/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/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala index 9ab12c6a86..46d890c5d1 100644 --- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala +++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala @@ -123,15 +123,21 @@ trait ToolBoxes extends { self: Universe => applyMeth.invoke(result) } } - - 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.uniqid.value = printKinds tree.toString - } finally - compiler.settings.printtypes.value = saved + } finally { + settings.printtypes.value = saved1 + settings.uniqid.value = saved2 + settings.Yshowsymkinds.value = saved3 + } } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6806ca03ba..107ffc35c6 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).", "") -- cgit v1.2.3 From 92a358aebae6336e4a2df54e5446f43efac71b21 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 28 Jan 2012 12:24:30 -0800 Subject: Cleaned up polymorphic method creation. I love the smell of polymorphic method synthesis in the early afternoon. --- .../scala/reflect/internal/Definitions.scala | 115 +++++++++------------ src/compiler/scala/reflect/internal/StdNames.scala | 2 + src/compiler/scala/reflect/internal/Symbols.scala | 4 + 3 files changed, 54 insertions(+), 67 deletions(-) diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 1490d80d7a..19726b694d 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -14,6 +14,14 @@ 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, newScope, clazz) @@ -311,17 +319,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 +351,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, newScope, clazz)) - } lazy val MatchingStrategyClass = getRequiredClass("scala.MatchingStrategy") // collections classes @@ -637,8 +629,8 @@ trait Definitions extends reflect.api.StandardDefinitions { } // members of class scala.Any - lazy val Any_== = newMethod(AnyClass, nme.EQ, anyparam, booltype, FINAL) - lazy val Any_!= = newMethod(AnyClass, nme.NE, anyparam, booltype, FINAL) + 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) @@ -653,12 +645,9 @@ trait Definitions extends reflect.api.StandardDefinitions { // 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 = newPolyMethod( - AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL - lazy val Any_asInstanceOf = newPolyMethod( - AnyClass, nme.asInstanceOf_, tparam => NullaryMethodType(tparam.typeConstructor)) setFlag FINAL + 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) @@ -666,15 +655,11 @@ trait Definitions extends reflect.api.StandardDefinitions { 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_synchronized = newPolyMethodCon( - ObjectClass, nme.synchronized_, - tparam => msym => MethodType(msym.newSyntheticValueParams(List(tparam.typeConstructor)), tparam.typeConstructor)) setFlag FINAL - 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 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_) @@ -686,7 +671,6 @@ trait Definitions extends reflect.api.StandardDefinitions { def Object_hashCode = getMember(ObjectClass, nme.hashCode_) def Object_toString = getMember(ObjectClass, nme.toString_) - // boxed classes lazy val ObjectRefClass = getRequiredClass("scala.runtime.ObjectRef") lazy val VolatileObjectRefClass = getRequiredClass("scala.runtime.VolatileObjectRef") @@ -831,39 +815,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), newScope, 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 diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index aba00088f9..b3069adfb4 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -291,6 +291,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" @@ -336,6 +337,7 @@ 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" diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 9f8476a6fe..e3355345f0 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -251,6 +251,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. -- cgit v1.2.3 From d0c5ee4c031a126cee4c552a34cf732716568076 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 28 Jan 2012 15:37:07 -0800 Subject: More method synthesis unification. --- .../scala/reflect/internal/Definitions.scala | 37 +++-- src/compiler/scala/reflect/internal/Symbols.scala | 12 ++ .../scala/tools/nsc/transform/Erasure.scala | 2 +- src/compiler/scala/tools/nsc/transform/Mixin.scala | 2 +- .../tools/nsc/typechecker/MethodSynthesis.scala | 133 ++++++++++++++++ .../tools/nsc/typechecker/SyntheticMethods.scala | 167 ++------------------- 6 files changed, 182 insertions(+), 171 deletions(-) diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 19726b694d..8114be20d5 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -421,24 +421,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 @@ -586,6 +589,14 @@ trait Definitions extends reflect.api.StandardDefinitions { case _ => NoType } + /** To avoid unchecked warnings on polymorphic classes, translate + * a Foo[T] into a Foo[_] for use in the pattern matcher. + */ + def typeCaseType(clazz: Symbol) = clazz.tpe.normalize match { + case TypeRef(_, sym, args) if args.nonEmpty => newExistentialType(sym.typeParams, clazz.tpe) + case tp => tp + } + def seqType(arg: Type) = appliedType(SeqClass.typeConstructor, List(arg)) def arrayType(arg: Type) = appliedType(ArrayClass.typeConstructor, List(arg)) def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg)) diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index e3355345f0..b3df2b0498 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1832,6 +1832,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 $$ * Do the same for any accessed symbols or setters/getters */ diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index b342b95742..fe479a5375 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -797,7 +797,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/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index bd29336703..b3b7596f9a 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -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/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index c6ca9870c3..0c32ff32c0 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. * diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 4e986dc5aa..4ea21b1c44 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 typeCaseType(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, typeCaseType(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 } -- cgit v1.2.3 From 4ab88fbe3ecc5d84a0dec2d8acfbb1687bdd5bd5 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 28 Jan 2012 23:25:31 -0800 Subject: Bonus test case for SI-3999. --- test/files/pos/t3999b.scala | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/files/pos/t3999b.scala diff --git a/test/files/pos/t3999b.scala b/test/files/pos/t3999b.scala new file mode 100644 index 0000000000..d3fe108479 --- /dev/null +++ b/test/files/pos/t3999b.scala @@ -0,0 +1,20 @@ +object `package` { + trait Score { def toString : String } + trait Test[+T <: Score] { def apply(s : String) : T } + + case class FT(f : Float) extends Score + implicit object FT extends Test[FT] { def apply(s : String) : FT = new FT(s.toFloat) } + + case class IT(i : Int) extends Score + implicit object IT extends Test[IT] { def apply(s : String) : IT = new IT(s.toInt) } +} + +class TT[+T <: Score](implicit val tb : Test[T]) { + def read(s : String) : T = tb(s) +} + +object Tester { + val tt = new TT[FT] + val r = tt.read("1.0") + r.toString +} \ No newline at end of file -- cgit v1.2.3 From 396c01b2c10fe361e92b060e9d524921bd3849d6 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sun, 29 Jan 2012 15:59:08 +0100 Subject: Test for https://issues.scala-lang.org/browse/SI-5418 --- test/pending/run/t5418.check | 0 test/pending/run/t5418.scala | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 test/pending/run/t5418.check create mode 100644 test/pending/run/t5418.scala diff --git a/test/pending/run/t5418.check b/test/pending/run/t5418.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pending/run/t5418.scala b/test/pending/run/t5418.scala new file mode 100644 index 0000000000..065710f15e --- /dev/null +++ b/test/pending/run/t5418.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + new Object().getClass + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} \ No newline at end of file -- cgit v1.2.3 From 818afc61dd508d601369e7a881eb0d2b97e07b77 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 29 Jan 2012 13:47:17 -0800 Subject: Test case closes SI-4515. --- test/files/neg/t4515.check | 6 ++++++ test/files/neg/t4515.scala | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 test/files/neg/t4515.check create mode 100644 test/files/neg/t4515.scala diff --git a/test/files/neg/t4515.check b/test/files/neg/t4515.check new file mode 100644 index 0000000000..ce5350b35f --- /dev/null +++ b/test/files/neg/t4515.check @@ -0,0 +1,6 @@ +t4515.scala:37: error: type mismatch; + found : _0(in value $anonfun) where type _0(in value $anonfun) + required: (some other)_0(in value $anonfun) where type +(some other)_0(in value $anonfun) + handler.onEvent(target, ctx.getEvent, node, ctx) + ^ +one error found diff --git a/test/files/neg/t4515.scala b/test/files/neg/t4515.scala new file mode 100644 index 0000000000..63049f201d --- /dev/null +++ b/test/files/neg/t4515.scala @@ -0,0 +1,41 @@ +import scala.collection.mutable.HashMap + +object Main { + trait Target { } + + trait PushEventContext[EventType] { + def getEvent: EventType + } + trait PushNode[EventType] { } + trait DerivedPushNode[EventType] extends PushNode[EventType] { } + + trait HandlerBase[EventType] { + def onEvent(target: Target, + event: EventType, + node: PushNode[EventType], + ctx: PushEventContext[EventType]): Unit + } + val handlers = new HashMap[DerivedPushNode[_], HandlerBase[_]] + + object TimerPushService { + private val INSTANCE: TimerPushService = new TimerPushService + def get: TimerPushService = INSTANCE + } + + class TimerPushService { + def add[EventType](node: DerivedPushNode[EventType], + context: PushEventContext[EventType]): Unit = {} + + def pollEvents[EventType](node: DerivedPushNode[EventType]): List[PushEventContext[EventType]] = + Nil + } + + def onTimer(target: Target) { + val pushService = TimerPushService.get + for ((node, handler) <- handlers) { + for (ctx <- pushService.pollEvents(node)) { + handler.onEvent(target, ctx.getEvent, node, ctx) + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 6eb24b2beacdb2c94b7818de319b338add298c98 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 30 Jan 2012 08:05:12 -0800 Subject: Moving getClassReturnType out of erasure. And other post-merge cleanup. --- .../scala/reflect/internal/Definitions.scala | 35 +++++++++++++++++++++- .../scala/tools/nsc/transform/Erasure.scala | 33 -------------------- .../scala/tools/nsc/typechecker/Typers.scala | 2 +- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 9c9a9293aa..4857ec6b80 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -663,7 +663,40 @@ trait Definitions extends reflect.api.StandardDefinitions { // AnyVal_getClass is defined here. Once we have a new strap, it could also be // defined directly in the AnyVal trait. Right now this does not work, because // strap complains about overriding a final getClass method in Any. - lazy val AnyVal_getClass = newMethod(AnyValClass, nme.getClass_, Nil, getClassReturnType(AnyValClass.tpe), DEFERRED) + lazy val AnyVal_getClass = newMethod(AnyValClass, nme.getClass_, Nil, getClassReturnType(AnyValClass.tpe)) + + // A type function from T => Class[U], used to determine the return + // type of getClass calls. The returned type is: + // + // 1. If T is a value type, Class[T]. + // 2. If T is a phantom type (Any or AnyVal), Class[_]. + // 3. If T is a local class, Class[_ <: |T|]. + // 4. Otherwise, Class[_ <: T]. + // + // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the + // receiver is AnyVal, it implies the receiver is boxed, so the correct + // class object is that of java.lang.Integer, not Int. + // + // TODO: If T is final, return type could be Class[T]. Should it? + def getClassReturnType(tp: Type): Type = { + val sym = tp.typeSymbol + + if (phase.erasedTypes) ClassClass.tpe + else if (isValueClass(sym)) ClassType(tp.widen) + else { + val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) + val upperBound = ( + if (isPhantomClass(sym)) AnyClass.tpe + else if (sym.isLocalClass) erasure.intersectionDominator(tp.parents) + else tp.widen + ) + + existentialAbstraction( + eparams, + ClassType(eparams.head setInfo TypeBounds.upper(upperBound) tpe) + ) + } + } // members of class java.lang.{ Object, String } lazy val Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 1d321b068c..6b7e34cf49 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -31,39 +31,6 @@ abstract class Erasure extends AddInterfaces // -------- erasure on types -------------------------------------------------------- - // A type function from T => Class[U], used to determine the return - // type of getClass calls. The returned type is: - // - // 1. If T is a value type, Class[T]. - // 2. If T is a phantom type (Any or AnyVal), Class[_]. - // 3. If T is a local class, Class[_ <: |T|]. - // 4. Otherwise, Class[_ <: T]. - // - // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the - // receiver is AnyVal, it implies the receiver is boxed, so the correct - // class object is that of java.lang.Integer, not Int. - // - // TODO: If T is final, return type could be Class[T]. Should it? - def getClassReturnType(tp: Type): Type = { - val sym = tp.typeSymbol - - if (phase.erasedTypes) ClassClass.tpe - else if (isValueClass(sym)) ClassType(tp.widen) - else { - val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) - val upperBound = ( - if (isPhantomClass(sym)) AnyClass.tpe - else if (sym.isLocalClass) intersectionDominator(tp.parents) - else tp.widen - ) - - existentialAbstraction( - eparams, - ClassType(eparams.head setInfo TypeBounds.upper(upperBound) tpe) - ) - } - } - // convert a numeric with a toXXX method def numericConversion(tree: Tree, numericSym: Symbol): Tree = { val mname = newTermName("to" + numericSym.name) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b427e19820..161daa4275 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3823,7 +3823,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, getClassReturnType(qual.tpe)) else selection } -- cgit v1.2.3