@@ -28,6 +28,7 @@ import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind}
2828import reporting .{trace , Message , OverrideError }
2929import Annotations .Annotation
3030import Capabilities .*
31+ import Mutability .*
3132import util .common .alwaysTrue
3233import scala .annotation .constructorOnly
3334
@@ -680,12 +681,16 @@ class CheckCaptures extends Recheck, SymTransformer:
680681 if pt.select.symbol.isReadOnlyMethod then
681682 markFree(ref.readOnly, tree)
682683 else
683- markPathFree(ref.select(pt.select.symbol).asInstanceOf [TermRef ], pt.pt, pt.select)
684+ val sel = ref.select(pt.select.symbol).asInstanceOf [TermRef ]
685+ sel.recomputeDenot()
686+ // We need to do a recomputeDenot here since we have not yet properly
687+ // computed the type of the full path. This means that we erroneously
688+ // think the denotation is the same as in the previous phase so no
689+ // member computation is performed. A test case where this matters is
690+ // read-only-use.scala, where the error on r3 goes unreported.
691+ markPathFree(sel, pt.pt, pt.select)
684692 case _ =>
685- if ref.derivesFromMutable && pt.isValueType && ! pt.isMutableType then
686- markFree(ref.readOnly, tree)
687- else
688- markFree(ref, tree)
693+ markFree(ref.adjustReadOnly(pt), tree)
689694
690695 /** The expected type for the qualifier of a selection. If the selection
691696 * could be part of a capability path or is a a read-only method, we return
@@ -724,13 +729,10 @@ class CheckCaptures extends Recheck, SymTransformer:
724729 case _ => denot
725730
726731 // 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)
732+ // an exclusive reference.
733+ if tree.symbol.isUpdateMethod then
734+ checkUpdate(qualType, tree.srcPos):
735+ i " Cannot call update ${tree.symbol} of ${qualType.showRef}"
734736
735737 val origSelType = recheckSelection(tree, qualType, name, disambiguate)
736738 val selType = mapResultRoots(origSelType, tree.symbol)
@@ -997,6 +999,16 @@ class CheckCaptures extends Recheck, SymTransformer:
997999 report.error(em " $refArg is not a tracked capability " , refArg.srcPos)
9981000 case _ =>
9991001
1002+ override def recheckAssign (tree : Assign )(using Context ): Type =
1003+ val lhsType = recheck(tree.lhs, LhsProto )
1004+ recheck(tree.rhs, lhsType.widen)
1005+ lhsType match
1006+ case lhsType @ TermRef (qualType, _)
1007+ if (qualType ne NoPrefix ) && ! lhsType.symbol.is(Transparent ) =>
1008+ checkUpdate(qualType, tree.srcPos)(i " Cannot assign to field ${lhsType.name} of ${qualType.showRef}" )
1009+ case _ =>
1010+ defn.UnitType
1011+
10001012 /** Recheck Closure node: add the captured vars of the anonymoys function
10011013 * to the result type. See also `recheckClosureBlock` which rechecks the
10021014 * block containing the anonymous function and the Closure node.
@@ -1752,90 +1764,6 @@ class CheckCaptures extends Recheck, SymTransformer:
17521764 case _ => widened
17531765 case _ => widened
17541766
1755- /** If actual is a capturing type T^C extending Mutable, and expected is an
1756- * unboxed non-singleton value type not extending mutable, narrow the capture
1757- * set `C` to `ro(C)`.
1758- * The unboxed condition ensures that the expected type is not a type variable
1759- * that's upper bounded by a read-only type. In this case it would not be sound
1760- * to narrow to the read-only set, since that set can be propagated
1761- * by the type variable instantiation.
1762- */
1763- private def improveReadOnly (actual : Type , expected : Type )(using Context ): Type = reporting.trace(i " improv ro $actual vs $expected" ):
1764- actual.dealiasKeepAnnots match
1765- case actual @ CapturingType (parent, refs) =>
1766- val parent1 = improveReadOnly(parent, expected)
1767- val refs1 =
1768- if parent1.derivesFrom(defn.Caps_Mutable )
1769- && expected.isValueType
1770- && (! expected.derivesFromMutable || expected.captureSet.isAlwaysReadOnly)
1771- && ! expected.isSingleton
1772- && actual.isBoxedCapturing == expected.isBoxedCapturing
1773- then refs.readOnly
1774- else refs
1775- actual.derivedCapturingType(parent1, refs1)
1776- case actual @ FunctionOrMethod (aargs, ares) =>
1777- expected.dealias.stripCapturing match
1778- case FunctionOrMethod (eargs, eres) =>
1779- actual.derivedFunctionOrMethod(aargs, improveReadOnly(ares, eres))
1780- case _ =>
1781- actual
1782- case actual @ AppliedType (atycon, aargs) =>
1783- def improveArgs (aargs : List [Type ], eargs : List [Type ], formals : List [ParamInfo ]): List [Type ] =
1784- aargs match
1785- case aargs @ (aarg :: aargs1) =>
1786- val aarg1 =
1787- if formals.head.paramVariance.is(Covariant )
1788- then improveReadOnly(aarg, eargs.head)
1789- else aarg
1790- aargs.derivedCons(aarg1, improveArgs(aargs1, eargs.tail, formals.tail))
1791- case Nil =>
1792- aargs
1793- val expected1 = expected.dealias.stripCapturing
1794- val esym = expected1.typeSymbol
1795- expected1 match
1796- case AppliedType (etycon, eargs) =>
1797- if atycon.typeSymbol == esym then
1798- actual.derivedAppliedType(atycon,
1799- improveArgs(aargs, eargs, etycon.typeParams))
1800- else if esym.isClass then
1801- // This case is tricky: Try to lift actual to the base type with class `esym`,
1802- // improve the resulting arguments, and figure out if anything can be
1803- // deduced from that for the original arguments.
1804- actual.baseType(esym) match
1805- case base @ AppliedType (_, bargs) =>
1806- // If any of the base type arguments can be improved, check
1807- // whether they are the same as an original argument, and in this
1808- // case improve the original argument.
1809- val iargs = improveArgs(bargs, eargs, etycon.typeParams)
1810- if iargs ne bargs then
1811- val updates =
1812- for
1813- (barg, iarg) <- bargs.lazyZip(iargs)
1814- if barg ne iarg
1815- aarg <- aargs.find(_ eq barg)
1816- yield (aarg, iarg)
1817- if updates.nonEmpty then AppliedType (atycon, aargs.map(updates.toMap))
1818- else actual
1819- else actual
1820- case _ => actual
1821- else actual
1822- case _ =>
1823- actual
1824- case actual @ RefinedType (aparent, aname, ainfo) =>
1825- expected.dealias.stripCapturing match
1826- case RefinedType (eparent, ename, einfo) if aname == ename =>
1827- actual.derivedRefinedType(
1828- improveReadOnly(aparent, eparent),
1829- aname,
1830- improveReadOnly(ainfo, einfo))
1831- case _ =>
1832- actual
1833- case actual @ AnnotatedType (parent, ann) =>
1834- actual.derivedAnnotatedType(improveReadOnly(parent, expected), ann)
1835- case _ =>
1836- actual
1837- end improveReadOnly
1838-
18391767 /* Currently not needed since it forms part of `adapt`
18401768 private def improve(actual: Type, prefix: Type)(using Context): Type =
18411769 val widened = actual.widen.dealiasKeepAnnots
@@ -1872,11 +1800,11 @@ class CheckCaptures extends Recheck, SymTransformer:
18721800 // since they obscures the capturing type.
18731801 val widened = actual.widen.dealiasKeepAnnots.dropUseAndConsumeAnnots
18741802 val improvedVAR = improveCaptures(widened, actual)
1875- val improved = improveReadOnly (improvedVAR, expected)
1803+ val adaptedReadOnly = adaptReadOnly (improvedVAR, actual, expected, tree )
18761804 val adapted = adaptBoxed(
1877- improved .withReachCaptures(actual), expected, tree,
1805+ adaptedReadOnly .withReachCaptures(actual), expected, tree,
18781806 covariant = true , alwaysConst = false )
1879- if adapted eq improvedVAR // no .rd improvement , no box-adaptation
1807+ if adapted eq improvedVAR // no read-only-adaptation, no reaches added , no box-adaptation
18801808 then actual // might as well use actual instead of improved widened
18811809 else adapted.showing(i " adapt $actual vs $expected = $adapted" , capt)
18821810 end adapt
0 commit comments