From 0565c787f77b615ed1eb9aa66c47c732c6bf40b1 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 14 Nov 2023 22:58:06 +0100 Subject: [PATCH 1/2] Don't follow opaque aliases when transforming sym info for cc Don't follow opaque aliases when transforming symbol info in Setup phase. --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 2 +- .../dotty/tools/dotc/core/Denotations.scala | 10 +++---- .../src/dotty/tools/dotc/core/Types.scala | 30 +++++++++++++++---- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 ++-- tests/pos/i18909.scala | 4 +++ 5 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 tests/pos/i18909.scala diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 1620e54c0530..0b2630a99e7d 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index efbdfeb49246..99156df9d86e 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -992,18 +992,18 @@ object Denotations { if (symbol == NoSymbol) symbol.toString else s"" - 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 } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8572cacb0eb8..ccb50bfb221b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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. */ @@ -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 opqaue 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 { @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 3d3481f371f7..c366146a789e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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 diff --git a/tests/pos/i18909.scala b/tests/pos/i18909.scala new file mode 100644 index 000000000000..87d1befc6c41 --- /dev/null +++ b/tests/pos/i18909.scala @@ -0,0 +1,4 @@ +import language.experimental.captureChecking +def foo(): Unit = + val r1: IArray[String] = ??? + val r2: String = IArray.head(r1) \ No newline at end of file From d713d3d00ad99cd9f999d158f0af73f5af267a17 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 15 Nov 2023 09:59:54 +0100 Subject: [PATCH 2/2] Avoid alias opaques in two other places during Setup --- compiler/src/dotty/tools/dotc/cc/Setup.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 0b2630a99e7d..690ed464eec2 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -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) => @@ -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) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ccb50bfb221b..c4be8af1cb51 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1489,7 +1489,7 @@ object Types { * 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 opqaue aliases when doing so. + * We have to be careful not to accidentally reveal opaque aliases when doing so. */ final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true)