From f8c9dc95929655a198066652cd12109329836198 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 Nov 2013 14:17:12 +0100 Subject: Changed Signatures Signatures have a different meaning before and after erasure. After erasure, the result type counts also whereas before it doesn't. The new definitions refelect this behavior. --- src/dotty/tools/dotc/config/Config.scala | 3 +- src/dotty/tools/dotc/core/Denotations.scala | 48 ++++++------------- src/dotty/tools/dotc/core/Signature.scala | 56 ++++++++++++++++++++++ src/dotty/tools/dotc/core/SymDenotations.scala | 53 ++++++++++---------- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 12 ++--- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 +- src/dotty/tools/dotc/core/transform/Erasure.scala | 18 +++---- src/dotty/tools/dotc/typer/Typer.scala | 1 + 9 files changed, 116 insertions(+), 79 deletions(-) create mode 100644 src/dotty/tools/dotc/core/Signature.scala (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index 4d93fae29..124b8d61c 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -2,9 +2,10 @@ package dotty.tools.dotc.config object Config { - final val cacheMemberNames = true + final val cacheMembersNamed = true final val cacheAsSeenFrom = true final val useFingerPrints = true + final val cacheMemberNames = true /** When set, use new signature-based matching. * Advantantage of doing so: It's supposed to be faster diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 8aafa5e64..f904f8d78 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -9,6 +9,7 @@ import Names.TypeName import Symbols.NoSymbol import Symbols._ import Types._, Periods._, Flags._, Transformers._, Decorators._ +import transform.Erasure import printing.Texts._ import printing.Printer import io.AbstractFile @@ -26,8 +27,7 @@ import Decorators.SymbolIteratorDecorator * * Lines ending in a horizontal line mean subtying (right is a subtype of left). * - * NamedType------NamedTypeWithSignature - * + * NamedType------TermRefWithSignature * | | Symbol---------ClassSymbol * | | | | * | denot | denot | denot | denot @@ -44,8 +44,9 @@ import Decorators.SymbolIteratorDecorator * NamedType A type consisting of a prefix type and a name, with fields * prefix: Type * name: Name - * NamedTypeWithSignature A named type that has in addition a signature to select an overloaded variant, with new field - * signature: Signature + * It has two subtypes: TermRef and TypeRef + * TermRefWithSignature A TermRef that has in addition a signature to select an overloaded variant, with new field + * sig: Signature * Symbol A label for a definition or declaration in one compiler run * ClassSymbol A symbol representing a class * Denotation The meaning of a named type or symbol during a period @@ -65,27 +66,6 @@ import Decorators.SymbolIteratorDecorator */ object Denotations { - /** The signature of a denotation. - * Overloaded denotations with the same name are distinguished by - * their signatures. A signature is a list of the fully qualified names - * of the type symbols of the erasure of the parameters of the - * denotation. For instance a definition - * - * def f(x: Int)(y: List[String]): String - * - * would have signature - * - * List("scala.Int".toTypeName, "scala.collection.immutable.List".toTypeName) - * - * TODO: discriminate on result type as well !!! - */ - type Signature = List[TypeName] - - /** The signature of a val or parameterless def, as opposed - * to List(), which is the signature of a zero-parameter def. - */ - val NotAMethod: Signature = List(Names.EmptyTypeName) - /** A denotation is the result of resolving * a name (either simple identifier or select) during a given period. * @@ -231,8 +211,7 @@ object Denotations { } case denot1: SingleDenotation => if (denot1 eq denot2) denot1 - else if (denot1.signature != denot2.signature) NoDenotation - else { + else if (denot1.signature matches denot2.signature) { val info1 = denot1.info val info2 = denot2.info val sym2 = denot2.symbol @@ -254,6 +233,7 @@ object Denotations { } } } + else NoDenotation } if (this eq that) this @@ -275,8 +255,7 @@ object Denotations { def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = { def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation = - if (denot1.signature != denot2.signature) NoDenotation - else { + if (denot1.signature matches denot2.signature) { val info1 = denot1.info val info2 = denot2.info val sym2 = denot2.symbol @@ -304,6 +283,7 @@ object Denotations { } } } + else NoDenotation def throwError = throw new MatchError(s"$this | $that") @@ -378,15 +358,15 @@ object Denotations { def hasUniqueSym: Boolean override def isType = info.isInstanceOf[TypeType] override def signature(implicit ctx: Context): Signature = { - if (isType) NotAMethod + if (isType) Signature.NotAMethod else info match { case tp: PolyType => tp.resultType match { case mt: MethodType => mt.signature - case _ => List() + case tp => Signature(tp) } case mt: MethodType => mt.signature - case _ => NotAMethod + case _ => Signature.NotAMethod } } @@ -411,7 +391,7 @@ object Denotations { if (symbol isAccessibleFrom (pre, superAccess)) this else NoDenotation def atSignature(sig: Signature)(implicit ctx: Context): SingleDenotation = - if (sig == signature) this else NoDenotation + if (sig matches signature) this else NoDenotation // ------ Transformations ----------------------------------------- @@ -535,7 +515,7 @@ object Denotations { final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) final def containsSig(sig: Signature)(implicit ctx: Context) = - exists && signature == sig + exists && (signature matches sig) final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation = if (p(this)) this else NoDenotation final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation = diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala new file mode 100644 index 000000000..50c42e96d --- /dev/null +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -0,0 +1,56 @@ +package dotty.tools.dotc +package core + +import Names._, Types._, Contexts._ +import transform.Erasure.sigName + +/** The signature of a denotation. + * Overloaded denotations with the same name are distinguished by + * their signatures. A signature of a method (of type PolyType,MethodType, or ExprType) is + * composed of a list of signature names, one for each parameter type, plus a signature for + * the result type. Methods are uncurried before taking their signatures. + * The signature name of a type is the fully qualified name of the type symbol of the type's erasure. + * + * For instance a definition + * + * def f(x: Int)(y: List[String]): String + * + * would have signature + * + * Signature( + * List("scala.Int".toTypeName, "scala.collection.immutable.List".toTypeName), + * "scala.String".toTypeName) + * + * The signatures of non-method types are always `NotAMethod`. + */ +case class Signature private (paramsSig: List[TypeName], resSig: TypeName) { + + /** Does this signature conincide with that signature on their parameter parts? */ + final def sameParams(that: Signature): Boolean = this.paramsSig == that.paramsSig + + /** The meaning of `matches` depends on the phase. If types are not erased, + * it means `sameParams`. Once types are erased, it means `==`, comparing parameter as + * well as result type parts. + */ + final def matches(that: Signature)(implicit ctx: Context) = + if (ctx.erasedTypes) equals(that) else sameParams(that) + + /** Construct a signature by prepending the signature names of the given `params` + * to the parameter part of this signature. + */ + def ++:(params: List[Type])(implicit ctx: Context) = + Signature((params map sigName) ++ paramsSig, resSig) + +} + +object Signature { + + /** The signature of everything that's not a method, i.e. that has + * a type different from PolyType, MethodType, or ExprType. + */ + val NotAMethod = Signature(List(EmptyTypeName), EmptyTypeName) + + /** The signature of a method with no parameters and result type `resultType`. */ + def apply(resultType: Type)(implicit ctx: Context): Signature = + apply(Nil, sigName(resultType)) +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index fa7b71450..2a0b9e9ce 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -10,6 +10,7 @@ import collection.immutable.BitSet import scala.reflect.io.AbstractFile import Decorators.SymbolIteratorDecorator import annotation.tailrec +import util.SimpleMap import config.Config trait SymDenotations { this: Context => @@ -655,7 +656,7 @@ object SymDenotations { * and at signature `NotAMethod`. */ def valRef(implicit ctx: Context): TermRef = - TermRef.withSig(owner.thisType, name.asTermName, NotAMethod).withDenot(this) + TermRef.withSig(owner.thisType, name.asTermName, Signature.NotAMethod).withDenot(this) /** The TermRef representing this term denotation at its original location * at the denotation's signature. @@ -931,7 +932,7 @@ object SymDenotations { * have existing symbols. */ final def membersNamed(name: Name)(implicit ctx: Context): PreDenotation = - if (Config.cacheMemberNames) { + if (Config.cacheMembersNamed) { var denots: PreDenotation = memberCache lookup name if (denots == null) { denots = computeMembersNamed(name) @@ -1027,7 +1028,7 @@ object SymDenotations { } } - private[this] var memberNamesCache: Map[NameFilter, Set[Name]] = Map() + private[this] var memberNamesCache: SimpleMap[NameFilter, Set[Name]] = SimpleMap.Empty def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { def computeMemberNames: Set[Name] = { @@ -1036,36 +1037,35 @@ object SymDenotations { val candidates = inheritedNames ++ ownNames candidates filter (keepOnly(thisType, _)) } - if (this is PackageClass) computeMemberNames // don't cache package member names; they might change - else memberNamesCache get keepOnly match { - case Some(names) => - names - case _ => + if ((this is PackageClass) || !Config.cacheMemberNames) computeMemberNames // don't cache package member names; they might change + else { + val cached = memberNamesCache(keepOnly) + if (cached != null) cached + else { setFlag(Frozen) val names = computeMemberNames memberNamesCache = memberNamesCache.updated(keepOnly, names) names + } } } - private[this] var fullNameCache: Map[Char, Name] = Map() - - override final def fullName(separator: Char)(implicit ctx: Context): Name = - fullNameCache get separator match { - case Some(fn) => - fn - case _ => - val fn = super.fullName(separator) - fullNameCache = fullNameCache.updated(separator, fn) - fn + private[this] var fullNameCache: SimpleMap[Character, Name] = SimpleMap.Empty + override final def fullName(separator: Char)(implicit ctx: Context): Name = { + val cached = fullNameCache(separator) + if (cached != null) cached + else { + val fn = super.fullName(separator) + fullNameCache = fullNameCache.updated(separator, fn) + fn } + } // to avoid overloading ambiguities override def fullName(implicit ctx: Context): Name = super.fullName override def primaryConstructor(implicit ctx: Context): Symbol = { - val cname = - if (this is Trait | ImplClass) nme.TRAIT_CONSTRUCTOR else nme.CONSTRUCTOR + val cname = if (this is Trait | ImplClass) nme.TRAIT_CONSTRUCTOR else nme.CONSTRUCTOR decls.denotsNamed(cname).first.symbol } } @@ -1086,7 +1086,7 @@ object SymDenotations { * Note: LazyTypes double up as (constant) functions from Symbol and * from (TermSymbol, ClassSymbol) to LazyType. That way lazy types can be * directly passed to symbol creation methods in Symbols that demand instances - * of these types. + * of these function types. */ abstract class LazyType extends UncachedGroundType with (Symbol => LazyType) @@ -1102,6 +1102,9 @@ object SymDenotations { private var mySourceModuleFn: () => Symbol = NoSymbolFn private var myModuleClassFn: () => Symbol = NoSymbolFn + /** A proxy to this lazy type that keeps the complete operation + * but provides fresh slots for scope/sourceModule/moduleClass + */ def proxy: LazyType = new LazyType { override def complete(denot: SymDenotation) = self.complete(denot) } @@ -1117,15 +1120,11 @@ object SymDenotations { val NoSymbolFn = () => NoSymbol + /** A missing completer */ class NoCompleter extends LazyType { def complete(denot: SymDenotation): Unit = unsupported("complete") } - /** A missing completer */ - object NoCompleter extends LazyType { - override def complete(denot: SymDenotation): Unit = unsupported("complete") - } - /** A lazy type for modules that points to the module class. * Needed so that `moduleClass` works before completion. * Completion of modules is always completion of the underlying @@ -1134,7 +1133,7 @@ object SymDenotations { class ModuleCompleter(override val moduleClass: ClassSymbol)(implicit cctx: CondensedContext) extends LazyType { def complete(denot: SymDenotation): Unit = { - val from = denot.moduleClass.denot.asClass + val from = moduleClass.denot.asClass denot.setFlag(from.flags.toTermFlags & RetainedModuleValFlags) denot.annotations = from.annotations filter (_.appliesToModule) // !!! ^^^ needs to be revised later. The problem is that annotations might diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index dfb9b382f..53098e943 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -313,7 +313,7 @@ class TypeComparer(initctx: Context) extends DotClass { case tp2: PolyType => tp1 match { case tp1: PolyType => - tp1.signature == tp2.signature && + (tp1.signature sameParams tp2.signature) && matchingTypeParams(tp1, tp2) && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 9c849bfdf..fb430f336 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -487,7 +487,7 @@ object Types { * poly types. */ def matches(that: Type)(implicit ctx: Context): Boolean = - if (Config.newMatch) this.signature == that.signature + if (Config.newMatch) this.signature matches that.signature else track("matches") { ctx.typeComparer.matchesType( this, that, alwaysMatchSimple = !ctx.phase.erasedTypes) @@ -798,7 +798,7 @@ object Types { * pattern is that method signatures use caching, so encapsulation * is improved using an OO scheme). */ - def signature(implicit ctx: Context): Signature = NotAMethod + def signature(implicit ctx: Context): Signature = Signature.NotAMethod /** Convert to text */ def toText(printer: Printer): Text = printer.toText(this) @@ -1406,11 +1406,11 @@ object Types { override def signature(implicit ctx: Context): Signature = { def computeSignature: Signature = { - val followSig = resultType match { + val followSig: Signature = resultType match { case rtp: MethodType => rtp.signature - case _ => Nil + case tp => Signature(tp) } - (paramTypes map Erasure.paramSignature) ++ followSig + paramTypes ++: followSig } if (ctx.runId != mySignatureRunId) { mySignature = computeSignature @@ -1499,7 +1499,7 @@ object Types { abstract case class ExprType(override val resultType: Type) extends CachedProxyType with TermType { override def underlying(implicit ctx: Context): Type = resultType - override def signature(implicit ctx: Context): Signature = Nil + override def signature(implicit ctx: Context): Signature = Signature(resultType) // todo: cache? def derivedExprType(resultType: Type)(implicit ctx: Context) = if (resultType eq this.resultType) this else ExprType(resultType) override def computeHash = doHash(resultType) diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 57fc3007a..0f4e42987 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -599,7 +599,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val pre = readTypeRef() val sym = readDisambiguatedSymbolRef(_.info.isParameterless) if (isLocal(sym) || (pre == NoPrefix)) pre select sym - else TermRef.withSig(pre, sym.name.asTermName, NotAMethod) // !!! should become redundant + else TermRef.withSig(pre, sym.name.asTermName, Signature.NotAMethod) // !!! should become redundant case SUPERtpe => val thistpe = readTypeRef() val supertpe = readTypeRef() diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 03726e4f5..1b86eff68 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -99,31 +99,31 @@ object Erasure { if (bcs1.isEmpty) defn.ObjectClass else bcs1.head } - /** The parameter signature of a type. - * Need to ensure correspondence with erasure + /** The name of the type as it is used in `Signature`s. + * Need to ensure correspondence with erasure! */ - def paramSignature(tp: Type)(implicit ctx: Context): TypeName = tp match { + def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match { case tp: TypeRef => val sym = tp.symbol if (sym.isClass) /*if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) else */if (sym.owner is Package) normalizeClass(sym.asClass).name else sym.asClass.name - else paramSignature(tp.info) + else sigName(tp.info) case tp: RefinedType => val parent = tp.parent if (parent isRef defn.ArrayClass) eraseArray(tp) match { case tp1: RefinedType if tp1.parent isRef defn.ArrayClass => - paramSignature(tp1.refinedInfo) ++ "[]" + sigName(tp1.refinedInfo) ++ "[]" case tp1 => - paramSignature(tp1) + sigName(tp1) } - else paramSignature(parent) + else sigName(parent) case tp: TypeProxy => - paramSignature(tp.underlying) + sigName(tp.underlying) case AndType(tp1, tp2) => - paramSignature(tp1) + sigName(tp1) case OrType(tp1, tp2) => lubClass(tp1, tp2).name case ErrorType => diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index a4b968c9a..e4081f633 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -979,6 +979,7 @@ class Typer extends Namer with Applications with Implicits { def adaptOverloaded(ref: TermRef) = { val altDenots = ref.denot.alternatives + println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %") val alts = altDenots map (alt => TermRef.withSig(ref.prefix, ref.name, alt.info.signature).withDenot(alt)) def expectedStr = err.expectedTypeStr(pt) -- cgit v1.2.3