diff --git a/compiler/src/dotty/tools/dotc/core/Signature.scala b/compiler/src/dotty/tools/dotc/core/Signature.scala index b1bc760df57e..b9856dcb8e4f 100644 --- a/compiler/src/dotty/tools/dotc/core/Signature.scala +++ b/compiler/src/dotty/tools/dotc/core/Signature.scala @@ -87,14 +87,15 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) { /** Construct a signature by prepending the signature names of the given `params` * to the parameter part of this signature. + * + * Like Signature#apply, the result is only cacheable if `isUnderDefined == false`. */ def prepend(params: List[Type], isJava: Boolean)(implicit ctx: Context) = Signature(params.map(p => sigName(p, isJava)) ++ paramsSig, resSig) /** A signature is under-defined if its paramsSig part contains at least one * `tpnme.Uninstantiated`. Under-defined signatures arise when taking a signature - * of a type that still contains uninstantiated type variables. They are eliminated - * by `fixSignature` in `PostTyper`. + * of a type that still contains uninstantiated type variables. */ def isUnderDefined(implicit ctx: Context) = paramsSig.contains(tpnme.Uninstantiated) || resSig == tpnme.Uninstantiated @@ -116,7 +117,12 @@ object Signature { */ val OverloadedSignature = Signature(List(tpnme.OVERLOADED), EmptyTypeName) - /** The signature of a method with no parameters and result type `resultType`. */ + /** The signature of a method with no parameters and result type `resultType`. + * + * The resulting value is only cacheable if `isUnderDefined == false`, + * otherwise the signature will change once the contained type variables have + * been instantiated. + */ def apply(resultType: Type, isJava: Boolean)(implicit ctx: Context): Signature = { assert(!resultType.isInstanceOf[ExprType]) apply(Nil, sigName(resultType, isJava)) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 79e4a469b00a..56c46804636b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1580,7 +1580,7 @@ object Types { // --- NamedTypes ------------------------------------------------------------------ - abstract class NamedType extends CachedProxyType with ValueType { self => + abstract class NamedType extends CachedProxyType with ValueType with SignatureCachingType { self => type ThisType >: this.type <: NamedType type ThisName <: Name @@ -1592,7 +1592,6 @@ object Types { assert(prefix.isValueType || (prefix eq NoPrefix), s"invalid prefix $prefix") private[this] var myName: Name = null - private[this] var mySig: Signature = null private[this] var lastDenotation: Denotation = null private[this] var lastSymbol: Symbol = null private[this] var checkedPeriod: Period = Nowhere @@ -1601,6 +1600,7 @@ object Types { // Invariants: // (1) checkedPeriod != Nowhere => lastDenotation != null // (2) lastDenotation != null => lastSymbol != null + // (3) mySigRunId != NoRunId => mySig != null def isType = isInstanceOf[TypeRef] def isTerm = isInstanceOf[TermRef] @@ -1621,12 +1621,7 @@ object Types { /** The signature of the last known denotation, or if there is none, the * signature of the symbol */ - final override def signature(implicit ctx: Context): Signature = { - if (mySig == null) mySig = computeSignature - mySig - } - - def computeSignature(implicit ctx: Context): Signature = { + protected def computeSignature(implicit ctx: Context): Signature = { val lastd = lastDenotation if (lastd != null) lastd.signature else symbol.asSeenFrom(prefix).signature @@ -1637,7 +1632,7 @@ object Types { * Otherwise NotAMethod. */ private def currentSignature(implicit ctx: Context): Signature = - if (mySig != null) mySig + if (ctx.runId == mySignatureRunId) mySignature else { val lastd = lastDenotation if (lastd != null) lastd.signature @@ -2584,13 +2579,22 @@ object Types { // and therefore two different poly types would never be equal. /** A trait that mixes in functionality for signature caching */ - trait MethodicType extends TermType { - - private[this] var mySignature: Signature = _ - private[this] var mySignatureRunId: Int = NoRunId + trait SignatureCachingType extends TermType { + protected[this] var mySignature: Signature = _ + protected[this] var mySignatureRunId: Int = NoRunId protected def computeSignature(implicit ctx: Context): Signature + final override def signature(implicit ctx: Context): Signature = { + if (ctx.runId != mySignatureRunId) { + mySignature = computeSignature + if (!mySignature.isUnderDefined) mySignatureRunId = ctx.runId + } + mySignature + } + } + + trait MethodicType extends SignatureCachingType { protected def resultSignature(implicit ctx: Context) = try resultType match { case rtp: MethodicType => rtp.signature case tp => @@ -2602,14 +2606,6 @@ object Types { println(i"failure while taking result signature of $this: $resultType") throw ex } - - final override def signature(implicit ctx: Context): Signature = { - if (ctx.runId != mySignatureRunId) { - mySignature = computeSignature - if (!mySignature.isUnderDefined) mySignatureRunId = ctx.runId - } - mySignature - } } /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */