Skip to content

Commit b5c3a28

Browse files
committed
Fix of the escaping MethodParam problem
The issue was in the dependency tracking for MethodTypes. We treated methods with false dependencies as non-dependent (as they should be), but in that case the ResultType could contain orphan MethodParams.
1 parent 023c7bc commit b5c3a28

File tree

3 files changed

+102
-57
lines changed

3 files changed

+102
-57
lines changed

src/dotty/tools/dotc/core/Types.scala

Lines changed: 88 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ object Types {
680680
* (Note: no stripTypeVar needed because TypeVar's can't refer to ExprTypes.)
681681
*/
682682
final def widenExpr: Type = this match {
683-
case tp: ExprType => tp.resultType
683+
case tp: ExprType => tp.resType
684684
case _ => this
685685
}
686686

@@ -858,33 +858,33 @@ object Types {
858858
}
859859

860860
/** The parameter types of a PolyType or MethodType, Empty list for others */
861-
final def paramTypess: List[List[Type]] = this match {
861+
final def paramTypess(implicit ctx: Context): List[List[Type]] = this match {
862862
case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess
863863
case pt: PolyType => pt.resultType.paramTypess
864864
case _ => Nil
865865
}
866866

867867
/** The parameter types in the first parameter section of a PolyType or MethodType, Empty list for others */
868-
final def firstParamTypes: List[Type] = this match {
868+
final def firstParamTypes(implicit ctx: Context): List[Type] = this match {
869869
case mt: MethodType => mt.paramTypes
870870
case pt: PolyType => pt.resultType.firstParamTypes
871871
case _ => Nil
872872
}
873873

874874
/** Is this either not a method at all, or a parameterless method? */
875-
final def isParameterless: Boolean = this match {
875+
final def isParameterless(implicit ctx: Context): Boolean = this match {
876876
case mt: MethodType => false
877877
case pt: PolyType => pt.resultType.isParameterless
878878
case _ => true
879879
}
880880

881881
/** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */
882-
def resultType: Type = this
882+
def resultType(implicit ctx: Context): Type = this
883883

884884
/** The final result type of a PolyType, MethodType, or ExprType, after skipping
885885
* all parameter sections, the type itself for all others.
886886
*/
887-
def finalResultType: Type = resultType match {
887+
def finalResultType(implicit ctx: Context): Type = resultType match {
888888
case mt: MethodType => mt.resultType.finalResultType
889889
case pt: PolyType => pt.resultType.finalResultType
890890
case _ => resultType
@@ -1941,49 +1941,77 @@ object Types {
19411941
abstract case class MethodType(paramNames: List[TermName], paramTypes: List[Type])
19421942
(resultTypeExp: MethodType => Type)
19431943
extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType =>
1944+
import MethodType._
19441945

1945-
override val resultType = resultTypeExp(this)
1946-
assert(resultType.exists)
19471946
def isJava = false
19481947
def isImplicit = false
1948+
1949+
private val resType = resultTypeExp(this)
1950+
assert(resType.exists)
1951+
1952+
override def resultType(implicit ctx: Context): Type =
1953+
if (dependencyStatus == FalseDeps) { // dealias all false dependencies
1954+
val dealiasMap = new TypeMap {
1955+
def apply(tp: Type) = tp match {
1956+
case tp @ TypeRef(MethodParam(`thisMethodType`, _), name) => // follow type alias to avoid dependency
1957+
val TypeAlias(alias) = tp.info
1958+
apply(alias)
1959+
case _ =>
1960+
mapOver(tp)
1961+
}
1962+
}
1963+
dealiasMap(resType)
1964+
}
1965+
else resType
19491966

1950-
private[this] var myIsDependent: Boolean = _
1951-
private[this] var myIsDepKnown = false
1952-
1953-
/** Does result type contain references to parameters of this method type?
1954-
*/
1955-
def isDependent(implicit ctx: Context) = {
1956-
if (!myIsDepKnown) {
1957-
val isDepAcc = new TypeAccumulator[Boolean] {
1958-
def apply(x: Boolean, tp: Type) = x || {
1959-
tp match {
1960-
case MethodParam(`thisMethodType`, _) => true
1961-
case tp @ TypeRef(MethodParam(`thisMethodType`, _), name) =>
1962-
tp.info match { // follow type arguments to avoid dependency
1963-
case TypeAlias(tp)=> apply(x, tp)
1964-
case _ => true
1965-
}
1966-
case _ =>
1967-
foldOver(x, tp)
1967+
private[this] var myDependencyStatus: DependencyStatus = Unknown
1968+
1969+
/** The dependency status of this method. Some examples:
1970+
*
1971+
* class C extends { type S; type T = String }
1972+
* def f(x: C)(y: Boolean) // dependencyStatus = NoDeps
1973+
* def f(x: C)(y: x.S) // dependencyStatus = TrueDeps
1974+
* def f(x: C)(y: x.T) // dependencyStatus = FalseDeps, i.e.
1975+
* // dependency can be eliminated by dealiasing.
1976+
*/
1977+
private def dependencyStatus(implicit ctx: Context): DependencyStatus = {
1978+
if (myDependencyStatus == Unknown) {
1979+
val isDepAcc = new TypeAccumulator[DependencyStatus] {
1980+
def apply(x: DependencyStatus, tp: Type) =
1981+
if (x == TrueDeps) x
1982+
else x max {
1983+
tp match {
1984+
case MethodParam(`thisMethodType`, _) => TrueDeps
1985+
case tp @ TypeRef(MethodParam(`thisMethodType`, _), name) =>
1986+
tp.info match { // follow type alias to avoid dependency
1987+
case TypeAlias(alias) => apply(x, alias) max FalseDeps
1988+
case _ => TrueDeps
1989+
}
1990+
case _ =>
1991+
foldOver(x, tp)
1992+
}
19681993
}
1969-
}
19701994
}
1971-
myIsDependent = isDepAcc(false, resultType)
1972-
myIsDepKnown = true
1995+
myDependencyStatus = isDepAcc(NoDeps, resType)
19731996
}
1974-
myIsDependent
1997+
myDependencyStatus
19751998
}
19761999

2000+
/** Does result type contain references to parameters of this method type,
2001+
* which cannot be eliminated by de-aliasing?
2002+
*/
2003+
def isDependent(implicit ctx: Context): Boolean = dependencyStatus == TrueDeps
2004+
19772005
protected def computeSignature(implicit ctx: Context): Signature =
19782006
resultSignature.prepend(paramTypes, isJava)
19792007

1980-
def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], restpe: Type)(implicit ctx: Context) =
1981-
if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (restpe eq this.resultType)) this
2008+
def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], resType: Type)(implicit ctx: Context) =
2009+
if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this
19822010
else {
1983-
val restpeFn = (x: MethodType) => restpe.subst(this, x)
1984-
if (isJava) JavaMethodType(paramNames, paramTypes)(restpeFn)
1985-
else if (isImplicit) ImplicitMethodType(paramNames, paramTypes)(restpeFn)
1986-
else MethodType(paramNames, paramTypes)(restpeFn)
2011+
val resTypeFn = (x: MethodType) => resType.subst(this, x)
2012+
if (isJava) JavaMethodType(paramNames, paramTypes)(resTypeFn)
2013+
else if (isImplicit) ImplicitMethodType(paramNames, paramTypes)(resTypeFn)
2014+
else MethodType(paramNames, paramTypes)(resTypeFn)
19872015
}
19882016

19892017
def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type =
@@ -1994,15 +2022,15 @@ object Types {
19942022
case that: MethodType =>
19952023
this.paramNames == that.paramNames &&
19962024
this.paramTypes == that.paramTypes &&
1997-
this.resultType == that.resultType
2025+
this.resType == that.resType
19982026
case _ =>
19992027
false
20002028
}
20012029

2002-
override def computeHash = doHash(paramNames, resultType, paramTypes)
2030+
override def computeHash = doHash(paramNames, resType, paramTypes)
20032031

20042032
protected def prefixString = "MethodType"
2005-
override def toString = s"$prefixString($paramNames, $paramTypes, $resultType)"
2033+
override def toString = s"$prefixString($paramNames, $paramTypes, $resType)"
20062034
}
20072035

20082036
final class CachedMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)
@@ -2052,6 +2080,12 @@ object Types {
20522080
object MethodType extends MethodTypeCompanion {
20532081
def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) =
20542082
unique(new CachedMethodType(paramNames, paramTypes)(resultTypeExp))
2083+
2084+
private type DependencyStatus = Byte
2085+
private final val Unknown: DependencyStatus = 0 // not yet computed
2086+
private final val NoDeps: DependencyStatus = 1 // no dependent parameters found
2087+
private final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types
2088+
private final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist
20552089
}
20562090

20572091
object JavaMethodType extends MethodTypeCompanion {
@@ -2065,13 +2099,14 @@ object Types {
20652099
}
20662100

20672101
/** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */
2068-
abstract case class ExprType(override val resultType: Type)
2102+
abstract case class ExprType(resType: Type)
20692103
extends CachedProxyType with TermType with MethodicType {
2070-
override def underlying(implicit ctx: Context): Type = resultType
2104+
override def resultType(implicit ctx: Context): Type = resType
2105+
override def underlying(implicit ctx: Context): Type = resType
20712106
protected def computeSignature(implicit ctx: Context): Signature = resultSignature
2072-
def derivedExprType(resultType: Type)(implicit ctx: Context) =
2073-
if (resultType eq this.resultType) this else ExprType(resultType)
2074-
override def computeHash = doHash(resultType)
2107+
def derivedExprType(resType: Type)(implicit ctx: Context) =
2108+
if (resType eq this.resType) this else ExprType(resType)
2109+
override def computeHash = doHash(resType)
20752110
}
20762111

20772112
final class CachedExprType(resultType: Type) extends ExprType(resultType)
@@ -2087,7 +2122,9 @@ object Types {
20872122
extends CachedGroundType with BindingType with TermType with MethodOrPoly {
20882123

20892124
val paramBounds = paramBoundsExp(this)
2090-
override val resultType = resultTypeExp(this)
2125+
val resType = resultTypeExp(this)
2126+
2127+
override def resultType(implicit ctx: Context) = resType
20912128

20922129
protected def computeSignature(implicit ctx: Context) = resultSignature
20932130

@@ -2097,21 +2134,21 @@ object Types {
20972134
def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] =
20982135
paramBounds.mapConserve(_.substParams(this, argTypes).bounds)
20992136

2100-
def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], restpe: Type)(implicit ctx: Context) =
2101-
if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (restpe eq this.resultType)) this
2102-
else duplicate(paramNames, paramBounds, restpe)
2137+
def derivedPolyType(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context) =
2138+
if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this
2139+
else duplicate(paramNames, paramBounds, resType)
21032140

2104-
def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, restpe: Type)(implicit ctx: Context) =
2141+
def duplicate(paramNames: List[TypeName] = this.paramNames, paramBounds: List[TypeBounds] = this.paramBounds, resType: Type)(implicit ctx: Context) =
21052142
PolyType(paramNames)(
21062143
x => paramBounds mapConserve (_.subst(this, x).bounds),
2107-
x => restpe.subst(this, x))
2144+
x => resType.subst(this, x))
21082145

21092146
// need to override hashCode and equals to be object identity
21102147
// because paramNames by itself is not discriminatory enough
21112148
override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
21122149
override def computeHash = identityHash
21132150

2114-
override def toString = s"PolyType($paramNames, $paramBounds, $resultType)"
2151+
override def toString = s"PolyType($paramNames, $paramBounds, $resType)"
21152152
}
21162153

21172154
object PolyType {

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ object Erasure extends TypeTestsCasts{
348348
}
349349

350350
private def protoArgs(pt: Type): List[untpd.Tree] = pt match {
351-
case pt: FunProto => pt.args ++ protoArgs(pt.resultType)
351+
case pt: FunProto => pt.args ++ protoArgs(pt.resType)
352352
case _ => Nil
353353
}
354354

src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,11 @@ object ProtoTypes {
162162
*
163163
* [](args): resultType
164164
*/
165-
case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context)
165+
case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context)
166166
extends UncachedGroundType with ApplyingProto {
167167
private var myTypedArgs: List[Tree] = Nil
168+
169+
override def resultType(implicit ctx: Context) = resType
168170

169171
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
170172
private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty
@@ -241,8 +243,11 @@ object ProtoTypes {
241243
*
242244
* []: argType => resultType
243245
*/
244-
abstract case class ViewProto(argType: Type, override val resultType: Type)
246+
abstract case class ViewProto(argType: Type, resType: Type)
245247
extends CachedGroundType with ApplyingProto {
248+
249+
override def resultType(implicit ctx: Context) = resType
250+
246251
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean =
247252
ctx.typer.isApplicable(tp, argType :: Nil, resultType)
248253

@@ -274,7 +279,10 @@ object ProtoTypes {
274279
*
275280
* [] [targs] resultType
276281
*/
277-
case class PolyProto(targs: List[Type], override val resultType: Type) extends UncachedGroundType with ProtoType {
282+
case class PolyProto(targs: List[Type], resType: Type) extends UncachedGroundType with ProtoType {
283+
284+
override def resultType(implicit ctx: Context) = resType
285+
278286
override def isMatchedBy(tp: Type)(implicit ctx: Context) = {
279287
def isInstantiatable(tp: Type) = tp.widen match {
280288
case PolyType(paramNames) => paramNames.length == targs.length
@@ -284,8 +292,8 @@ object ProtoTypes {
284292
}
285293

286294
def derivedPolyProto(targs: List[Type], resultType: Type) =
287-
if ((targs eq this.targs) && (resultType eq this.resultType)) this
288-
else PolyProto(targs, resultType)
295+
if ((targs eq this.targs) && (resType eq this.resType)) this
296+
else PolyProto(targs, resType)
289297

290298
def map(tm: TypeMap)(implicit ctx: Context): PolyProto =
291299
derivedPolyProto(targs mapConserve tm, tm(resultType))

0 commit comments

Comments
 (0)