Skip to content

Commit 50b0b4d

Browse files
committed
Drop some BiTypeMaps
Also simplify BiTypeMap: the special cases for TypeParamRefs with underlying NoType are no linger needed since isTrackableRef was changed to include these TypeParamRefs.
1 parent c662bc3 commit 50b0b4d

File tree

8 files changed

+46
-185
lines changed

8 files changed

+46
-185
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ object ccConfig:
5555

5656
/** Not used currently. Handy for trying out new features */
5757
def newScheme(using Context): Boolean =
58-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
58+
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.8`)
5959

6060
end ccConfig
6161

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,6 @@ sealed abstract class CaptureSet extends Showable:
320320
* sound nor complete.
321321
*/
322322
def map(tm: TypeMap)(using Context): CaptureSet =
323-
def freeze() = this match
324-
case self: Var if !isConst && ccConfig.newScheme =>
325-
if tm.variance < 0 then self.solve()
326-
else self.markSolved(provisional = true)
327-
case _ =>
328323
tm match
329324
case tm: BiTypeMap =>
330325
val mappedElems = elems.map(tm.forward)
@@ -341,7 +336,7 @@ sealed abstract class CaptureSet extends Showable:
341336
if isConst then
342337
if mapped.isConst && mapped.elems == elems && !mapped.keepAlways then this
343338
else mapped
344-
else if ccConfig.newScheme then
339+
else if true || ccConfig.newScheme then
345340
if mapped.elems == elems then this
346341
else
347342
asVar.markSolved(provisional = true)
@@ -458,7 +453,10 @@ object CaptureSet:
458453
def isProvisionallySolved(using Context) = false
459454

460455
def addThisElem(elem: CaptureRef)(using Context, VarState): CompareResult =
461-
addIfHiddenOrFail(elem)
456+
val res = addIfHiddenOrFail(elem)
457+
if !res.isOK && this.isProvisionallySolved then
458+
println(i"Cannot add $elem to provisionally solved $this")
459+
res
462460

463461
def addDependent(cs: CaptureSet)(using Context, VarState) = CompareResult.OK
464462

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

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -92,44 +92,6 @@ object CheckCaptures:
9292
override def toString = "SubstParamsMap"
9393
end SubstParamsMap
9494

95-
/** Used for substituting parameters in a special case: when all actual arguments
96-
* are mutually distinct capabilities.
97-
*/
98-
final class SubstParamsBiMap(from: LambdaType, to: List[Type])(using Context)
99-
extends BiTypeMap:
100-
thisMap =>
101-
102-
def apply(tp: Type): Type = tp match
103-
case tp: ParamRef =>
104-
if tp.binder == from then to(tp.paramNum) else tp
105-
case tp: NamedType =>
106-
if tp.prefix `eq` NoPrefix then tp
107-
else tp.derivedSelect(apply(tp.prefix))
108-
case _: ThisType =>
109-
tp
110-
case _ =>
111-
mapOver(tp)
112-
override def toString = "SubstParamsBiMap"
113-
114-
lazy val inverse = new BiTypeMap:
115-
def apply(tp: Type): Type = tp match
116-
case tp: NamedType =>
117-
var idx = 0
118-
var to1 = to
119-
while idx < to.length && (tp ne to(idx)) do
120-
idx += 1
121-
to1 = to1.tail
122-
if idx < to.length then from.paramRefs(idx)
123-
else if tp.prefix `eq` NoPrefix then tp
124-
else tp.derivedSelect(apply(tp.prefix))
125-
case _: ThisType =>
126-
tp
127-
case _ =>
128-
mapOver(tp)
129-
override def toString = "SubstParamsBiMap.inverse"
130-
def inverse = thisMap
131-
end SubstParamsBiMap
132-
13395
/** A prototype that indicates selection with an immutable value */
13496
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto
13597

@@ -825,19 +787,11 @@ class CheckCaptures extends Recheck, SymTransformer:
825787
* This means
826788
* - Instantiate result type with actual arguments
827789
* - if `sym` is a constructor, refine its type with `refineInstanceType`
828-
* If all argument types are mutually different trackable capture references, use a BiTypeMap,
829-
* since that is more precise. Otherwise use a normal idempotent map, which might lose information
830-
* in the case where the result type contains captureset variables that are further
831-
* constrained afterwards.
832790
*/
833791
override def instantiate(mt: MethodType, argTypes: List[Type], sym: Symbol)(using Context): Type =
834792
val ownType =
835-
if !mt.isResultDependent then
836-
mt.resType
837-
else if argTypes.forall(_.isTrackableRef) && isDistinct(argTypes) then
838-
SubstParamsBiMap(mt, argTypes)(mt.resType)
839-
else
840-
SubstParamsMap(mt, argTypes)(mt.resType)
793+
if !mt.isResultDependent then mt.resType
794+
else SubstParamsMap(mt, argTypes)(mt.resType)
841795
if sym.isConstructor then refineConstructorInstance(ownType, mt, argTypes, sym)
842796
else ownType
843797

@@ -958,8 +912,10 @@ class CheckCaptures extends Recheck, SymTransformer:
958912
case FunctionOrMethod(argTypes, resType) =>
959913
assert(params.hasSameLengthAs(argTypes), i"$mdef vs $pt, ${params}")
960914
for (argType, param) <- argTypes.lazyZip(params) do
961-
//println(i"compare $argType against $param")
962-
checkConformsExpr(argType, root.freshToCap(param.asInstanceOf[ValDef].tpt.nuType), param)
915+
val paramTpt = param.asInstanceOf[ValDef].tpt
916+
val paramType = root.freshToCap(paramTpt.nuType)
917+
checkConformsExpr(argType, paramType, param)
918+
.showing(i"compared expected closure formal $argType against $param with ${paramTpt.nuType}", capt)
963919
if ccConfig.preTypeClosureResults && !(isEtaExpansion(mdef) && ccConfig.handleEtaExpansionsSpecially) then
964920
// Check whether the closure's result conforms to the expected type
965921
// This constrains parameter types of the closure which can give better

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

Lines changed: 21 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -468,44 +468,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
468468
else tp3
469469
end transformExplicitType
470470

471-
/** Substitute parameter symbols in `from` to paramRefs in corresponding
472-
* method or poly types `to`. We use a single BiTypeMap to do everything.
473-
* @param from a list of lists of type or term parameter symbols of a curried method
474-
* @param to a list of method or poly types corresponding one-to-one to the parameter lists
475-
*/
476-
private class SubstParams(from: List[List[Symbol]], to: List[LambdaType])(using Context)
477-
extends DeepTypeMap, BiTypeMap:
478-
479-
def apply(t: Type): Type = t match
480-
case t: NamedType =>
481-
if t.prefix == NoPrefix then
482-
val sym = t.symbol
483-
def outer(froms: List[List[Symbol]], tos: List[LambdaType]): Type =
484-
def inner(from: List[Symbol], to: List[ParamRef]): Type =
485-
if from.isEmpty then outer(froms.tail, tos.tail)
486-
else if sym eq from.head then to.head
487-
else inner(from.tail, to.tail)
488-
if tos.isEmpty then t
489-
else inner(froms.head, tos.head.paramRefs)
490-
outer(from, to)
491-
else t.derivedSelect(apply(t.prefix))
492-
case _ =>
493-
mapOver(t)
494-
495-
lazy val inverse = new BiTypeMap:
496-
override def toString = "SubstParams.inverse"
497-
def apply(t: Type): Type = t match
498-
case t: ParamRef =>
499-
def recur(from: List[LambdaType], to: List[List[Symbol]]): Type =
500-
if from.isEmpty then t
501-
else if t.binder eq from.head then to.head(t.paramNum).namedType
502-
else recur(from.tail, to.tail)
503-
recur(to, from)
504-
case _ =>
505-
mapOver(t)
506-
def inverse = SubstParams.this
507-
end SubstParams
508-
509471
/** Update info of `sym` for CheckCaptures phase only */
510472
private def updateInfo(sym: Symbol, info: Type)(using Context) =
511473
toBeUpdated += sym
@@ -664,44 +626,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
664626
def signatureChanges =
665627
tree.tpt.hasNuType || paramSignatureChanges
666628

667-
// Replace an existing symbol info with inferred types where capture sets of
668-
// TypeParamRefs and TermParamRefs are put in correspondence by BiTypeMaps with the
669-
// capture sets of the types of the method's parameter symbols and result type.
670-
def integrateRT(
671-
info: Type, // symbol info to replace
672-
psymss: List[List[Symbol]], // the local (type and term) parameter symbols corresponding to `info`
673-
resType: Type, // the locally computed return type
674-
prevPsymss: List[List[Symbol]], // the local parameter symbols seen previously in reverse order
675-
prevLambdas: List[LambdaType] // the outer method and polytypes generated previously in reverse order
676-
): Type =
677-
info match
678-
case mt: MethodOrPoly =>
679-
val psyms = psymss.head
680-
// TODO: the substitution does not work for param-dependent method types.
681-
// For example, `(x: T, y: x.f.type) => Unit`. In this case, when we
682-
// substitute `x.f.type`, `x` becomes a `TermParamRef`. But the new method
683-
// type is still under initialization and `paramInfos` is still `null`,
684-
// so the new `NamedType` will not have a denotation.
685-
def adaptedInfo(psym: Symbol, info: mt.PInfo): mt.PInfo = mt.companion match
686-
case mtc: MethodTypeCompanion => mtc.adaptParamInfo(psym, info).asInstanceOf[mt.PInfo]
687-
case _ => info
688-
mt.companion(mt.paramNames)(
689-
mt1 =>
690-
if !paramSignatureChanges && !mt.isParamDependent && prevLambdas.isEmpty then
691-
mt.paramInfos
692-
else
693-
val subst = SubstParams(psyms :: prevPsymss, mt1 :: prevLambdas)
694-
psyms.map(psym => adaptedInfo(psym, subst(root.freshToCap(psym.nextInfo)).asInstanceOf[mt.PInfo])),
695-
mt1 =>
696-
integrateRT(mt.resType, psymss.tail, resType, psyms :: prevPsymss, mt1 :: prevLambdas)
697-
)
698-
case info: ExprType =>
699-
info.derivedExprType(resType =
700-
integrateRT(info.resType, psymss, resType, prevPsymss, prevLambdas))
701-
case info =>
702-
if prevLambdas.isEmpty then resType
703-
else SubstParams(prevPsymss, prevLambdas)(resType)
704-
705629
def paramsToCap(mt: Type)(using Context): Type = mt match
706630
case mt: MethodType =>
707631
mt.derivedLambdaType(
@@ -714,52 +638,30 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
714638
// If there's a change in the signature, update the info of `sym`
715639
if sym.exists && signatureChanges then
716640
val updatedInfo =
717-
if ccConfig.newScheme then
718-
val paramSymss = sym.paramSymss
719-
def newInfo(using Context) = // will be run in this or next phase
720-
root.toResultInResults(report.error(_, tree.srcPos)):
721-
if sym.is(Method) then
722-
paramsToCap(methodType(paramSymss, localReturnType))
723-
else tree.tpt.nuType
724-
if tree.tpt.isInstanceOf[InferredTypeTree]
725-
&& !sym.is(Param) && !sym.is(ParamAccessor)
726-
then
727-
val prevInfo = sym.info
728-
new LazyType:
729-
def complete(denot: SymDenotation)(using Context) =
730-
assert(ctx.phase == thisPhase.next, i"$sym")
731-
sym.info = prevInfo // set info provisionally so we can analyze the symbol in recheck
732-
completeDef(tree, sym, this)
733-
sym.info = newInfo
734-
.showing(i"new info of $sym = $result", capt)
735-
else if sym.is(Method) then
736-
new LazyType:
737-
def complete(denot: SymDenotation)(using Context) =
738-
sym.info = newInfo
739-
.showing(i"new info of $sym = $result", capt)
740-
else newInfo
741-
else
742-
val newInfo =
743-
root.toResultInResults(report.error(_, tree.srcPos)):
744-
integrateRT(sym.info, sym.paramSymss, localReturnType, Nil, Nil)
745-
.showing(i"update info $sym: ${sym.info} = $result", capt)
746-
if sym.isAnonymousFunction
747-
|| sym.is(Param)
748-
|| sym.is(ParamAccessor)
749-
|| sym.isPrimaryConstructor
750-
then
751-
// closures are handled specially; the newInfo is constrained from
752-
// the expected type and only afterwards we recheck the definition
753-
newInfo
754-
else new LazyType:
755-
// infos of other methods are determined from their definitions, which
756-
// are checked on demand
641+
642+
val paramSymss = sym.paramSymss
643+
def newInfo(using Context) = // will be run in this or next phase
644+
root.toResultInResults(report.error(_, tree.srcPos)):
645+
if sym.is(Method) then
646+
paramsToCap(methodType(paramSymss, localReturnType))
647+
else tree.tpt.nuType
648+
if tree.tpt.isInstanceOf[InferredTypeTree]
649+
&& !sym.is(Param) && !sym.is(ParamAccessor)
650+
then
651+
val prevInfo = sym.info
652+
new LazyType:
757653
def complete(denot: SymDenotation)(using Context) =
758654
assert(ctx.phase == thisPhase.next, i"$sym")
759-
capt.println(i"forcing $sym, printing = ${ctx.mode.is(Mode.Printing)}")
760-
//if ctx.mode.is(Mode.Printing) then new Error().printStackTrace()
761-
sym.info = newInfo
655+
sym.info = prevInfo // set info provisionally so we can analyze the symbol in recheck
762656
completeDef(tree, sym, this)
657+
sym.info = newInfo
658+
.showing(i"new info of $sym = $result", capt)
659+
else if sym.is(Method) then
660+
new LazyType:
661+
def complete(denot: SymDenotation)(using Context) =
662+
sym.info = newInfo
663+
.showing(i"new info of $sym = $result", capt)
664+
else newInfo
763665
updateInfo(sym, updatedInfo)
764666

765667
case tree: Bind =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ object root:
188188

189189
override def toString = "CapToFresh"
190190

191-
lazy val inverse: BiTypeMap & FollowAliasesMap = new BiTypeMap with FollowAliasesMap:
191+
object inverse extends BiTypeMap, FollowAliasesMap:
192192
def apply(t: Type): Type = t match
193193
case t @ Fresh(_) => cap
194194
case t @ CapturingType(_, refs) => mapOver(t)

compiler/src/dotty/tools/dotc/core/Substituters.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ object Substituters:
180180
def apply(tp: Type): Type = subst(tp, from, to, this)(using mapCtx)
181181
}
182182

183-
final class SubstSymMap(from: List[Symbol], to: List[Symbol])(using Context) extends DeepTypeMap, BiTypeMap {
183+
final class SubstSymMap(from: List[Symbol], to: List[Symbol])(using Context) extends DeepTypeMap {
184184
def apply(tp: Type): Type = substSym(tp, from, to, this)(using mapCtx)
185185
def inverse = SubstSymMap(to, from) // implicitly requires that `to` contains no duplicates.
186186
}

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6085,9 +6085,6 @@ object Types extends TypeUtils {
60856085
def forward(ref: CaptureRef): CaptureRef =
60866086
val result = this(ref)
60876087
def ensureTrackable(tp: Type): CaptureRef = tp match
6088-
/* Issue #22437: handle case when info is not yet available during postProcess in CC setup */
6089-
case tp: (TypeParamRef | TermRef) if tp.underlying == NoType =>
6090-
tp
60916088
case tp: CaptureRef =>
60926089
if tp.isTrackableRef then tp
60936090
else ensureTrackable(tp.underlying)
@@ -6099,9 +6096,6 @@ object Types extends TypeUtils {
60996096

61006097
/** A restriction of the inverse to a function on tracked CaptureRefs */
61016098
def backward(ref: CaptureRef): CaptureRef = inverse(ref) match
6102-
/* Ensure bijection for issue #22437 fix in method forward above: */
6103-
case result: (TypeParamRef | TermRef) if result.underlying == NoType =>
6104-
result
61056099
case result: CaptureRef if result.isTrackableRef => result
61066100
end BiTypeMap
61076101

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import language.experimental.captureChecking
2+
3+
class Network
4+
5+
class Page(val nw: Network^):
6+
def render(client: Page^{nw} -> Unit) = client(this)
7+
8+
def main(net: Network^) =
9+
var page: Page{val nw: Network^{net}}^{net} = Page(net)
10+
page.render(p => ())
11+

0 commit comments

Comments
 (0)