Skip to content

Commit c3869dd

Browse files
committed
Fix #5279: Be more careful with symbolic named types
Be more where we create symbolic refs from named ones.
1 parent c63cbd5 commit c3869dd

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

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

+54-9
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,10 @@ object Types {
19171917
||
19181918
lastSymbol.infoOrCompleter.isInstanceOf[ErrorType]
19191919
||
1920+
!sym.exists
1921+
||
1922+
!lastSymbol.exists
1923+
||
19201924
sym.isPackageObject // package objects can be visited before we get around to index them
19211925
||
19221926
sym.owner != lastSymbol.owner &&
@@ -2070,17 +2074,36 @@ object Types {
20702074
else this
20712075

20722076
/** A reference like this one, but with the given denotation, if it exists.
2073-
* If the symbol of `denot` is the same as the current symbol, the denotation
2074-
* is re-used, otherwise a new one is created.
2077+
* Returns a new named type with the denotation's symbol if that symbol exists, and
2078+
* one of the following alternatives applies:
2079+
* 1. The current designator is a symbol and the symbols differ, or
2080+
* 2. The current designator is a name and the new symbolic named type
2081+
* does not have a currently known denotation.
2082+
* 3. The current designator is a name and the new symbolic named type
2083+
* has the same info as the current info
2084+
* Otherwise the current denotation is overwritten with the given one.
2085+
*
2086+
* Note: (2) and (3) are a "lock in mechanism" where a reference with a name as
2087+
* designator can turn into a symbolic reference.
2088+
*
2089+
* Note: This is a subtle dance to keep the balance between going to symbolic
2090+
* references as much as we can (since otherwise we'd risk getting cycles)
2091+
* and to still not lose any type info in the denotation (since symbolic
2092+
* references often recompute their info directly from the symbol's info).
2093+
* A test case is neg/opaque-self-encoding.scala.
20752094
*/
20762095
final def withDenot(denot: Denotation)(implicit ctx: Context): ThisType =
20772096
if (denot.exists) {
20782097
val adapted = withSym(denot.symbol)
2079-
if (adapted ne this) adapted.withDenot(denot).asInstanceOf[ThisType]
2080-
else {
2081-
setDenot(denot)
2082-
this
2083-
}
2098+
val result =
2099+
if (adapted.eq(this)
2100+
|| designator.isInstanceOf[Symbol]
2101+
|| !adapted.denotationIsCurrent
2102+
|| adapted.info.eq(denot.info))
2103+
adapted
2104+
else this
2105+
result.setDenot(denot)
2106+
result.asInstanceOf[ThisType]
20842107
}
20852108
else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala.
20862109
this
@@ -2181,6 +2204,11 @@ object Types {
21812204
override protected def designator_=(d: Designator): Unit = myDesignator = d
21822205

21832206
override def underlying(implicit ctx: Context): Type = info
2207+
2208+
/** Hook that can be called from creation methods in TermRef and TypeRef */
2209+
def validated(implicit ctx: Context): this.type = {
2210+
this
2211+
}
21842212
}
21852213

21862214
final class CachedTermRef(prefix: Type, designator: Designator, hc: Int) extends TermRef(prefix, designator) {
@@ -2197,6 +2225,23 @@ object Types {
21972225
private def assertUnerased()(implicit ctx: Context) =
21982226
if (Config.checkUnerased) assert(!ctx.phase.erasedTypes)
21992227

2228+
/** The designator to be used for a named type creation with given prefix, name, and denotation.
2229+
* This is the denotation's symbol, if it exists and the prefix is not the this type
2230+
* of the class owning the symbol. The reason for the latter qualification is that
2231+
* when re-computing the denotation of a `this.<symbol>` reference we read the
2232+
* type directly off the symbol. But the given denotation might contain a more precise
2233+
* type than what can be computed from the symbol's info. We have to create in this case
2234+
* a reference with a name as designator so that the denotation will be correctly updated in
2235+
* the future. See also NamedType#withDenot. Test case is neg/opaque-self-encoding.scala.
2236+
*/
2237+
private def designatorFor(prefix: Type, name: Name, denot: Denotation)(implicit ctx: Context): Designator = {
2238+
val sym = denot.symbol
2239+
if (sym.exists && (prefix.eq(NoPrefix) || prefix.ne(sym.owner.thisType)))
2240+
sym
2241+
else
2242+
name
2243+
}
2244+
22002245
object NamedType {
22012246
def isType(desig: Designator)(implicit ctx: Context): Boolean = desig match {
22022247
case sym: Symbol => sym.isType
@@ -2220,7 +2265,7 @@ object Types {
22202265
* from the denotation's symbol if the latter exists, or else it is the given name.
22212266
*/
22222267
def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef =
2223-
apply(prefix, if (denot.symbol.exists) denot.symbol.asTerm else name).withDenot(denot)
2268+
apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot)
22242269
}
22252270

22262271
object TypeRef {
@@ -2233,7 +2278,7 @@ object Types {
22332278
* from the denotation's symbol if the latter exists, or else it is the given name.
22342279
*/
22352280
def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef =
2236-
apply(prefix, if (denot.symbol.exists) denot.symbol.asType else name).withDenot(denot)
2281+
apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot)
22372282
}
22382283

22392284
// --- Other SingletonTypes: ThisType/SuperType/ConstantType ---------------------------

tests/neg/opaque-self-encoding.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait TS { type TX = Int }
2+
trait TT { self: { type TX = Int } =>
3+
type TX
4+
def lift(x: Int): TX = x
5+
}
6+
7+
class Test {
8+
val t = new TT {}
9+
t.lift(1): Int // error
10+
}

tests/pos/refinements.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait TS { type TX = Int }
2+
trait TT { self: { type TX = Int } =>
3+
type TX
4+
def lift(x: Int): TX = x
5+
}
6+
7+
// A more direct version
8+
9+
trait UU {
10+
type UX
11+
val u: UX
12+
val x: this.type & { type UX = Int }
13+
val y: Int = x.u
14+
val z: x.UX = y
15+
}

0 commit comments

Comments
 (0)