Skip to content

Always use adapted type in withDenotation #16901

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 58 additions & 32 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2501,10 +2501,48 @@ object Types {

/** A reference with the initial symbol in `symd` has an info that
* might depend on the given prefix.
* Note: If M is an abstract type or non-final term member in trait or class C,
* its info depends even on C.this if class C has a self type that refines
* the info of M.
*/
private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean =

def refines(tp: Type, name: Name): Boolean = tp match
case tp: TypeRef =>
tp.symbol match
case cls: ClassSymbol =>
val otherd = cls.nonPrivateMembersNamed(name)
otherd.exists && !otherd.containsSym(symd.symbol)
case tsym =>
refines(tsym.info.hiBound, name)
// avoid going through tp.denot, since that might call infoDependsOnPrefix again
case RefinedType(parent, rname, _) =>
rname == name || refines(parent, name)
case tp: TypeProxy =>
refines(tp.underlying, name)
case AndType(tp1, tp2) =>
refines(tp1, name) || refines(tp2, name)
case _ =>
false

def givenSelfTypeOrCompleter(cls: Symbol) = cls.infoOrCompleter match
case cinfo: ClassInfo =>
cinfo.selfInfo match
case sym: Symbol => sym.infoOrCompleter
case tpe: Type => tpe
case _ => NoType

symd.maybeOwner.membersNeedAsSeenFrom(prefix) && !symd.is(NonMember)
|| prefix.isInstanceOf[Types.ThisType] && symd.is(Opaque) // see pos/i11277.scala for a test where this matters
|| prefix.match
case prefix: Types.ThisType =>
(symd.isAbstractType
|| symd.isTerm
&& !symd.flagsUNSAFE.isOneOf(Module | Final | Param)
&& !symd.maybeOwner.isEffectivelyFinal)
&& prefix.sameThis(symd.maybeOwner.thisType)
&& refines(givenSelfTypeOrCompleter(prefix.cls), symd.name)
case _ => false
end infoDependsOnPrefix

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

/** A reference like this one, but with the given symbol, if it exists */
final def withSym(sym: Symbol)(using Context): ThisType =
if ((designator ne sym) && sym.exists) NamedType(prefix, sym).asInstanceOf[ThisType]
private def withSym(sym: Symbol)(using Context): ThisType =
if designator ne sym then NamedType(prefix, sym).asInstanceOf[ThisType]
else this

private def withName(name: Name)(using Context): ThisType =
if designator ne name then NamedType(prefix, name).asInstanceOf[ThisType]
else this

/** A reference like this one, but with the given denotation, if it exists.
* Returns a new named type with the denotation's symbol if that symbol exists, and
* one of the following alternatives applies:
* 1. The current designator is a symbol and the symbols differ, or
* 2. The current designator is a name and the new symbolic named type
* does not have a currently known denotation.
* 3. The current designator is a name and the new symbolic named type
* has the same info as the current info
* Otherwise the current denotation is overwritten with the given one.
*
* Note: (2) and (3) are a "lock in mechanism" where a reference with a name as
* designator can turn into a symbolic reference.
*
* Note: This is a subtle dance to keep the balance between going to symbolic
* references as much as we can (since otherwise we'd risk getting cycles)
* and to still not lose any type info in the denotation (since symbolic
* references often recompute their info directly from the symbol's info).
* A test case is neg/opaque-self-encoding.scala.
* Returns a new named type with the denotation's symbol as designator
* if that symbol exists and it is different from the current designator.
* Returns a new named type with the denotations's name as designator
* if the denotation is overloaded and its name is different from the
* current designator.
*/
final def withDenot(denot: Denotation)(using Context): ThisType =
if denot.exists then
val adapted = withSym(denot.symbol)
val result =
if (adapted.eq(this)
|| designator.isInstanceOf[Symbol]
|| !adapted.denotationIsCurrent
|| adapted.info.eq(denot.info))
adapted
val adapted =
if denot.symbol.exists then withSym(denot.symbol)
else if denot.isOverloaded then withName(denot.name)
else this
val lastDenot = result.lastDenotation
val lastDenot = adapted.lastDenotation
denot match
case denot: SymDenotation
if denot.validFor.firstPhaseId < ctx.phase.id
Expand All @@ -2687,15 +2713,15 @@ object Types {
// In this case the new SymDenotation might be valid for all phases, which means
// we would not recompute the denotation when travelling to an earlier phase, maybe
// in the next run. We fix that problem by creating a UniqueRefDenotation instead.
core.println(i"overwrite ${result.toString} / ${result.lastDenotation}, ${result.lastDenotation.getClass} with $denot at ${ctx.phaseId}")
result.setDenot(
core.println(i"overwrite ${adapted.toString} / ${adapted.lastDenotation}, ${adapted.lastDenotation.getClass} with $denot at ${ctx.phaseId}")
adapted.setDenot(
UniqueRefDenotation(
denot.symbol, denot.info,
Period(ctx.runId, ctx.phaseId, denot.validFor.lastPhaseId),
this.prefix))
case _ =>
result.setDenot(denot)
result.asInstanceOf[ThisType]
adapted.setDenot(denot)
adapted.asInstanceOf[ThisType]
else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala.
this

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ extends NotFoundMsg(MissingIdentID) {
}
}

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

def msg(using Context) =
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotc/pos-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ i12299a.scala
i13871.scala
i15181.scala
i15922.scala
t5031_2.scala

# Tree is huge and blows stack for printing Text
i7034.scala
Expand Down