Skip to content

Commit 2507577

Browse files
authored
Always use adapted type in withDenotation (#16901)
When creating a NamedType with a given overloaded denotation, make sure that the type has a Name as designator. This prevents accidentally overwriting a more precise symbolic TermRef that refers to one specific alternative of the denotation. This might be enough to fix #16884. EDIT: It wasn't enough but the second commit [46e82dd](46e82dd) should fix it. The second commit never overwrites in `withDenot`. It can do that because we fix `infoDependsOnPrefix` to work correctly for abstract types that are refined in a self type. It turned out that previously we needed some TypeRefs to keep their Name designators because that way we would recompute their info with a `member` operation. If these TypeRefs had a symbol designator they would be recomputed wrongly by `symd.current` in `fromDesignator` because the preceding `infoDependsOnPrefix` test was faulty. It would be great if we could maintain the general invariant that NamedTypes with overloaded denotations always have names as designators. But that looks very hard when we take into account that we need to update named types to new runs. A type might have a single denotation in one round and an overloaded one in the next.
2 parents 0416992 + 9942821 commit 2507577

File tree

3 files changed

+60
-33
lines changed

3 files changed

+60
-33
lines changed

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

+58-32
Original file line numberDiff line numberDiff line change
@@ -2501,10 +2501,48 @@ object Types {
25012501

25022502
/** A reference with the initial symbol in `symd` has an info that
25032503
* might depend on the given prefix.
2504+
* Note: If M is an abstract type or non-final term member in trait or class C,
2505+
* its info depends even on C.this if class C has a self type that refines
2506+
* the info of M.
25042507
*/
25052508
private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean =
2509+
2510+
def refines(tp: Type, name: Name): Boolean = tp match
2511+
case tp: TypeRef =>
2512+
tp.symbol match
2513+
case cls: ClassSymbol =>
2514+
val otherd = cls.nonPrivateMembersNamed(name)
2515+
otherd.exists && !otherd.containsSym(symd.symbol)
2516+
case tsym =>
2517+
refines(tsym.info.hiBound, name)
2518+
// avoid going through tp.denot, since that might call infoDependsOnPrefix again
2519+
case RefinedType(parent, rname, _) =>
2520+
rname == name || refines(parent, name)
2521+
case tp: TypeProxy =>
2522+
refines(tp.underlying, name)
2523+
case AndType(tp1, tp2) =>
2524+
refines(tp1, name) || refines(tp2, name)
2525+
case _ =>
2526+
false
2527+
2528+
def givenSelfTypeOrCompleter(cls: Symbol) = cls.infoOrCompleter match
2529+
case cinfo: ClassInfo =>
2530+
cinfo.selfInfo match
2531+
case sym: Symbol => sym.infoOrCompleter
2532+
case tpe: Type => tpe
2533+
case _ => NoType
2534+
25062535
symd.maybeOwner.membersNeedAsSeenFrom(prefix) && !symd.is(NonMember)
2507-
|| prefix.isInstanceOf[Types.ThisType] && symd.is(Opaque) // see pos/i11277.scala for a test where this matters
2536+
|| prefix.match
2537+
case prefix: Types.ThisType =>
2538+
(symd.isAbstractType
2539+
|| symd.isTerm
2540+
&& !symd.flagsUNSAFE.isOneOf(Module | Final | Param)
2541+
&& !symd.maybeOwner.isEffectivelyFinal)
2542+
&& prefix.sameThis(symd.maybeOwner.thisType)
2543+
&& refines(givenSelfTypeOrCompleter(prefix.cls), symd.name)
2544+
case _ => false
2545+
end infoDependsOnPrefix
25082546

25092547
/** Is this a reference to a class or object member with an info that might depend
25102548
* on the prefix?
@@ -2644,40 +2682,28 @@ object Types {
26442682
}
26452683

26462684
/** A reference like this one, but with the given symbol, if it exists */
2647-
final def withSym(sym: Symbol)(using Context): ThisType =
2648-
if ((designator ne sym) && sym.exists) NamedType(prefix, sym).asInstanceOf[ThisType]
2685+
private def withSym(sym: Symbol)(using Context): ThisType =
2686+
if designator ne sym then NamedType(prefix, sym).asInstanceOf[ThisType]
2687+
else this
2688+
2689+
private def withName(name: Name)(using Context): ThisType =
2690+
if designator ne name then NamedType(prefix, name).asInstanceOf[ThisType]
26492691
else this
26502692

26512693
/** A reference like this one, but with the given denotation, if it exists.
2652-
* Returns a new named type with the denotation's symbol if that symbol exists, and
2653-
* one of the following alternatives applies:
2654-
* 1. The current designator is a symbol and the symbols differ, or
2655-
* 2. The current designator is a name and the new symbolic named type
2656-
* does not have a currently known denotation.
2657-
* 3. The current designator is a name and the new symbolic named type
2658-
* has the same info as the current info
2659-
* Otherwise the current denotation is overwritten with the given one.
2660-
*
2661-
* Note: (2) and (3) are a "lock in mechanism" where a reference with a name as
2662-
* designator can turn into a symbolic reference.
2663-
*
2664-
* Note: This is a subtle dance to keep the balance between going to symbolic
2665-
* references as much as we can (since otherwise we'd risk getting cycles)
2666-
* and to still not lose any type info in the denotation (since symbolic
2667-
* references often recompute their info directly from the symbol's info).
2668-
* A test case is neg/opaque-self-encoding.scala.
2694+
* Returns a new named type with the denotation's symbol as designator
2695+
* if that symbol exists and it is different from the current designator.
2696+
* Returns a new named type with the denotations's name as designator
2697+
* if the denotation is overloaded and its name is different from the
2698+
* current designator.
26692699
*/
26702700
final def withDenot(denot: Denotation)(using Context): ThisType =
26712701
if denot.exists then
2672-
val adapted = withSym(denot.symbol)
2673-
val result =
2674-
if (adapted.eq(this)
2675-
|| designator.isInstanceOf[Symbol]
2676-
|| !adapted.denotationIsCurrent
2677-
|| adapted.info.eq(denot.info))
2678-
adapted
2702+
val adapted =
2703+
if denot.symbol.exists then withSym(denot.symbol)
2704+
else if denot.isOverloaded then withName(denot.name)
26792705
else this
2680-
val lastDenot = result.lastDenotation
2706+
val lastDenot = adapted.lastDenotation
26812707
denot match
26822708
case denot: SymDenotation
26832709
if denot.validFor.firstPhaseId < ctx.phase.id
@@ -2687,15 +2713,15 @@ object Types {
26872713
// In this case the new SymDenotation might be valid for all phases, which means
26882714
// we would not recompute the denotation when travelling to an earlier phase, maybe
26892715
// in the next run. We fix that problem by creating a UniqueRefDenotation instead.
2690-
core.println(i"overwrite ${result.toString} / ${result.lastDenotation}, ${result.lastDenotation.getClass} with $denot at ${ctx.phaseId}")
2691-
result.setDenot(
2716+
core.println(i"overwrite ${adapted.toString} / ${adapted.lastDenotation}, ${adapted.lastDenotation.getClass} with $denot at ${ctx.phaseId}")
2717+
adapted.setDenot(
26922718
UniqueRefDenotation(
26932719
denot.symbol, denot.info,
26942720
Period(ctx.runId, ctx.phaseId, denot.validFor.lastPhaseId),
26952721
this.prefix))
26962722
case _ =>
2697-
result.setDenot(denot)
2698-
result.asInstanceOf[ThisType]
2723+
adapted.setDenot(denot)
2724+
adapted.asInstanceOf[ThisType]
26992725
else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala.
27002726
this
27012727

compiler/src/dotty/tools/dotc/reporting/messages.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ extends NotFoundMsg(MissingIdentID) {
245245
}
246246
}
247247

248-
class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context)
248+
class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context)
249249
extends TypeMismatchMsg(found, expected)(TypeMismatchID):
250250

251251
def msg(using Context) =

compiler/test/dotc/pos-test-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ i12299a.scala
1919
i13871.scala
2020
i15181.scala
2121
i15922.scala
22+
t5031_2.scala
2223

2324
# Tree is huge and blows stack for printing Text
2425
i7034.scala

0 commit comments

Comments
 (0)