diff options
author | Paul Phillips <paulp@improving.org> | 2012-07-06 12:48:52 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-07-06 14:20:15 -0700 |
commit | 724b0dc71f1f8f91b995d01e9e027789f54ecdfe (patch) | |
tree | 5155c036949eb43d5bbb661bbbc5d18ffe3cc987 | |
parent | c632aaca8bdf1dc7c8eac24f5dd0acb18b4683b6 (diff) | |
download | scala-724b0dc71f1f8f91b995d01e9e027789f54ecdfe.tar.gz scala-724b0dc71f1f8f91b995d01e9e027789f54ecdfe.tar.bz2 scala-724b0dc71f1f8f91b995d01e9e027789f54ecdfe.zip |
Implicits to encourage more Name-dropping.
This creates implicits in cakes across the land from:
String => TermName
String => TypeName
And also from:
Name => NameOps[Name] // lower priority
TermName => NameOps[TermName]
TypeName => NameOps[TypeName]
What this is all about, using "drop" as a motivating example,
is that these should all work:
"abc" drop 1 // "bc": String
("abc": TermName) drop 1 // "bc": TermName
("abc": TypeName) drop 1 // "bc": TypeName
(("abc": TypeName): Name) drop 1 // "bc": Name
But this should not:
("bc": Name) // ambiguity error
This requires drop not being directly on Name; peer implicits
from String => TermName and String => TypeName; implicit
classes to install drop on TermName and TypeName; and a lower
priority implicit class to allow ops on Names.
Review by @xeno.by .
-rw-r--r-- | src/compiler/scala/reflect/reify/utils/NodePrinters.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala | 18 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 | ||||
-rw-r--r-- | src/library/scala/reflect/base/Names.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Names.scala | 23 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/StdNames.scala | 7 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaMirrors.scala | 2 | ||||
-rw-r--r-- | test/files/neg/reflection-names-neg.check | 10 | ||||
-rw-r--r-- | test/files/neg/reflection-names-neg.scala | 6 | ||||
-rw-r--r-- | test/files/run/reflection-names.check | 4 | ||||
-rw-r--r-- | test/files/run/reflection-names.scala | 15 |
12 files changed, 73 insertions, 21 deletions
diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 7214da597e..6394e1eac2 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -42,7 +42,7 @@ trait NodePrinters { val buf = new collection.mutable.ListBuffer[String] val annotations = m.group(3) - if (buf.nonEmpty || annotations.nonEmpty) + if (buf.nonEmpty || annotations != "") buf.append("List(" + annotations + ")") val privateWithin = "" + m.group(2) diff --git a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala index a3f6726b44..c79248e1c1 100644 --- a/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala +++ b/src/compiler/scala/tools/nsc/interactive/ScratchPadMaker.scala @@ -92,7 +92,7 @@ trait ScratchPadMaker { self: Global => case PackageDef(_, _) => super.traverse(tree) case ModuleDef(_, name, Template(_, _, body)) => - if (objectName.length == 0 /* objectName.isEmpty does not compile on Java 5 due to ambiguous implicit conversions: augmentString, stringToTermName */) + if (objectName.length == 0) objectName = tree.symbol.fullName body foreach traverseStat applyPendingPatches(skipped) diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index e54ecdd590..c5092aa057 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -35,8 +35,6 @@ abstract class TypeParser { protected var busy: Boolean = false // lock to detect recursive reads - private implicit def stringToTermName(s: String): TermName = newTermName(s) - private object unpickler extends UnPickler { val global: TypeParser.this.global.type = TypeParser.this.global } @@ -260,8 +258,8 @@ abstract class TypeParser { || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ ) { val loader = new loaders.MsilFileLoader(new MsilFile(ntype)) - val nclazz = statics.newClass(ntype.Name.toTypeName) - val nmodule = statics.newModule(ntype.Name) + val nclazz = statics.newClass(ntype.Name) + val nmodule = statics.newModule(ntype.Name) nclazz.setInfo(loader) nmodule.setInfo(loader) staticDefs.enter(nclazz) @@ -311,7 +309,7 @@ abstract class TypeParser { assert(prop.PropertyType == getter.ReturnType); val gparams: Array[ParameterInfo] = getter.GetParameters(); gparamsLength = gparams.length; - val name: Name = if (gparamsLength == 0) prop.Name else nme.apply; + val name: TermName = if (gparamsLength == 0) prop.Name else nme.apply; val flags = translateAttributes(getter); val owner: Symbol = if (getter.IsStatic) statics else clazz; val methodSym = owner.newMethod(name, NoPosition, flags) @@ -333,7 +331,7 @@ abstract class TypeParser { if(getter != null) assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter); - val name: Name = if (gparamsLength == 0) nme.getterToSetter(prop.Name) + val name: TermName = if (gparamsLength == 0) nme.getterToSetter(prop.Name) else nme.update; val flags = translateAttributes(setter); val mtype = methodType(setter, definitions.UnitClass.tpe); @@ -494,13 +492,13 @@ abstract class TypeParser { else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo]; } - private def createMethod(name: Name, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = { + private def createMethod(name: TermName, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = { val mtype = methodType(args, getCLSType(retType)) assert(mtype != null) createMethod(name, flags, mtype, method, statik) } - private def createMethod(name: Name, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { + private def createMethod(name: TermName, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { val methodSym: Symbol = (if (statik) statics else clazz).newMethod(name) methodSym.setFlag(flags).setInfo(mtype(methodSym)) (if (statik) staticDefs else instanceDefs).enter(methodSym) @@ -541,7 +539,7 @@ abstract class TypeParser { s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false); } - private def getName(method: MethodBase): Name = { + private def getName(method: MethodBase): TermName = { def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match { case 1 => name match { @@ -653,7 +651,7 @@ abstract class TypeParser { private def getClassType(typ: MSILType): Type = { assert(typ != null); - val res = rootMirror.getClassByName(typ.FullName.replace('+', '.')).tpe; + val res = rootMirror.getClassByName(typ.FullName.replace('+', '.') : TypeName).tpe; //if (res.isError()) // global.reporter.error("unknown class reference " + type.FullName); res diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ccd346e72d..c675167139 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4283,7 +4283,7 @@ trait Typers extends Modes with Adaptations with Tags { } def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { - val prefix = name stripSuffix nme.EQL + val prefix = name.toTermName stripSuffix nme.EQL def mkAssign(vble: Tree): Tree = Assign( vble, diff --git a/src/library/scala/reflect/base/Names.scala b/src/library/scala/reflect/base/Names.scala index edf2ba7dc9..280a6ce8a2 100644 --- a/src/library/scala/reflect/base/Names.scala +++ b/src/library/scala/reflect/base/Names.scala @@ -11,6 +11,9 @@ package base * `name1 == name2` implies `name1 eq name2`. */ trait Names { + /** Intentionally no implicit from String => Name. */ + implicit def stringToTermName(s: String): TermName = newTermName(s) + implicit def stringToTypeName(s: String): TypeName = newTypeName(s) /** The abstract type of names */ type Name >: Null <: NameBase diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index 18671871ae..3c0848f77f 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -10,12 +10,18 @@ import scala.io.Codec import java.security.MessageDigest import language.implicitConversions +trait LowPriorityNames { + self: Names => + + implicit def nameToNameOps(name: Name): NameOps[Name] = new NameOps[Name](name) +} + /** The class Names ... * * @author Martin Odersky * @version 1.0, 05/02/2005 */ -trait Names extends api.Names { +trait Names extends api.Names with LowPriorityNames { implicit def promoteTermNamesAsNecessary(name: Name): TermName = name.toTermName // Operations ------------------------------------------------------------- @@ -356,11 +362,6 @@ trait Names extends api.Names { final def endsWith(char: Char): Boolean = len > 0 && endChar == char final def endsWith(name: String): Boolean = endsWith(newTermName(name)) - def dropRight(n: Int): ThisNameType = subName(0, len - n) - def drop(n: Int): ThisNameType = subName(n, len) - def stripSuffix(suffix: Name): ThisNameType = - if (this endsWith suffix) dropRight(suffix.length) else thisName - def indexOf(ch: Char) = { val idx = pos(ch) if (idx == length) -1 else idx @@ -429,6 +430,16 @@ trait Names extends api.Names { def debugString = { val s = decode ; if (isTypeName) s + "!" else s } } + implicit def TermNameOps(name: TermName): NameOps[TermName] = new NameOps(name) + implicit def TypeNameOps(name: TypeName): NameOps[TypeName] = new NameOps(name) + + final class NameOps[T <: Name](name: T) { + def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name + def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] + def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + def nonEmpty: Boolean = name.length > 0 + } + implicit val NameTag = ClassTag[Name](classOf[Name]) /** A name that contains no operator chars nor dollar signs. diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 72a99589d5..4070ad83c6 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -84,6 +84,9 @@ trait StdNames { abstract class CommonNames extends NamesApi { type NameType >: Null <: Name + // Masking some implicits so as to allow our targeted => NameType. + protected val stringToTermName = null + protected val stringToTypeName = null protected implicit def createNameType(name: String): NameType def flattenedName(segments: Name*): NameType = @@ -963,7 +966,7 @@ trait StdNames { case -1 => if (name == "") scala.Nil else scala.List(mkName(name, assumeTerm)) // otherwise, we can tell based on whether '#' or '.' is the following char. case idx => - val (simple, div, rest) = (name take idx, name charAt idx, newTermName(name) drop (idx + 1)) + val (simple, div, rest) = (name take idx, name charAt idx, name drop idx + 1) mkName(simple, div == '.') :: segments(rest, assumeTerm) } } @@ -1038,6 +1041,8 @@ trait StdNames { } abstract class SymbolNames { + protected val stringToTermName = null + protected val stringToTypeName = null protected implicit def createNameType(s: String): TypeName = newTypeNameCached(s) val BeanProperty : TypeName diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 41955170bd..e147046410 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -658,7 +658,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym val ownerModule: ModuleSymbol = if (split > 0) packageNameToScala(fullname take split) else this.RootPackage val owner = ownerModule.moduleClass - val name = newTermName(fullname drop (split + 1)) + val name = (fullname: TermName) drop split + 1 val opkg = owner.info decl name if (opkg.isPackage) opkg.asModuleSymbol diff --git a/test/files/neg/reflection-names-neg.check b/test/files/neg/reflection-names-neg.check new file mode 100644 index 0000000000..a56a19e7fd --- /dev/null +++ b/test/files/neg/reflection-names-neg.check @@ -0,0 +1,10 @@ +reflection-names-neg.scala:5: error: type mismatch; + found : String("abc") + required: reflect.runtime.universe.Name +Note that implicit conversions are not applicable because they are ambiguous: + both method stringToTermName in trait Names of type (s: String)reflect.runtime.universe.TermName + and method stringToTypeName in trait Names of type (s: String)reflect.runtime.universe.TypeName + are possible conversion functions from String("abc") to reflect.runtime.universe.Name + val x2 = ("abc": Name) drop 1 // error + ^ +one error found diff --git a/test/files/neg/reflection-names-neg.scala b/test/files/neg/reflection-names-neg.scala new file mode 100644 index 0000000000..7283d16db9 --- /dev/null +++ b/test/files/neg/reflection-names-neg.scala @@ -0,0 +1,6 @@ +import scala.reflect.runtime.universe._ + +object Test { + val x1 = "abc" drop 1 // "bc": String + val x2 = ("abc": Name) drop 1 // error +} diff --git a/test/files/run/reflection-names.check b/test/files/run/reflection-names.check new file mode 100644 index 0000000000..f8cb78cc67 --- /dev/null +++ b/test/files/run/reflection-names.check @@ -0,0 +1,4 @@ +(java.lang.String,bc) +(scala.reflect.internal.Names$TermName_R,bc) +(scala.reflect.internal.Names$TypeName_R,bc) +(scala.reflect.internal.Names$TypeName_R,bc) diff --git a/test/files/run/reflection-names.scala b/test/files/run/reflection-names.scala new file mode 100644 index 0000000000..2433c84813 --- /dev/null +++ b/test/files/run/reflection-names.scala @@ -0,0 +1,15 @@ +import scala.tools.nsc._ + +object Test { + val global = new Global(new Settings()) + import global._ + + val x1 = "abc" drop 1 // "bc": String + val x2 = ("abc": TermName) drop 1 // "bc": TermName + val x3 = ("abc": TypeName) drop 1 // "bc": TypeName + val x4 = (("abc": TypeName): Name) drop 1 // "bc": Name + + def main(args: Array[String]): Unit = { + List(x1, x2, x3, x4) foreach (x => println(x.getClass.getName, x)) + } +} |