Skip to content

Commit 601432f

Browse files
committed
Type#isRef: Do not strip refinements by default
Stripping refinements makes sense when checking if a type is a reference to a parameterized type like Option, but this shouldn't be the default, otherwise refinements to Any may be discarded by methods like lub and glb as illustrated in the tests.
1 parent 1c62d05 commit 601432f

File tree

8 files changed

+32
-17
lines changed

8 files changed

+32
-17
lines changed

src/dotty/tools/dotc/ast/CheckTrees.scala.disabled

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ object CheckTrees {
211211
if (rtp isRef defn.BooleanClass)
212212
check(args.isEmpty)
213213
else {
214-
check(rtp isRef defn.OptionClass)
214+
check(rtp.isRef(defn.OptionClass, stripRefinements = true))
215215
val normArgs = rtp.argTypesHi match {
216216
case optionArg :: Nil =>
217217
optionArg.argTypesHi match {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ class Definitions {
564564
if (ctx.erasedTypes) JavaArrayType(elem)
565565
else ArrayType.appliedTo(elem :: Nil)
566566
def unapply(tp: Type)(implicit ctx: Context): Option[Type] = tp.dealias match {
567-
case at: RefinedType if (at isRef ArrayType.symbol) && at.argInfos.length == 1 => Some(at.argInfos.head)
567+
case at: RefinedType if (at.parent.isRef(ArrayType.symbol)) && at.argInfos.length == 1 => Some(at.argInfos.head)
568568
case _ => None
569569
}
570570
}
@@ -667,7 +667,7 @@ class Definitions {
667667

668668
def isTupleType(tp: Type)(implicit ctx: Context) = {
669669
val arity = tp.dealias.argInfos.length
670-
arity <= MaxTupleArity && TupleType(arity) != null && (tp isRef TupleType(arity).symbol)
670+
arity <= MaxTupleArity && TupleType(arity) != null && (tp.isRef(TupleType(arity).symbol, stripRefinements = true))
671671
}
672672

673673
def tupleType(elems: List[Type]) = {
@@ -679,7 +679,7 @@ class Definitions {
679679

680680
def isFunctionType(tp: Type)(implicit ctx: Context) = {
681681
val arity = functionArity(tp)
682-
0 <= arity && arity <= MaxFunctionArity && (tp isRef FunctionType(arity).symbol)
682+
0 <= arity && arity <= MaxFunctionArity && (tp.isRef(FunctionType(arity).symbol, stripRefinements = true))
683683
}
684684

685685
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
446446
// constructor method should not be semi-erased.
447447
else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
448448
else this(tp)
449-
case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) =>
449+
case RefinedType(parent, _, _) if !(parent.isRef(defn.ArrayClass, stripRefinements = true)) =>
450450
eraseResult(parent)
451451
case _ =>
452452
this(tp)

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,22 @@ object Types {
106106
case _ => false
107107
}
108108

109-
/** Is this type a (possibly refined or applied or aliased) type reference
110-
* to the given type symbol?
111-
* @sym The symbol to compare to. It must be a class symbol or abstract type.
112-
* It makes no sense for it to be an alias type because isRef would always
113-
* return false in that case.
109+
/** Is this type a (possibly aliased) type reference to the given type symbol?
110+
*
111+
* @param sym The symbol to compare to. It must be a class symbol or abstract type.
112+
* It makes no sense for it to be an alias type because isRef would always
113+
* return false in that case.
114+
* @param stripRefinements If true, go through refinements.
114115
*/
115-
def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
116+
def isRef(sym: Symbol, stripRefinements: Boolean = false)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
116117
case this1: TypeRef =>
117118
this1.info match { // see comment in Namer#typeDefSig
118-
case TypeAlias(tp) => tp.isRef(sym)
119+
case TypeAlias(tp) => tp.isRef(sym, stripRefinements)
119120
case _ => this1.symbol eq sym
120121
}
121-
case this1: RefinedOrRecType => this1.parent.isRef(sym)
122-
case this1: HKApply => this1.superType.isRef(sym)
122+
case this1: RefinedType if stripRefinements => this1.parent.isRef(sym, stripRefinements)
123+
case this1: RecType => this1.parent.isRef(sym, stripRefinements)
124+
case this1: HKApply => this1.superType.isRef(sym, stripRefinements)
123125
case _ => false
124126
}
125127

@@ -3313,7 +3315,7 @@ object Types {
33133315
case mt: MethodType if !mt.isDependent => Some(absMems.head)
33143316
case _ => None
33153317
}
3316-
else if (tp isRef defn.PartialFunctionClass)
3318+
else if (tp.isRef(defn.PartialFunctionClass, stripRefinements = true))
33173319
// To maintain compatibility with 2.x, we treat PartialFunction specially,
33183320
// pretending it is a SAM type. In the future it would be better to merge
33193321
// Function and PartialFunction, have Function1 contain a isDefinedAt method

src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
911911
val expr = readTerm()
912912
val tpt = readTpt()
913913
val expr1 = expr match {
914-
case SeqLiteral(elems, elemtpt) if tpt.tpe.isRef(defn.ArrayClass) =>
914+
case SeqLiteral(elems, elemtpt) if tpt.tpe.isRef(defn.ArrayClass, stripRefinements = true) =>
915915
JavaSeqLiteral(elems, elemtpt)
916916
case expr => expr
917917
}

src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ object Scala2Unpickler {
7171
def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match {
7272
case tp @ MethodType(paramNames, paramTypes) =>
7373
val lastArg = paramTypes.last
74-
assert(lastArg isRef defn.ArrayClass)
74+
assert(lastArg.isRef(defn.ArrayClass, stripRefinements = true))
7575
val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass)
7676
val elemtp = elemtp0 match {
7777
case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) =>

tests/neg/isRef.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait Foo {
2+
type A = (Any { type T = Int })
3+
type B = (Any { type S = String })
4+
def a: A
5+
def b: B
6+
def aandb: A & B = b // error: found: B, required: A & B
7+
}

tests/pos/isRef.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
trait Foo {
2+
type A = (Any { type T = Int })
3+
type B = (Any { type S = String })
4+
def b: B
5+
def aorb: A | B = b
6+
}

0 commit comments

Comments
 (0)