Skip to content

Commit 606e37a

Browse files
committed
Merge branch 'main' into dedented-strings
2 parents 4f2c7f4 + a9c731c commit 606e37a

File tree

291 files changed

+4255
-2393
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

291 files changed

+4255
-2393
lines changed

.github/workflows/stdlib.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ jobs:
531531
distribution: 'temurin'
532532
java-version: 17
533533
cache: 'sbt'
534-
- uses: actions/setup-node@v5
534+
- uses: actions/setup-node@v6
535535
with:
536536
node-version: '24.x'
537537
- uses: sbt/setup-sbt@v1

community-build/src/scala/dotty/communitybuild/projects.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ final case class MillCommunityProject(
7575
// uncomment once mill is released
7676
// if ignoreDocs then null else s"$baseCommand.docJar"
7777
override val runCommandsArgs = List("-i", "-D", s"dottyVersion=$compilerVersion")
78-
override val environment = Map("MILL_VERSION" -> "0.10.5")
7978

8079
final case class SbtCommunityProject(
8180
project: String,

community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ class CommunityBuildTestC:
9191
@Test def scalatestplusScalacheck = projects.scalatestplusScalacheck.run()
9292
@Test def scalaXml = projects.scalaXml.run()
9393
@Test def scalaz = projects.scalaz.run()
94-
@Test def scas = projects.scas.run()
94+
// mill 1.0.5 fails to build, cannot find scala-library artifact on classpath
95+
// @Test def scas = projects.scas.run()
9596
@Test def sconfig = projects.sconfig.run()
9697
@Test def shapeless3 = projects.shapeless3.run()
9798
@Test def sourcecode = projects.sourcecode.run()

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.io.{AbstractFile, FileExtension}
1010
import reporting.*
1111
import core.Decorators.*
1212
import config.Feature
13+
import util.chaining.*
1314

1415
import scala.util.control.NonFatal
1516
import fromtasty.{TASTYCompiler, TastyFileUtil}
@@ -83,15 +84,26 @@ class Driver {
8384
MacroClassLoader.init(ictx)
8485
Positioned.init(using ictx)
8586

86-
inContext(ictx) {
87+
inContext(ictx):
8788
if !ctx.settings.XdropComments.value || ctx.settings.XreadComments.value then
8889
ictx.setProperty(ContextDoc, new ContextDocstrings)
8990
val fileNamesOrNone = command.checkUsage(summary, sourcesRequired)(using ctx.settings)(using ctx.settingsState)
90-
fileNamesOrNone.map { fileNames =>
91+
fileNamesOrNone.map: fileNames =>
9192
val files = fileNames.map(ctx.getFile)
9293
(files, fromTastySetup(files))
93-
}
94-
}
94+
.tap: _ =>
95+
if !ctx.settings.Yreporter.isDefault then
96+
ctx.settings.Yreporter.value match
97+
case "help" =>
98+
case reporterClassName =>
99+
try
100+
Class.forName(reporterClassName).getDeclaredConstructor().newInstance() match
101+
case userReporter: Reporter =>
102+
ictx.setReporter(userReporter)
103+
case badReporter => report.error:
104+
em"Not a reporter: ${ctx.settings.Yreporter.value}"
105+
catch case e: ReflectiveOperationException => report.error:
106+
em"Could not create reporter ${ctx.settings.Yreporter.value}: ${e}"
95107
}
96108

97109
/** Setup extra classpath of tasty and jar files */

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ object desugar {
226226
paramss = (setterParam :: Nil) :: Nil,
227227
tpt = TypeTree(defn.UnitType),
228228
rhs = setterRhs
229-
).withMods((vdef.mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
229+
).withMods((vdef.mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy | Transparent))
230230
.dropEndMarker() // the end marker should only appear on the getter definition
231231
Thicket(vdef1, setter)
232232
else vdef1
@@ -1267,25 +1267,27 @@ object desugar {
12671267
else tree
12681268
}
12691269

1270-
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1271-
1272-
def check(name: Name, errSpan: Span): Unit = name match
1273-
case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) =>
1274-
report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan))
1275-
case _ =>
1276-
1277-
def loop(part: RefTree): Unit = part match
1278-
case part @ Ident(name) => check(name, part.span)
1279-
case part @ Select(qual: RefTree, name) =>
1280-
check(name, part.nameSpan)
1281-
loop(qual)
1270+
def checkSimplePackageName(name: Name, errSpan: Span, source: SourceFile, isPackageObject: Boolean)(using Context) =
1271+
if !ctx.isAfterTyper then
1272+
name match
1273+
case name: SimpleName if (isPackageObject || !errSpan.isSynthetic) && name.exists(Chars.willBeEncoded) =>
1274+
report.warning(EncodedPackageName(name), source.atSpan(errSpan))
12821275
case _ =>
12831276

1277+
def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit =
1278+
def check(name: Name, errSpan: Span) = checkSimplePackageName(name, errSpan, mdef.source, isPackageObject = false)
12841279
mdef match
1285-
case pdef: PackageDef => loop(pdef.pid)
1286-
case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan)
1287-
case _ =>
1288-
end checkPackageName
1280+
case pdef: PackageDef =>
1281+
def loop(part: RefTree): Unit = part match
1282+
case part @ Ident(name) => check(name, part.span)
1283+
case part @ Select(qual: RefTree, name) =>
1284+
check(name, part.nameSpan)
1285+
loop(qual)
1286+
case _ =>
1287+
loop(pdef.pid)
1288+
case mdef: ModuleDef if mdef.mods.is(Package) =>
1289+
check(mdef.name, mdef.nameSpan)
1290+
case _ =>
12891291

12901292
/** The normalized name of `mdef`. This means
12911293
* 1. Check that the name does not redefine a Scala core class.

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

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,6 @@ extension (tp: Type)
352352
case _ =>
353353
false
354354

355-
/** Is this a type extending `Mutable` that has update methods? */
356-
def isMutableType(using Context): Boolean =
357-
tp.derivesFrom(defn.Caps_Mutable)
358-
&& tp.membersBasedOnFlags(Mutable | Method, EmptyFlags)
359-
.exists(_.hasAltWith(_.symbol.isUpdateMethod))
360-
361355
/** Is this a reference to caps.cap? Note this is _not_ the GlobalCap capability. */
362356
def isCapRef(using Context): Boolean = tp match
363357
case tp: TermRef => tp.name == nme.CAPTURE_ROOT && tp.symbol == defn.captureRoot
@@ -629,16 +623,6 @@ extension (sym: Symbol)
629623
sym.hasAnnotation(defn.ConsumeAnnot)
630624
|| sym.info.hasAnnotation(defn.ConsumeAnnot)
631625

632-
def isUpdateMethod(using Context): Boolean =
633-
sym.isAllOf(Mutable | Method, butNot = Accessor)
634-
635-
def isReadOnlyMethod(using Context): Boolean =
636-
sym.is(Method, butNot = Mutable | Accessor) && sym.owner.derivesFrom(defn.Caps_Mutable)
637-
638-
def isInReadOnlyMethod(using Context): Boolean =
639-
if sym.is(Method) && sym.owner.isClass then isReadOnlyMethod
640-
else sym.owner.isInReadOnlyMethod
641-
642626
def qualString(prefix: String)(using Context): String =
643627
if !sym.exists then ""
644628
else if sym.isAnonymousFunction then i" $prefix enclosing function"

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,15 @@ sealed abstract class CaptureSet extends Showable:
6060
def mutability_=(x: Mutability): Unit =
6161
myMut = x
6262

63-
/** Mark this capture set as belonging to a Mutable type. */
64-
def setMutable()(using Context): Unit
63+
/** Mark this capture set as belonging to a Mutable type. Called when a new
64+
* CapturingType is formed. This is different from the setter `mutability_=`
65+
* in that it be defined with different behaviors:
66+
*
67+
* - set mutability to Mutable (for normal Vars)
68+
* - take mutability from the set's sources (for DerivedVars)
69+
* - compute mutability on demand based on mutability of elements (for Consts)
70+
*/
71+
def associateWithMutable()(using Context): Unit
6572

6673
/** Is this capture set constant (i.e. not an unsolved capture variable)?
6774
* Solved capture variables count as constant.
@@ -610,7 +617,7 @@ object CaptureSet:
610617

611618
private var isComplete = true
612619

613-
def setMutable()(using Context): Unit =
620+
def associateWithMutable()(using Context): Unit =
614621
if !elems.isEmpty then
615622
isComplete = false // delay computation of Mutability status
616623

@@ -630,9 +637,9 @@ object CaptureSet:
630637
else ""
631638

632639
private def capImpliedByCapability(parent: Type)(using Context): Capability =
633-
if parent.derivesFromExclusive then GlobalCap.readOnly else GlobalCap
640+
if parent.derivesFromMutable then GlobalCap.readOnly else GlobalCap
634641

635-
/* The same as {cap.rd} but generated implicitly for references of Capability subtypes.
642+
/* The same as {cap} but generated implicitly for references of Capability subtypes.
636643
*/
637644
class CSImpliedByCapability(parent: Type)(using @constructorOnly ctx: Context)
638645
extends Const(SimpleIdentitySet(capImpliedByCapability(parent)))
@@ -705,7 +712,7 @@ object CaptureSet:
705712
*/
706713
var deps: Deps = SimpleIdentitySet.empty
707714

708-
def setMutable()(using Context): Unit =
715+
def associateWithMutable()(using Context): Unit =
709716
mutability = Mutable
710717

711718
def isConst(using Context) = solved >= ccState.iterationId
@@ -1028,6 +1035,9 @@ object CaptureSet:
10281035

10291036
addAsDependentTo(source)
10301037

1038+
/** Mutability is same as in source, except for readOnly */
1039+
override def associateWithMutable()(using Context): Unit = ()
1040+
10311041
override def mutableToReader(origin: CaptureSet)(using Context): Boolean =
10321042
super.mutableToReader(origin)
10331043
&& ((origin eq source) || source.mutableToReader(this))
@@ -1364,7 +1374,7 @@ object CaptureSet:
13641374
def description(using Context): String =
13651375
def ofType(tp: Type) = if tp.exists then i"of the mutable type $tp" else "of a mutable type"
13661376
i"""$cs is an exclusive capture set ${ofType(hi)},
1367-
|it cannot subsume a read-only capture set ${ofType(lo)}."""
1377+
|it cannot subsume a read-only capture set ${ofType(lo)}"""
13681378

13691379
/** A VarState serves as a snapshot mechanism that can undo
13701380
* additions of elements or super sets if an operation fails

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ object CapturingType:
3939
case parent @ CapturingType(parent1, refs1) if boxed || !parent.isBoxed =>
4040
apply(parent1, refs ++ refs1, boxed)
4141
case _ =>
42-
if parent.derivesFromMutable then refs.setMutable()
42+
if parent.derivesFromMutable then refs.associateWithMutable()
4343
refs.adoptClassifier(parent.classifier)
4444
AnnotatedType(parent, CaptureAnnotation(refs, boxed)(defn.RetainsAnnot))
4545

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

Lines changed: 27 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind}
2828
import reporting.{trace, Message, OverrideError}
2929
import Annotations.Annotation
3030
import Capabilities.*
31+
import Mutability.*
3132
import util.common.alwaysTrue
3233
import 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

Comments
 (0)