Skip to content

Commit 073fe4c

Browse files
committed
Add existential capabilities, 2nd draft
1 parent 2a39069 commit 073fe4c

File tree

16 files changed

+580
-51
lines changed

16 files changed

+580
-51
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ object ccConfig:
2626
*/
2727
inline val allowUnsoundMaps = false
2828

29+
val useExistentials = false
30+
2931
/** If true, use `sealed` as encapsulation mechanism instead of the
3032
* previous global retriction that `cap` can't be boxed or unboxed.
3133
*/
@@ -532,6 +534,33 @@ object ReachCapability extends AnnotatedCapability(defn.ReachCapabilityAnnot)
532534
*/
533535
object MaybeCapability extends AnnotatedCapability(defn.MaybeCapabilityAnnot)
534536

537+
/** Offers utility method to be used for type maps that follow aliases */
538+
trait ConservativeFollowAliasMap(using Context) extends TypeMap:
539+
540+
/** If `mapped` is a type alias, apply the map to the alias, while keeping
541+
* annotations. If the result is different, return it, otherwise return `mapped`.
542+
* Furthermore, if `original` is a LazyRef or TypeVar and the mapped result is
543+
* the same as the underlying type, keep `original`. This avoids spurious differences
544+
* which would lead to spurious dealiasing in the result
545+
*/
546+
protected def applyToAlias(original: Type, mapped: Type) =
547+
val mapped1 = mapped match
548+
case t: (TypeRef | AppliedType) =>
549+
val t1 = t.dealiasKeepAnnots
550+
if t1 eq t then t
551+
else
552+
// If we see a type alias, map the alias type and keep it if it's different
553+
val t2 = apply(t1)
554+
if t2 ne t1 then t2 else t
555+
case _ =>
556+
mapped
557+
original match
558+
case original: (LazyRef | TypeVar) if mapped1 eq original.underlying =>
559+
original
560+
case _ =>
561+
mapped1
562+
end ConservativeFollowAliasMap
563+
535564
/** An extractor for all kinds of function types as well as method and poly types.
536565
* @return 1st half: The argument types or empty if this is a type function
537566
* 2nd half: The result type

compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import printing.{Showable, Printer}
1414
import printing.Texts.*
1515
import util.{SimpleIdentitySet, Property}
1616
import typer.ErrorReporting.Addenda
17+
import TypeComparer.canSubsumeExistentially
1718
import util.common.alwaysTrue
1819
import scala.collection.mutable
1920
import CCState.*
@@ -172,6 +173,7 @@ sealed abstract class CaptureSet extends Showable:
172173
x.info match
173174
case x1: CaptureRef => x1.subsumes(y)
174175
case _ => false
176+
case x: TermParamRef => canSubsumeExistentially(x, y)
175177
case _ => false
176178

177179
/** {x} <:< this where <:< is subcapturing, but treating all variables

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -612,10 +612,10 @@ class CheckCaptures extends Recheck, SymTransformer:
612612
mdef.rhs.putAttachment(ClosureBodyValue, ())
613613
case _ =>
614614

615-
// Constrain closure's parameters and result from the expected type before
616-
// rechecking the body.
617615
openClosures = (mdef.symbol, pt) :: openClosures
618616
try
617+
// Constrain closure's parameters and result from the expected type before
618+
// rechecking the body.
619619
val res = recheckClosure(expr, pt, forceDependent = true)
620620
if !isEtaExpansion(mdef) then
621621
// If closure is an eta expanded method reference it's better to not constrain
@@ -699,7 +699,7 @@ class CheckCaptures extends Recheck, SymTransformer:
699699
val localSet = capturedVars(sym)
700700
if !localSet.isAlwaysEmpty then
701701
curEnv = Env(sym, EnvKind.Regular, localSet, curEnv)
702-
inNestedLevel:
702+
inNestedLevel: // TODO: needed here?
703703
try checkInferredResult(super.recheckDefDef(tree, sym), tree)
704704
finally
705705
if !sym.isAnonymousFunction then
@@ -920,8 +920,7 @@ class CheckCaptures extends Recheck, SymTransformer:
920920
case expected @ defn.FunctionOf(args, resultType, isContextual)
921921
if defn.isNonRefinedFunction(expected) =>
922922
actual match
923-
case RefinedType(parent, nme.apply, rinfo: MethodType)
924-
if defn.isFunctionNType(actual) =>
923+
case defn.RefinedFunctionOf(rinfo: MethodType) =>
925924
depFun(args, resultType, isContextual, rinfo.paramNames)
926925
case _ => expected
927926
case _ => expected
@@ -1132,12 +1131,12 @@ class CheckCaptures extends Recheck, SymTransformer:
11321131
* @param sym symbol of the field definition that is being checked
11331132
*/
11341133
override def checkSubType(actual: Type, expected: Type)(using Context): Boolean =
1135-
val expected1 = alignDependentFunction(addOuterRefs(expected, actual), actual.stripCapturing)
1134+
val expected1 = alignDependentFunction(addOuterRefs(/*Existential.strip*/(expected), actual), actual.stripCapturing)
11361135
val actual1 =
11371136
val saved = curEnv
11381137
try
11391138
curEnv = Env(clazz, EnvKind.NestedInOwner, capturedVars(clazz), outer0 = curEnv)
1140-
val adapted = adaptBoxed(actual, expected1, srcPos, covariant = true, alwaysConst = true)
1139+
val adapted = adaptBoxed(/*Existential.strip*/(actual), expected1, srcPos, covariant = true, alwaysConst = true)
11411140
actual match
11421141
case _: MethodType =>
11431142
// We remove the capture set resulted from box adaptation for method types,

0 commit comments

Comments
 (0)