Skip to content

Don't follow opaque aliases when transforming sym info for cc #18929

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 2 commits into from
Nov 15, 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
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)