Skip to content

Commit

Permalink
Don't follow opaque aliases when transforming sym info for cc (#18929)
Browse files Browse the repository at this point in the history
Don't follow opaque aliases when transforming symbol info in Setup
phase.

Fixes #18909
  • Loading branch information
Linyxus authored Nov 15, 2023
2 parents 41740f4 + d713d3d commit 0420aaf
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 16 deletions.
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
* convert it to be boxed.
*/
private def box(tp: Type)(using Context): Type =
def recur(tp: Type): Type = tp.dealiasKeepAnnots match
def recur(tp: Type): Type = tp.dealiasKeepAnnotsAndOpaques match
case tp @ CapturingType(parent, refs) =>
if tp.isBoxed then tp else tp.boxed
case tp @ AnnotatedType(parent, ann) =>
Expand Down Expand Up @@ -574,7 +574,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
if sym.isClass then
!sym.isPureClass && sym != defn.AnyClass
else
val tp1 = tp.dealiasKeepAnnots
val tp1 = tp.dealiasKeepAnnotsAndOpaques
if tp1 ne tp then needsVariable(tp1)
else instanceCanBeImpure(tp1)
case tp: (RefinedOrRecType | MatchType) =>
Expand Down Expand Up @@ -640,7 +640,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
def maybeAdd(target: Type, fallback: Type) =
if needsVariable(target) then CapturingType(target, addedSet(target))
else fallback
val dealiased = tp.dealiasKeepAnnots
val dealiased = tp.dealiasKeepAnnotsAndOpaques
if dealiased ne tp then
val transformed = transformInferredType(dealiased)
maybeAdd(transformed, if transformed ne dealiased then transformed else tp)
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -992,18 +992,18 @@ object Denotations {
if (symbol == NoSymbol) symbol.toString
else s"<SingleDenotation of type $infoOrCompleter>"

def definedPeriodsString: String = {
/** Show all defined periods and the info of the denotation at each */
def definedPeriodsString(using Context): String = {
var sb = new StringBuilder()
var cur = this
var cnt = 0
while ({
sb.append(" " + cur.validFor)
while
sb.append(i" ${cur.validFor.toString}:${cur.infoOrCompleter}")
cur = cur.nextInRun
cnt += 1
if (cnt > MaxPossiblePhaseId) { sb.append(" ..."); cur = this }
cur ne this
})
()
do ()
sb.toString
}

Expand Down
30 changes: 25 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1460,13 +1460,13 @@ object Types {
case _ => this
}

/** Follow aliases and dereferences LazyRefs, annotated types and instantiated
/** Follow aliases and dereference LazyRefs, annotated types and instantiated
* TypeVars until type is no longer alias type, annotated type, LazyRef,
* or instantiated type variable.
*/
final def dealias(using Context): Type = dealias1(keepNever, keepOpaques = false)

/** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
/** Follow aliases and dereference LazyRefs and instantiated TypeVars until type
* is no longer alias type, LazyRef, or instantiated type variable.
* Goes through annotated types and rewraps annotations on the result.
*/
Expand All @@ -1475,12 +1475,30 @@ object Types {
/** Like `dealiasKeepAnnots`, but keeps only refining annotations */
final def dealiasKeepRefiningAnnots(using Context): Type = dealias1(keepIfRefining, keepOpaques = false)

/** Follow non-opaque aliases and dereferences LazyRefs, annotated types and instantiated
* TypeVars until type is no longer alias type, annotated type, LazyRef,
* or instantiated type variable.
/** Like dealias, but does not follow aliases if symbol is Opaque. This is
* necessary if we want to look at the info of a symbol containing opaque
* type aliases but pretend "it's from the outside". For instance, consider:
*
* opaque type IArray[T] = Array[? <: T]
* object IArray:
* def head[T](xs: IArray[T]): T = ???
*
* If we dealias types in the info of `head`, those types appear with prefix
* IArray.this, where IArray's self type is `IArray { type IArray[T] = Array[? <: T] }`.
* Hence, if we see IArray it will appear as an alias of [T] =>> Array[? <: T].
* But if we want to see the type from the outside of object IArray we need to
* suppress this dealiasing. A test case where this matters is i18909.scala.
* Here, we dealias symbol infos at the start of capture checking in operation `fluidify`.
* We have to be careful not to accidentally reveal opaque aliases when doing so.
*/
final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true)

/** Like dealiasKeepAnnots, but does not follow opaque aliases. See `dealiasKeepOpaques`
* for why this is sometimes necessary.
*/
final def dealiasKeepAnnotsAndOpaques(using Context): Type =
dealias1(keepAlways, keepOpaques = true)

/** Approximate this type with a type that does not contain skolem types. */
final def deskolemized(using Context): Type =
val deskolemizer = new ApproximatingTypeMap {
Expand Down Expand Up @@ -5351,6 +5369,8 @@ object Types {
case that: AliasingBounds => this.isTypeAlias == that.isTypeAlias && alias.eq(that.alias)
case _ => false
}

override def toString = s"${getClass.getSimpleName}($alias)"
}

/** = T
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,9 @@ class TreeUnpickler(reader: TastyReader,
else EmptyValDef
cls.setNoInitsFlags(parentsKind(parents), bodyFlags)
cls.info = ClassInfo(
cls.owner.thisType, cls, parentTypes, cls.unforcedDecls,
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe)
.integrateOpaqueMembers
cls.owner.thisType, cls, parentTypes, cls.unforcedDecls,
selfInfo = if (self.isEmpty) NoType else self.tpt.tpe
).integrateOpaqueMembers
val constr = readIndexedDef().asInstanceOf[DefDef]
val mappedParents: LazyTreeList =
if parents.exists(_.isInstanceOf[InferredTypeTree]) then
Expand Down
4 changes: 4 additions & 0 deletions tests/pos/i18909.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import language.experimental.captureChecking
def foo(): Unit =
val r1: IArray[String] = ???
val r2: String = IArray.head(r1)

0 comments on commit 0420aaf

Please sign in to comment.