This page is no longer maintained — Please continue to the home page at www.scala-lang.org

on namespaces and name equality

No replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

Can anyone offer some insight into the design of Name/TermName/TypeName
and the issues I will now describe? Since I am a perverse individual who
cannot leave anything alone, I experimented locally with promoting the
latter two types out of their cave of privacy and using the more
specific type where possible, for instance:

- final def newMethod(name: Name, pos: Position = NoPosition) =
+ final def newMethod(name: TermName, pos: Position = NoPosition) =
- final def newAliasType(pos: Position, name: Name) =
+ final def newAliasType(pos: Position, name: TypeName) =

etc. etc. Then I sprinkled with implicits and logged it whenever one
name type was expected and the other found instead. I did not encounter
any obvious showstoppers doing this.

I undertook it because I live in fear that we have conditions which are
always false but arise rarely enough that it slips by. This because if
you want to compare names, you are utterly on your own:

// can you tell with no doubt which of these might be off the beam?
case Typed(expr, Ident(name)) => name == nme.WILDCARD_STAR.toTypeName
val template = templateOpt(mods1, constrMods withAnnotations constrAnnots, vparamss, tstart, name == nme.Array.toTypeName)
case ClassDef(_, `name`, _, _) :: Nil => true

In a separate experiment, I mixed in my "paranoid equals" trait which
makes a big noise about certain kinds of failed equality comparisons: in
this case we're looking for TermName == TypeName, and we're always
looking for Name == String (where I have found a number of bugs in the
past, but not this time.)

I had to filter out Scopes (which I think is unlikely to lose anything
of interest) and BoxesRunTime (which is probably losing comparisons of
interest.) Post-filtering here is what was left:

scala.tools.nsc.ast.TreeInfo.isImplDef(TreeInfo.scala:346): 'ScalaObject' == 'ScalaObject (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.ast.parser.Parsers$Parser.templateOpt(Parsers.scala:2493): 'Array' == 'Array (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Attribute' == 'Attribute (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Bin' == 'Bin (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'BlackTree' == 'BlackTree (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'CIL_NEWOBJ' == 'CIL_NEWOBJ (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Constant' == 'Constant (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Constructor' == 'Constructor (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'CyclicReference' == 'CyclicReference (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'DummyImplicit' == 'DummyImplicit (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Identifier' == 'Identifier (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'ImportType' == 'ImportType (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'JPropertiesWrapper' == 'JPropertiesWrapper (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'LazyAnnotationInfo' == 'LazyAnnotationInfo (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'MatrixContext' == 'MatrixContext (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Outer' == 'Outer (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'ParRangeIterator' == 'ParRangeIterator (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'RandomAccessSeq' == 'RandomAccessSeq (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Result' == 'Result (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'SelectFromArray' == 'SelectFromArray (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'TypeHistory' == 'TypeHistory (class is class scala.tools.nsc.symtab.Names$TypeName)'
scala.tools.nsc.symtab.Types$Type.findMember(Types.scala:915): 'Wildcard' == 'Wildcard (class is class scala.tools.nsc.symtab.Names$TypeName)'

The first one comes down to this:

def isUnitInScala(tree: Tree, name: Name) = tree match {
case PackageDef(Ident(nme.scala_), defs) => isImplDef(defs, name)
case _ => false
}

private def isImplDef(trees: List[Tree], name: Name): Boolean = trees match {
case Import(_, _) :: xs => isImplDef(xs, name)
case DocDef(_, tree1) :: Nil => isImplDef(List(tree1), name)
case Annotated(_, tree1) :: Nil => isImplDef(List(tree1), name)
case ModuleDef(_, `name`, _) :: Nil => true
case ClassDef(_, `name`, _, _) :: Nil => true
case _ => false
}

Method isUnitInScala will only be called with term names, but then it
calls isImplDef and matches on a ClassDef:

case ClassDef(_, `name`, _, _) :: Nil => true

But the name of a ClassDef is a TypeName, so it will never match. And
indeed I can watch it failing to match scala.ScalaObject, which is half
the names it is ever called with. It gets away with it because it is a
multi-boolean conditional which calls the same method with scala.Predef,
which has a module definition and thus a term name.

So am I missing some hidden safety net which means these are not items
of concern? I'm not smart enough to get this kind of thing right all the
time and I'm a little skeptical anyone else is either. Is there a
reason we can't get the type system involved a little more?

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland