Skip to content

Commit a6dfec2

Browse files
committed
Merge branch 'main' into export-diagnostics
2 parents 4f6a092 + 182331b commit a6dfec2

File tree

174 files changed

+13391
-1041
lines changed

Some content is hidden

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

174 files changed

+13391
-1041
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ object projects:
372372
sbtTestCommand = """set deriving/scalacOptions -= "-Xfatal-warnings"; set typeable/scalacOptions -= "-Xfatal-warnings"; test""",
373373
// selectively disable -Xfatal-warnings due to deprecations
374374
sbtDocCommand = forceDoc("typeable", "deriving", "data"),
375-
scalacOptions = Nil // disable -Ysafe-init, due to -Xfatal-warnings
375+
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init"), // due to -Xfatal-warnings
376376
)
377377

378378
lazy val xmlInterpolator = SbtCommunityProject(
@@ -682,7 +682,7 @@ object projects:
682682
sbtTestCommand = "runCommunityBuild",
683683
sbtPublishCommand = "publishLocal",
684684
dependencies = List(scalatest),
685-
scalacOptions = List("-language:implicitConversions"), // disabled -Ysafe-init, due to bug in macro
685+
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Xcheck-macros") :+ "-language:implicitConversions", // disabled -Xcheck-macros, due to bug in macro
686686
)
687687

688688
lazy val onnxScala = SbtCommunityProject(

compiler/src/dotty/tools/backend/jvm/CodeGen.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import scala.tools.asm
3030
import scala.tools.asm.tree._
3131
import tpd._
3232
import dotty.tools.io.AbstractFile
33+
import dotty.tools.dotc.util
3334
import dotty.tools.dotc.util.NoSourcePosition
3435

3536

@@ -106,7 +107,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
106107
}
107108

108109
// Creates a callback that will be evaluated in PostProcessor after creating a file
109-
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: interfaces.SourceFile): AbstractFile => Unit = clsFile => {
110+
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = clsFile => {
110111
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
111112
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
112113
}
@@ -115,12 +116,9 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
115116
if (ctx.compilerCallback != null)
116117
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)
117118

118-
if (ctx.sbtCallback != null) {
119-
val jSourceFile = sourceFile.jfile.orElse(null)
120-
val cb = ctx.sbtCallback
121-
if (isLocal) cb.generatedLocalClass(jSourceFile, clsFile.file)
122-
else cb.generatedNonLocalClass(jSourceFile, clsFile.file, className, fullClassName)
123-
}
119+
ctx.withIncCallback: cb =>
120+
if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath)
121+
else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName)
124122
}
125123

126124
/** Convert a `dotty.tools.io.AbstractFile` into a

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

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags
77
import Symbols._, StdNames._, Trees._, ContextOps._
88
import Decorators._, transform.SymUtils._
99
import Annotations.Annotation
10-
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName, WildcardParamName}
10+
import NameKinds.{UniqueName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName}
1111
import typer.{Namer, Checking}
1212
import util.{Property, SourceFile, SourcePosition, Chars}
1313
import config.Feature.{sourceVersion, migrateTo3, enabled}
@@ -203,10 +203,14 @@ object desugar {
203203
else vdef1
204204
end valDef
205205

206-
def makeImplicitParameters(tpts: List[Tree], implicitFlag: FlagSet, forPrimaryConstructor: Boolean = false)(using Context): List[ValDef] =
207-
for (tpt <- tpts) yield {
206+
def makeImplicitParameters(
207+
tpts: List[Tree], implicitFlag: FlagSet,
208+
mkParamName: () => TermName,
209+
forPrimaryConstructor: Boolean = false
210+
)(using Context): List[ValDef] =
211+
for (tpt, i) <- tpts.zipWithIndex yield {
208212
val paramFlags: FlagSet = if (forPrimaryConstructor) LocalParamAccessor else Param
209-
val epname = EvidenceParamName.fresh()
213+
val epname = mkParamName()
210214
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | implicitFlag)
211215
}
212216

@@ -240,17 +244,27 @@ object desugar {
240244
val DefDef(_, paramss, tpt, rhs) = meth
241245
val evidenceParamBuf = ListBuffer[ValDef]()
242246

247+
var seenContextBounds: Int = 0
243248
def desugarContextBounds(rhs: Tree): Tree = rhs match
244249
case ContextBounds(tbounds, cxbounds) =>
245250
val iflag = if sourceVersion.isAtLeast(`future`) then Given else Implicit
246251
evidenceParamBuf ++= makeImplicitParameters(
247-
cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor)
252+
cxbounds, iflag,
253+
// Just like with `makeSyntheticParameter` on nameless parameters of
254+
// using clauses, we only need names that are unique among the
255+
// parameters of the method since shadowing does not affect
256+
// implicit resolution in Scala 3.
257+
mkParamName = () =>
258+
val index = seenContextBounds + 1 // Start at 1 like FreshNameCreator.
259+
val ret = ContextBoundParamName(EmptyTermName, index)
260+
seenContextBounds += 1
261+
ret,
262+
forPrimaryConstructor = isPrimaryConstructor)
248263
tbounds
249264
case LambdaTypeTree(tparams, body) =>
250265
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
251266
case _ =>
252267
rhs
253-
254268
val paramssNoContextBounds =
255269
mapParamss(paramss) {
256270
tparam => cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
@@ -381,11 +395,14 @@ object desugar {
381395
tree match
382396
case untpd.Block(stats, expr) =>
383397
val (untpdTypeVariables, otherStats) = stats.span {
384-
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
398+
case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern
385399
case _ => false
386400
}
401+
val untpdCaseTypeVariables = untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]].map {
402+
tdef => tdef.withMods(tdef.mods | Case)
403+
}
387404
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
388-
(untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]], pattern)
405+
(untpdCaseTypeVariables, pattern)
389406
case _ =>
390407
(Nil, tree)
391408

@@ -406,11 +423,11 @@ object desugar {
406423
meth.paramss :+ evidenceParams
407424
cpy.DefDef(meth)(paramss = paramss1)
408425

409-
/** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
426+
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
410427
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
411428
meth.paramss.reverse match {
412429
case ValDefs(vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) =>
413-
vparams.takeWhile(_.name.is(EvidenceParamName))
430+
vparams.takeWhile(_.name.is(ContextBoundParamName))
414431
case _ =>
415432
Nil
416433
}
@@ -1014,7 +1031,7 @@ object desugar {
10141031
* if the type has a pattern variable name
10151032
*/
10161033
def quotedPatternTypeDef(tree: TypeDef)(using Context): TypeDef = {
1017-
assert(ctx.mode.is(Mode.QuotedPattern))
1034+
assert(ctx.mode.isQuotedPattern)
10181035
if tree.name.isVarPattern && !tree.isBackquoted then
10191036
val patternTypeAnnot = New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)
10201037
val mods = tree.mods.withAddedAnnotation(patternTypeAnnot)
@@ -1083,19 +1100,21 @@ object desugar {
10831100
*/
10841101
def makePolyFunctionType(tree: PolyFunction)(using Context): RefinedTypeTree =
10851102
val PolyFunction(tparams: List[untpd.TypeDef] @unchecked, fun @ untpd.Function(vparamTypes, res)) = tree: @unchecked
1086-
val funFlags = fun match
1103+
val paramFlags = fun match
10871104
case fun: FunctionWithMods =>
1088-
fun.mods.flags
1089-
case _ => EmptyFlags
1105+
// TODO: make use of this in the desugaring when pureFuns is enabled.
1106+
// val isImpure = funFlags.is(Impure)
10901107

1091-
// TODO: make use of this in the desugaring when pureFuns is enabled.
1092-
// val isImpure = funFlags.is(Impure)
1108+
// Function flags to be propagated to each parameter in the desugared method type.
1109+
val givenFlag = fun.mods.flags.toTermFlags & Given
1110+
fun.erasedParams.map(isErased => if isErased then givenFlag | Erased else givenFlag)
1111+
case _ =>
1112+
vparamTypes.map(_ => EmptyFlags)
10931113

1094-
// Function flags to be propagated to each parameter in the desugared method type.
1095-
val paramFlags = funFlags.toTermFlags & Given
1096-
val vparams = vparamTypes.zipWithIndex.map:
1097-
case (p: ValDef, _) => p.withAddedFlags(paramFlags)
1098-
case (p, n) => makeSyntheticParameter(n + 1, p).withAddedFlags(paramFlags)
1114+
val vparams = vparamTypes.lazyZip(paramFlags).zipWithIndex.map {
1115+
case ((p: ValDef, paramFlags), n) => p.withAddedFlags(paramFlags)
1116+
case ((p, paramFlags), n) => makeSyntheticParameter(n + 1, p).withAddedFlags(paramFlags)
1117+
}.toList
10991118

11001119
RefinedTypeTree(ref(defn.PolyFunctionType), List(
11011120
DefDef(nme.apply, tparams :: vparams :: Nil, res, EmptyTree).withFlags(Synthetic)
@@ -1363,7 +1382,7 @@ object desugar {
13631382
case tree: ValDef => valDef(tree)
13641383
case tree: TypeDef =>
13651384
if (tree.isClassDef) classDef(tree)
1366-
else if (ctx.mode.is(Mode.QuotedPattern)) quotedPatternTypeDef(tree)
1385+
else if (ctx.mode.isQuotedPattern) quotedPatternTypeDef(tree)
13671386
else tree
13681387
case tree: DefDef =>
13691388
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
@@ -1585,7 +1604,7 @@ object desugar {
15851604

15861605
def makeContextualFunction(formals: List[Tree], body: Tree, erasedParams: List[Boolean])(using Context): Function = {
15871606
val mods = Given
1588-
val params = makeImplicitParameters(formals, mods)
1607+
val params = makeImplicitParameters(formals, mods, mkParamName = () => ContextFunctionParamName.fresh())
15891608
FunctionWithMods(params, body, Modifiers(mods), erasedParams)
15901609
}
15911610

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ import config.Feature
1515
private val Captures: Key[CaptureSet] = Key()
1616
private val BoxedType: Key[BoxedTypeCache] = Key()
1717

18+
/** Switch whether unpickled function types and byname types should be mapped to
19+
* impure types. With the new gradual typing using Fluid capture sets, this should
20+
* be no longer needed. Also, it has bad interactions with pickling tests.
21+
*/
22+
private val adaptUnpickledFunctionTypes = false
23+
1824
/** The arguments of a @retains or @retainsByName annotation */
1925
private[cc] def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
2026
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
@@ -49,7 +55,7 @@ extension (tree: Tree)
4955
* a by name parameter type, turning the latter into an impure by name parameter type.
5056
*/
5157
def adaptByNameArgUnderPureFuns(using Context): Tree =
52-
if Feature.pureFunsEnabledSomewhere then
58+
if adaptUnpickledFunctionTypes && Feature.pureFunsEnabledSomewhere then
5359
val rbn = defn.RetainsByNameAnnot
5460
Annotated(tree,
5561
New(rbn.typeRef).select(rbn.primaryConstructor).appliedTo(
@@ -145,7 +151,7 @@ extension (tp: Type)
145151
*/
146152
def adaptFunctionTypeUnderPureFuns(using Context): Type = tp match
147153
case AppliedType(fn, args)
148-
if Feature.pureFunsEnabledSomewhere && defn.isFunctionClass(fn.typeSymbol) =>
154+
if adaptUnpickledFunctionTypes && Feature.pureFunsEnabledSomewhere && defn.isFunctionClass(fn.typeSymbol) =>
149155
val fname = fn.typeSymbol.name
150156
defn.FunctionType(
151157
fname.functionArity,

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,20 @@ object CaptureSet:
356356
override def toString = elems.toString
357357
end Const
358358

359+
/** A special capture set that gets added to the types of symbols that were not
360+
* themselves capture checked, in order to admit arbitrary corresponding capture
361+
* sets in subcapturing comparisons. Similar to platform types for explicit
362+
* nulls, this provides more lenient checking against compilation units that
363+
* were not yet compiled with capture checking on.
364+
*/
365+
object Fluid extends Const(emptySet):
366+
override def isAlwaysEmpty = false
367+
override def addNewElems(elems: Refs, origin: CaptureSet)(using Context, VarState) = CompareResult.OK
368+
override def accountsFor(x: CaptureRef)(using Context): Boolean = true
369+
override def mightAccountFor(x: CaptureRef)(using Context): Boolean = true
370+
override def toString = "<fluid>"
371+
end Fluid
372+
359373
/** The subclass of captureset variables with given initial elements */
360374
class Var(initialElems: Refs = emptySet) extends CaptureSet:
361375

@@ -863,7 +877,7 @@ object CaptureSet:
863877
case CapturingType(parent, refs) =>
864878
recur(parent) ++ refs
865879
case tpd @ RefinedType(parent, _, rinfo: MethodType)
866-
if followResult && defn.isFunctionType(tpd) =>
880+
if followResult && defn.isFunctionNType(tpd) =>
867881
ofType(parent, followResult = false) // pick up capture set from parent type
868882
++ (recur(rinfo.resType) // add capture set of result
869883
-- CaptureSet(rinfo.paramRefs.filter(_.isTracked)*)) // but disregard bound parameters

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

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,14 +290,46 @@ class CheckCaptures extends Recheck, SymTransformer:
290290
def includeCallCaptures(sym: Symbol, pos: SrcPos)(using Context): Unit =
291291
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
292292

293+
private def handleBackwardsCompat(tp: Type, sym: Symbol, initialVariance: Int = 1)(using Context): Type =
294+
val fluidify = new TypeMap with IdempotentCaptRefMap:
295+
variance = initialVariance
296+
def apply(t: Type): Type = t match
297+
case t: MethodType =>
298+
mapOver(t)
299+
case t: TypeLambda =>
300+
t.derivedLambdaType(resType = this(t.resType))
301+
case CapturingType(_, _) =>
302+
t
303+
case _ =>
304+
val t1 = t match
305+
case t @ RefinedType(parent, rname, rinfo: MethodType) if defn.isFunctionType(t) =>
306+
t.derivedRefinedType(parent, rname, this(rinfo))
307+
case _ =>
308+
mapOver(t)
309+
if variance > 0 then t1
310+
else Setup.decorate(t1, Function.const(CaptureSet.Fluid))
311+
312+
def isPreCC(sym: Symbol): Boolean =
313+
sym.isTerm && sym.maybeOwner.isClass
314+
&& !sym.owner.is(CaptureChecked)
315+
&& !defn.isFunctionSymbol(sym.owner)
316+
317+
if isPreCC(sym) then
318+
val tpw = tp.widen
319+
val fluidTp = fluidify(tpw)
320+
if fluidTp eq tpw then tp
321+
else fluidTp.showing(i"fluid for ${sym.showLocated}, ${sym.is(JavaDefined)}: $tp --> $result", capt)
322+
else tp
323+
end handleBackwardsCompat
324+
293325
override def recheckIdent(tree: Ident)(using Context): Type =
294326
if tree.symbol.is(Method) then
295327
if tree.symbol.info.isParameterless then
296328
// there won't be an apply; need to include call captures now
297329
includeCallCaptures(tree.symbol, tree.srcPos)
298330
else
299331
markFree(tree.symbol, tree.srcPos)
300-
super.recheckIdent(tree)
332+
handleBackwardsCompat(super.recheckIdent(tree), tree.symbol)
301333

302334
/** A specialized implementation of the selection rule.
303335
*
@@ -327,7 +359,7 @@ class CheckCaptures extends Recheck, SymTransformer:
327359
val selType = recheckSelection(tree, qualType, name, disambiguate)
328360
val selCs = selType.widen.captureSet
329361
if selCs.isAlwaysEmpty || selType.widen.isBoxedCapturing || qualType.isBoxedCapturing then
330-
selType
362+
handleBackwardsCompat(selType, tree.symbol)
331363
else
332364
val qualCs = qualType.captureSet
333365
capt.println(i"pick one of $qualType, ${selType.widen}, $qualCs, $selCs in $tree")
@@ -362,7 +394,16 @@ class CheckCaptures extends Recheck, SymTransformer:
362394
val argType0 = f(recheckStart(arg, pt))
363395
val argType = super.recheckFinish(argType0, arg, pt)
364396
super.recheckFinish(argType, tree, pt)
365-
if meth == defn.Caps_unsafeBox then
397+
398+
if meth == defn.Caps_unsafeAssumePure then
399+
val arg :: Nil = tree.args: @unchecked
400+
val argType0 = recheck(arg, pt.capturing(CaptureSet.universal))
401+
val argType =
402+
if argType0.captureSet.isAlwaysEmpty then argType0
403+
else argType0.widen.stripCapturing
404+
capt.println(i"rechecking $arg with ${pt.capturing(CaptureSet.universal)}: $argType")
405+
super.recheckFinish(argType, tree, pt)
406+
else if meth == defn.Caps_unsafeBox then
366407
mapArgUsing(_.forceBoxStatus(true))
367408
else if meth == defn.Caps_unsafeUnbox then
368409
mapArgUsing(_.forceBoxStatus(false))
@@ -662,8 +703,10 @@ class CheckCaptures extends Recheck, SymTransformer:
662703
/** Turn `expected` into a dependent function when `actual` is dependent. */
663704
private def alignDependentFunction(expected: Type, actual: Type)(using Context): Type =
664705
def recur(expected: Type): Type = expected.dealias match
665-
case expected @ CapturingType(eparent, refs) =>
666-
CapturingType(recur(eparent), refs, boxed = expected.isBoxed)
706+
case expected0 @ CapturingType(eparent, refs) =>
707+
val eparent1 = recur(eparent)
708+
if eparent1 eq eparent then expected
709+
else CapturingType(eparent1, refs, boxed = expected0.isBoxed)
667710
case expected @ defn.FunctionOf(args, resultType, isContextual)
668711
if defn.isNonRefinedFunction(expected) && defn.isFunctionNType(actual) && !defn.isNonRefinedFunction(actual) =>
669712
val expected1 = toDepFun(args, resultType, isContextual)
@@ -883,7 +926,7 @@ class CheckCaptures extends Recheck, SymTransformer:
883926
* But maybe we can then elide the check during the RefChecks phase under captureChecking?
884927
*/
885928
def checkOverrides = new TreeTraverser:
886-
class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, srcPos: SrcPos)(using Context) extends OverridingPairsChecker(clazz, self) {
929+
class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, srcPos: SrcPos)(using Context) extends OverridingPairsChecker(clazz, self):
887930
/** Check subtype with box adaptation.
888931
* This function is passed to RefChecks to check the compatibility of overriding pairs.
889932
* @param sym symbol of the field definition that is being checked
@@ -905,7 +948,11 @@ class CheckCaptures extends Recheck, SymTransformer:
905948
case _ => adapted
906949
finally curEnv = saved
907950
actual1 frozen_<:< expected1
908-
}
951+
952+
override def adjustInfo(tp: Type, member: Symbol)(using Context): Type =
953+
handleBackwardsCompat(tp, member, initialVariance = 0)
954+
//.showing(i"adjust $other: $tp --> $result")
955+
end OverridingPairsCheckerCC
909956

910957
def traverse(t: Tree)(using Context) =
911958
t match

0 commit comments

Comments
 (0)