Skip to content

Commit ab52310

Browse files
committed
Merge branch 'main' into new-lazy-vals
2 parents ce0569f + 7919cb3 commit ab52310

File tree

129 files changed

+1628
-742
lines changed

Some content is hidden

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

129 files changed

+1628
-742
lines changed
Submodule stdLib213 updated 613 files

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3144,7 +3144,23 @@ class JSCodeGen()(using genCtx: Context) {
31443144
val tpe = atPhase(elimErasedValueTypePhase) {
31453145
sym.info.finalResultType
31463146
}
3147-
unbox(boxedResult, tpe)
3147+
if (tpe.isRef(defn.BoxedUnitClass) && sym.isGetter) {
3148+
/* Work around to reclaim Scala 2 erasure behavior, assumed by the test
3149+
* NonNativeJSTypeTest.defaultValuesForFields.
3150+
* Scala 2 erases getters of `Unit`-typed fields as returning `Unit`
3151+
* (not `BoxedUnit`). Therefore, when called in expression position,
3152+
* the call site introduces an explicit `BoxedUnit.UNIT`. Even if the
3153+
* field has not been initialized at all (with `= _`), this results in
3154+
* an actual `()` value.
3155+
* In Scala 3, the same pattern returns `null`, as a `BoxedUnit`, so we
3156+
* introduce here an explicit `()` value.
3157+
* TODO We should remove this branch if the upstream test is updated
3158+
* not to assume such a strict interpretation of erasure.
3159+
*/
3160+
js.Block(boxedResult, js.Undefined())
3161+
} else {
3162+
unbox(boxedResult, tpe)
3163+
}
31483164
}
31493165
}
31503166

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ extension (tp: Type)
121121
case _ =>
122122
tp
123123

124+
def isCapturingType(using Context): Boolean =
125+
tp match
126+
case CapturingType(_, _) => true
127+
case _ => false
128+
124129
extension (sym: Symbol)
125130

126131
/** Does this symbol allow results carrying the universal capability?

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

Lines changed: 147 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,20 @@ object CheckCaptures:
4242
end Pre
4343

4444
/** A class describing environments.
45-
* @param owner the current owner
46-
* @param captured the caputure set containing all references to tracked free variables outside of boxes
47-
* @param isBoxed true if the environment is inside a box (in which case references are not counted)
48-
* @param outer0 the next enclosing environment
45+
* @param owner the current owner
46+
* @param nestedInOwner true if the environment is a temporary one nested in the owner's environment,
47+
* and does not have a different actual owner symbol (this happens when doing box adaptation).
48+
* @param captured the caputure set containing all references to tracked free variables outside of boxes
49+
* @param isBoxed true if the environment is inside a box (in which case references are not counted)
50+
* @param outer0 the next enclosing environment
4951
*/
50-
case class Env(owner: Symbol, captured: CaptureSet, isBoxed: Boolean, outer0: Env | Null):
52+
case class Env(
53+
owner: Symbol,
54+
nestedInOwner: Boolean,
55+
captured: CaptureSet,
56+
isBoxed: Boolean,
57+
outer0: Env | Null
58+
):
5159
def outer = outer0.nn
5260

5361
def isOutermost = outer0 == null
@@ -204,7 +212,7 @@ class CheckCaptures extends Recheck, SymTransformer:
204212
report.error(i"$header included in allowed capture set ${res.blocking}", pos)
205213

206214
/** The current environment */
207-
private var curEnv: Env = Env(NoSymbol, CaptureSet.empty, isBoxed = false, null)
215+
private var curEnv: Env = Env(NoSymbol, nestedInOwner = false, CaptureSet.empty, isBoxed = false, null)
208216

209217
private val myCapturedVars: util.EqHashMap[Symbol, CaptureSet] = EqHashMap()
210218

@@ -249,8 +257,12 @@ class CheckCaptures extends Recheck, SymTransformer:
249257
if !cs.isAlwaysEmpty then
250258
forallOuterEnvsUpTo(ctx.owner.topLevelClass) { env =>
251259
val included = cs.filter {
252-
case ref: TermRef => env.owner.isProperlyContainedIn(ref.symbol.owner)
253-
case ref: ThisType => env.owner.isProperlyContainedIn(ref.cls)
260+
case ref: TermRef =>
261+
(env.nestedInOwner || env.owner != ref.symbol.owner)
262+
&& env.owner.isContainedIn(ref.symbol.owner)
263+
case ref: ThisType =>
264+
(env.nestedInOwner || env.owner != ref.cls)
265+
&& env.owner.isContainedIn(ref.cls)
254266
case _ => false
255267
}
256268
capt.println(i"Include call capture $included in ${env.owner}")
@@ -439,7 +451,7 @@ class CheckCaptures extends Recheck, SymTransformer:
439451
if !Synthetics.isExcluded(sym) then
440452
val saved = curEnv
441453
val localSet = capturedVars(sym)
442-
if !localSet.isAlwaysEmpty then curEnv = Env(sym, localSet, isBoxed = false, curEnv)
454+
if !localSet.isAlwaysEmpty then curEnv = Env(sym, nestedInOwner = false, localSet, isBoxed = false, curEnv)
443455
try super.recheckDefDef(tree, sym)
444456
finally
445457
interpolateVarsIn(tree.tpt)
@@ -455,7 +467,7 @@ class CheckCaptures extends Recheck, SymTransformer:
455467
val localSet = capturedVars(cls)
456468
for parent <- impl.parents do // (1)
457469
checkSubset(capturedVars(parent.tpe.classSymbol), localSet, parent.srcPos)
458-
if !localSet.isAlwaysEmpty then curEnv = Env(cls, localSet, isBoxed = false, curEnv)
470+
if !localSet.isAlwaysEmpty then curEnv = Env(cls, nestedInOwner = false, localSet, isBoxed = false, curEnv)
459471
try
460472
val thisSet = cls.classInfo.selfType.captureSet.withDescription(i"of the self type of $cls")
461473
checkSubset(localSet, thisSet, tree.srcPos) // (2)
@@ -495,14 +507,20 @@ class CheckCaptures extends Recheck, SymTransformer:
495507
recheckFinish(result, arg, pt)
496508
*/
497509

498-
/** If expected type `pt` is boxed, don't propagate free variables.
510+
/** If expected type `pt` is boxed and the tree is a function or a reference,
511+
* don't propagate free variables.
499512
* Otherwise, if the result type is boxed, simulate an unboxing by
500513
* adding all references in the boxed capture set to the current environment.
501514
*/
502515
override def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
503516
if tree.isTerm && pt.isBoxedCapturing then
504517
val saved = curEnv
505-
curEnv = Env(curEnv.owner, CaptureSet.Var(), isBoxed = true, curEnv)
518+
519+
tree match
520+
case _: RefTree | closureDef(_) =>
521+
curEnv = Env(curEnv.owner, nestedInOwner = false, CaptureSet.Var(), isBoxed = true, curEnv)
522+
case _ =>
523+
506524
try super.recheck(tree, pt)
507525
finally curEnv = saved
508526
else
@@ -593,25 +611,121 @@ class CheckCaptures extends Recheck, SymTransformer:
593611

594612
/** Adapt function type `actual`, which is `aargs -> ares` (possibly with dependencies)
595613
* to `expected` type.
614+
* It returns the adapted type along with the additionally captured variable
615+
* during adaptation.
596616
* @param reconstruct how to rebuild the adapted function type
597617
*/
598618
def adaptFun(actual: Type, aargs: List[Type], ares: Type, expected: Type,
599-
covariant: Boolean,
600-
reconstruct: (List[Type], Type) => Type): Type =
601-
val (eargs, eres) = expected.dealias match
602-
case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres)
603-
case _ => (aargs.map(_ => WildcardType), WildcardType)
604-
val aargs1 = aargs.zipWithConserve(eargs)(adapt(_, _, !covariant))
605-
val ares1 = adapt(ares, eres, covariant)
606-
if (ares1 eq ares) && (aargs1 eq aargs) then actual
607-
else reconstruct(aargs1, ares1)
608-
609-
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = actual.dealias match
610-
case actual @ CapturingType(parent, refs) =>
611-
val parent1 = adapt(parent, expected, covariant)
612-
if actual.isBoxed != expected.isBoxedCapturing then
619+
covariant: Boolean, boxed: Boolean,
620+
reconstruct: (List[Type], Type) => Type): (Type, CaptureSet) =
621+
val saved = curEnv
622+
curEnv = Env(curEnv.owner, nestedInOwner = true, CaptureSet.Var(), isBoxed = false, if boxed then null else curEnv)
623+
624+
try
625+
val (eargs, eres) = expected.dealias.stripCapturing match
626+
case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres)
627+
case expected: MethodType => (expected.paramInfos, expected.resType)
628+
case expected @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(expected) => (rinfo.paramInfos, rinfo.resType)
629+
case _ => (aargs.map(_ => WildcardType), WildcardType)
630+
val aargs1 = aargs.zipWithConserve(eargs) { (aarg, earg) => adapt(aarg, earg, !covariant) }
631+
val ares1 = adapt(ares, eres, covariant)
632+
633+
val resTp =
634+
if (ares1 eq ares) && (aargs1 eq aargs) then actual
635+
else reconstruct(aargs1, ares1)
636+
637+
(resTp, curEnv.captured)
638+
finally
639+
curEnv = saved
640+
641+
/** Adapt type function type `actual` to the expected type.
642+
* @see [[adaptFun]]
643+
*/
644+
def adaptTypeFun(
645+
actual: Type, ares: Type, expected: Type,
646+
covariant: Boolean, boxed: Boolean,
647+
reconstruct: Type => Type): (Type, CaptureSet) =
648+
val saved = curEnv
649+
curEnv = Env(curEnv.owner, nestedInOwner = true, CaptureSet.Var(), isBoxed = false, if boxed then null else curEnv)
650+
651+
try
652+
val eres = expected.dealias.stripCapturing match
653+
case RefinedType(_, _, rinfo: PolyType) => rinfo.resType
654+
case expected: PolyType => expected.resType
655+
case _ => WildcardType
656+
657+
val ares1 = adapt(ares, eres, covariant)
658+
659+
val resTp =
660+
if ares1 eq ares then actual
661+
else reconstruct(ares1)
662+
663+
(resTp, curEnv.captured)
664+
finally
665+
curEnv = saved
666+
end adaptTypeFun
667+
668+
def adaptInfo(actual: Type, expected: Type, covariant: Boolean): String =
669+
val arrow = if covariant then "~~>" else "<~~"
670+
i"adapting $actual $arrow $expected"
671+
672+
/** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed),
673+
* where `tp0` is not a capturing type.
674+
*
675+
* If `tp` is a nested capturing type, the return tuple always represents
676+
* the innermost capturing type. The outer capture annotations can be
677+
* reconstructed with the returned function.
678+
*/
679+
def destructCapturingType(tp: Type, reconstruct: Type => Type = x => x): ((Type, CaptureSet, Boolean), Type => Type) =
680+
tp.dealias match
681+
case tp @ CapturingType(parent, cs) =>
682+
if parent.dealias.isCapturingType then
683+
destructCapturingType(parent, res => reconstruct(tp.derivedCapturingType(res, cs)))
684+
else
685+
((parent, cs, tp.isBoxed), reconstruct)
686+
case actual =>
687+
((actual, CaptureSet(), false), reconstruct)
688+
689+
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) {
690+
if expected.isInstanceOf[WildcardType] then actual
691+
else
692+
val ((parent, cs, actualIsBoxed), recon) = destructCapturingType(actual)
693+
694+
val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing
695+
val insertBox = needsAdaptation && covariant != actualIsBoxed
696+
697+
val (parent1, cs1) = parent match {
698+
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
699+
val (parent1, leaked) = adaptFun(parent, args.init, args.last, expected, covariant, insertBox,
700+
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
701+
(parent1, leaked ++ cs)
702+
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
703+
// TODO Find a way to combine handling of generic and dependent function types (here and elsewhere)
704+
val (parent1, leaked) = adaptFun(parent, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox,
705+
(aargs1, ares1) =>
706+
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
707+
.toFunctionType(isJava = false, alwaysDependent = true))
708+
(parent1, leaked ++ cs)
709+
case actual: MethodType =>
710+
val (parent1, leaked) = adaptFun(parent, actual.paramInfos, actual.resType, expected, covariant, insertBox,
711+
(aargs1, ares1) =>
712+
actual.derivedLambdaType(paramInfos = aargs1, resType = ares1))
713+
(parent1, leaked ++ cs)
714+
case actual @ RefinedType(p, nme, rinfo: PolyType) if defn.isFunctionOrPolyType(actual) =>
715+
val (parent1, leaked) = adaptTypeFun(parent, rinfo.resType, expected, covariant, insertBox,
716+
ares1 =>
717+
val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1)
718+
val actual1 = actual.derivedRefinedType(p, nme, rinfo1)
719+
actual1
720+
)
721+
(parent1, leaked ++ cs)
722+
case _ =>
723+
(parent, cs)
724+
}
725+
726+
if needsAdaptation then
613727
val criticalSet = // the set which is not allowed to have `*`
614-
if covariant then refs // can't box with `*`
728+
if covariant then cs1 // can't box with `*`
615729
else expected.captureSet // can't unbox with `*`
616730
if criticalSet.isUniversal then
617731
// We can't box/unbox the universal capability. Leave `actual` as it is
@@ -627,20 +741,13 @@ class CheckCaptures extends Recheck, SymTransformer:
627741
|since one of their capture sets contains the root capability `*`""",
628742
pos)
629743
}
630-
if covariant == actual.isBoxed then markFree(refs, pos)
631-
CapturingType(parent1, refs, boxed = !actual.isBoxed)
744+
if !insertBox then // unboxing
745+
markFree(criticalSet, pos)
746+
recon(CapturingType(parent1, cs1, !actualIsBoxed))
632747
else
633-
actual.derivedCapturingType(parent1, refs)
634-
case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) =>
635-
adaptFun(actual, args.init, args.last, expected, covariant,
636-
(aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1))
637-
case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) =>
638-
// TODO Find a way to combine handling of generic and dependent function types (here and elsewhere)
639-
adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant,
640-
(aargs1, ares1) =>
641-
rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1)
642-
.toFunctionType(isJava = false, alwaysDependent = true))
643-
case _ => actual
748+
recon(CapturingType(parent1, cs1, actualIsBoxed))
749+
}
750+
644751

645752
var actualw = actual.widenDealias
646753
actual match

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ private sealed trait VerboseSettings:
150150
val VprofileSortedBy = ChoiceSetting("-Vprofile-sorted-by", "key", "Show metrics about sources and internal representations sorted by given column name", List("name", "path", "lines", "tokens", "tasty", "complexity"), "")
151151
val VprofileDetails = IntSetting("-Vprofile-details", "Show metrics about sources and internal representations of the most complex methods", 0)
152152
val VreplMaxPrintElements: Setting[Int] = IntSetting("-Vrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000)
153+
val VreplMaxPrintCharacters: Setting[Int] = IntSetting("-Vrepl-max-print-characters", "Number of characters to be printed before output is truncated.", 50000)
153154

154155
/** -W "Warnings" settings
155156
*/

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import scala.util.control.NonFatal
99
import Contexts._, Names._, Phases._, Symbols._
1010
import printing.{ Printer, Showable }, printing.Formatting._, printing.Texts._
1111
import transform.MegaPhase
12+
import reporting.{Message, NoExplanation}
1213

1314
/** This object provides useful implicit decorators for types defined elsewhere */
1415
object Decorators {
@@ -57,6 +58,9 @@ object Decorators {
5758
padding + s.replace("\n", "\n" + padding)
5859
end extension
5960

61+
extension (str: => String)
62+
def toMessage: Message = reporting.NoExplanation(str)
63+
6064
/** Implements a findSymbol method on iterators of Symbols that
6165
* works like find but avoids Option, replacing None with NoSymbol.
6266
*/

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ class Definitions {
997997
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
998998
@tu lazy val InvariantBetweenAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InvariantBetween")
999999
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
1000+
@tu lazy val MappedAlternativeAnnot: ClassSymbol = requiredClass("scala.annotation.internal.MappedAlternative")
10001001
@tu lazy val MigrationAnnot: ClassSymbol = requiredClass("scala.annotation.migration")
10011002
@tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn")
10021003
@tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait")

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ object Flags {
571571
val GivenOrImplicit: FlagSet = Given | Implicit
572572
val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags
573573
val GivenMethod: FlagSet = Given | Method
574+
val LazyGiven: FlagSet = Given | Lazy
574575
val InlineOrProxy: FlagSet = Inline | InlineProxy // An inline method or inline argument proxy */
575576
val InlineMethod: FlagSet = Inline | Method
576577
val InlineParam: FlagSet = Inline | Param

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ class TypeApplications(val self: Type) extends AnyVal {
204204
}
205205
}
206206

207+
/** Substitute in `self` the type parameters of `tycon` by some other types. */
208+
final def substTypeParams(tycon: Type, to: List[Type])(using Context): Type =
209+
(tycon.typeParams: @unchecked) match
210+
case LambdaParam(lam, _) :: _ => self.substParams(lam, to)
211+
case params: List[Symbol @unchecked] => self.subst(params, to)
212+
207213
/** If `self` is a higher-kinded type, its type parameters, otherwise Nil */
208214
final def hkTypeParams(using Context): List[TypeParamInfo] =
209215
if (isLambdaSub) typeParams else Nil

0 commit comments

Comments
 (0)