@@ -680,12 +680,20 @@ class CheckCaptures extends Recheck, SymTransformer:
680680 if pt.select.symbol.isReadOnlyMethod then
681681 markFree(ref.readOnly, tree)
682682 else
683- markPathFree(ref.select(pt.select.symbol).asInstanceOf [TermRef ], pt.pt, pt.select)
683+ val sel = ref.select(pt.select.symbol).asInstanceOf [TermRef ]
684+ sel.recomputeDenot()
685+ // We need to do a recomputeDenot here since we have not yet properly
686+ // computed the type of the full path. This means that we erroneously
687+ // think the denotation is the same as in the previous phase so no
688+ // member computation is performed. A test case where this matters is
689+ // read-only-use.scala, where the error on r3 goes unreported.
690+ markPathFree(sel, pt.pt, pt.select)
684691 case _ =>
685- if ref.derivesFromMutable && pt.isValueType && ! pt.isMutableType then
686- markFree(ref.readOnly, tree)
687- else
688- markFree(ref, tree)
692+ if ref.derivesFromMutable then
693+ if pt.isValueType && ! pt.isMutableType || ref.exclusivityInContext != Exclusivity .OK
694+ then markFree(ref.readOnly, tree)
695+ else markFree(ref, tree)
696+ else markFree(ref, tree)
689697
690698 /** The expected type for the qualifier of a selection. If the selection
691699 * could be part of a capability path or is a a read-only method, we return
@@ -724,13 +732,10 @@ class CheckCaptures extends Recheck, SymTransformer:
724732 case _ => denot
725733
726734 // Don't allow update methods to be called unless the qualifier captures
727- // an exclusive reference. TODO This should probably rolled into
728- // qualifier logic once we have it.
729- if tree.symbol.isUpdateMethod && ! qualType.captureSet.isExclusive then
730- report.error(
731- em """ cannot call update ${tree.symbol} from $qualType,
732- |since its capture set ${qualType.captureSet} is read-only """ ,
733- tree.srcPos)
735+ // an exclusive reference.
736+ if tree.symbol.isUpdateMethod then
737+ checkUpdate(qualType, tree.srcPos):
738+ i " Cannot call update ${tree.symbol} of ${qualType.showRef}"
734739
735740 val origSelType = recheckSelection(tree, qualType, name, disambiguate)
736741 val selType = mapResultRoots(origSelType, tree.symbol)
@@ -768,6 +773,12 @@ class CheckCaptures extends Recheck, SymTransformer:
768773 selType
769774 }// .showing(i"recheck sel $tree, $qualType = $result")
770775
776+ def checkUpdate (qualType : Type , pos : SrcPos )(msg : => String )(using Context ): Unit =
777+ qualType.exclusivityInContext match
778+ case Exclusivity .OK =>
779+ case err =>
780+ report.error(em " $msg\n since ${err.description(qualType)}. " , pos)
781+
771782 /** Recheck applications, with special handling of unsafeAssumePure.
772783 * More work is done in `recheckApplication`, `recheckArg` and `instantiate` below.
773784 */
@@ -997,6 +1008,16 @@ class CheckCaptures extends Recheck, SymTransformer:
9971008 report.error(em " $refArg is not a tracked capability " , refArg.srcPos)
9981009 case _ =>
9991010
1011+ override def recheckAssign (tree : Assign )(using Context ): Type =
1012+ val lhsType = recheck(tree.lhs, LhsProto )
1013+ recheck(tree.rhs, lhsType.widen)
1014+ lhsType match
1015+ case lhsType @ TermRef (qualType, _)
1016+ if (qualType ne NoPrefix ) && ! lhsType.symbol.is(Transparent ) =>
1017+ checkUpdate(qualType, tree.srcPos)(i " Cannot assign to field ${lhsType.name} of ${qualType.showRef}" )
1018+ case _ =>
1019+ defn.UnitType
1020+
10001021 /** Recheck Closure node: add the captured vars of the anonymoys function
10011022 * to the result type. See also `recheckClosureBlock` which rechecks the
10021023 * block containing the anonymous function and the Closure node.
@@ -1836,6 +1857,17 @@ class CheckCaptures extends Recheck, SymTransformer:
18361857 actual
18371858 end improveReadOnly
18381859
1860+ def adaptReadOnly (improved : Type , original : Type , expected : Type , tree : Tree )(using Context ): Type = improved match
1861+ case improved @ CapturingType (parent, refs)
1862+ if parent.derivesFrom(defn.Caps_Mutable )
1863+ && expected.isValueType
1864+ && refs.isExclusive
1865+ && ! original.exclusivityInContext.isOK =>
1866+ improved.derivedCapturingType(parent, refs.readOnly)
1867+ .showing(i " Adapted readonly $improved for $tree with original = $original in ${ctx.owner} --> $result" , capt)
1868+ case _ =>
1869+ improved
1870+
18391871 /* Currently not needed since it forms part of `adapt`
18401872 private def improve(actual: Type, prefix: Type)(using Context): Type =
18411873 val widened = actual.widen.dealiasKeepAnnots
@@ -1873,10 +1905,11 @@ class CheckCaptures extends Recheck, SymTransformer:
18731905 val widened = actual.widen.dealiasKeepAnnots.dropUseAndConsumeAnnots
18741906 val improvedVAR = improveCaptures(widened, actual)
18751907 val improved = improveReadOnly(improvedVAR, expected)
1908+ val adaptedReadOnly = adaptReadOnly(improved, actual, expected, tree)
18761909 val adapted = adaptBoxed(
1877- improved .withReachCaptures(actual), expected, tree,
1910+ adaptedReadOnly .withReachCaptures(actual), expected, tree,
18781911 covariant = true , alwaysConst = false )
1879- if adapted eq improvedVAR // no .rd improvement, no box-adaptation
1912+ if adapted eq improvedVAR // no .rd improvement or adaptation , no box-adaptation
18801913 then actual // might as well use actual instead of improved widened
18811914 else adapted.showing(i " adapt $actual vs $expected = $adapted" , capt)
18821915 end adapt
0 commit comments