Skip to content

Commit 527c8de

Browse files
authored
Merge branch 'main' into drop-deps-cb
2 parents 68e78a7 + a120c7c commit 527c8de

Some content is hidden

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

42 files changed

+679
-109
lines changed

.github/workflows/stdlib.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,6 @@ jobs:
399399
test-scala3-compiler-nonbootstrapped:
400400
runs-on: ubuntu-latest
401401
needs: [scala3-compiler-nonbootstrapped, tasty-core-nonbootstrapped, scala-library-nonbootstrapped]
402-
## The reference compiler generates wrong code for the non-bootstrapped stdlib, hence we cannot run tests on it at the moment
403-
if: false
404402
steps:
405403
- name: Git Checkout
406404
uses: actions/checkout@v5
@@ -413,7 +411,7 @@ jobs:
413411
cache: 'sbt'
414412
- uses: sbt/setup-sbt@v1
415413
- name: Test `scala3-compiler-nonbootstrapped`
416-
run: ./project/scripts/sbt scala3-compiler-nonbootstrapped-new/test
414+
run: ./project/scripts/sbt scala3-compiler-nonbootstrapped/test
417415

418416
test-scala3-compiler-bootstrapped:
419417
runs-on: ubuntu-latest

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,15 @@ object CaptureSet:
655655
inline val debugVars = false
656656
inline val debugTarget = 1745
657657

658-
/** The subclass of captureset variables with given initial elements */
659-
class Var(initialOwner: Symbol = NoSymbol, initialElems: Refs = emptyRefs, underBox: Boolean = false)(using /*@constructorOnly*/ ictx: Context) extends CaptureSet:
658+
/** The subclass of captureset variables with given initial elements
659+
* @param initialOwner the initial owner. This is the real owner, except that
660+
* it can be change in HiddenSets. Used for level checking
661+
* if different from NoSymbol.
662+
* @param initialElems the initial elements
663+
* @param nestedOK relevant only if owner != NoSymbol. If true the set accepts
664+
* elements that are directly owned by owner.
665+
*/
666+
class Var(initialOwner: Symbol = NoSymbol, initialElems: Refs = emptyRefs, nestedOK: Boolean = true)(using /*@constructorOnly*/ ictx: Context) extends CaptureSet:
660667

661668
override def owner = initialOwner
662669

@@ -682,7 +689,7 @@ object CaptureSet:
682689
protected var myElems: Refs = initialElems
683690

684691
if debugVars && id == debugTarget then
685-
println(i"###INIT ELEMS of $id to $initialElems")
692+
println(i"###INIT ELEMS of $id of class $getClass in $initialOwner, $nestedOK to $initialElems")
686693
assert(false)
687694

688695
def elems: Refs = myElems
@@ -828,15 +835,16 @@ object CaptureSet:
828835

829836
def levelOK(elem: Capability)(using Context): Boolean = elem match
830837
case elem @ ResultCap(binder) =>
831-
rootLimit == null && (this.isInstanceOf[BiMapped] || isPartOf(binder.resType))
838+
rootLimit == null && isPartOf(binder.resType)
832839
case GlobalCap =>
833840
rootLimit == null
834841
case elem: ParamRef =>
835-
this.isInstanceOf[BiMapped] || isPartOf(elem.binder.resType)
842+
isPartOf(elem.binder.resType)
836843
case _ =>
837844
if owner.exists then
838845
val elemVis = elem.visibility
839846
!elemVis.isProperlyContainedIn(owner)
847+
|| nestedOK && elemVis.owner == owner
840848
else true
841849

842850
def addDependent(cs: CaptureSet)(using Context, VarState): Boolean =
@@ -950,6 +958,9 @@ object CaptureSet:
950958
abstract class DerivedVar(owner: Symbol, initialElems: Refs)(using @constructorOnly ctx: Context)
951959
extends Var(owner, initialElems):
952960

961+
override def levelOK(elem: Capability)(using Context): Boolean =
962+
true
963+
953964
// For debugging: A trace where a set was created. Note that logically it would make more
954965
// sense to place this variable in Mapped, but that runs afoul of the initialization checker.
955966
// val stack = if debugSets && this.isInstanceOf[Mapped] then (new Throwable).getStackTrace().take(20) else null
@@ -995,6 +1006,10 @@ object CaptureSet:
9951006
(val source: Var, val bimap: BiTypeMap, initialElems: Refs)(using @constructorOnly ctx: Context)
9961007
extends DerivedVar(source.owner, initialElems):
9971008

1009+
if debugVars && id == debugTarget then
1010+
println(i"variable $id is derived from $source")
1011+
assert(false)
1012+
9981013
override def tryInclude(elem: Capability, origin: CaptureSet)(using Context, VarState): Boolean =
9991014
if origin eq source then
10001015
val mappedElem = bimap.mapCapability(elem)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ class CheckCaptures extends Recheck, SymTransformer:
430430
def capturedVars(sym: Symbol)(using Context): CaptureSet =
431431
myCapturedVars.getOrElseUpdate(sym,
432432
if sym.isTerm || !sym.owner.isStaticOwner
433-
then CaptureSet.Var(sym)
433+
then CaptureSet.Var(sym, nestedOK = false)
434434
else CaptureSet.empty)
435435

436436
// ---- Record Uses with MarkFree ----------------------------------------------------

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
495495
def transformResultType(tpt: TypeTree, sym: Symbol)(using Context): Unit =
496496
// First step: Transform the type and record it as knownType of tpt.
497497
try
498-
transformTT(tpt, sym, boxed = false)
498+
inContext(ctx.addMode(Mode.CCPreciseOwner)):
499+
transformTT(tpt, sym, boxed = false)
499500
catch case ex: IllegalCaptureRef =>
500501
capt.println(i"fail while transforming result type $tpt of $sym")
501502
throw ex
@@ -851,7 +852,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
851852

852853
/** Add a capture set variable to `tp` if necessary. */
853854
private def addVar(tp: Type, owner: Symbol)(using Context): Type =
854-
decorate(tp, CaptureSet.Var(owner, _))
855+
decorate(tp, CaptureSet.Var(owner, _, nestedOK = !ctx.mode.is(Mode.CCPreciseOwner)))
855856

856857
/** A map that adds <fluid> capture sets at all contra- and invariant positions
857858
* in a type where a capture set would be needed. This is used to make types

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ object Feature:
3737
val modularity = experimental("modularity")
3838
val quotedPatternsWithPolymorphicFunctions = experimental("quotedPatternsWithPolymorphicFunctions")
3939
val packageObjectValues = experimental("packageObjectValues")
40+
val multiSpreads = experimental("multiSpreads")
4041
val subCases = experimental("subCases")
4142

4243
def experimentalAutoEnableFeatures(using Context): List[TermName] =

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,11 @@ class Definitions {
468468
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
469469
MethodType(List(ThrowableType), NothingType))
470470

471+
@tu lazy val spreadMethod = enterMethod(OpsPackageClass, nme.spread,
472+
PolyType(TypeBounds.empty :: Nil)(
473+
tl => MethodType(AnyType :: Nil, tl.paramRefs(0))
474+
))
475+
471476
@tu lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
472477
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyType))
473478
def NothingType: TypeRef = NothingClass.typeRef
@@ -519,6 +524,8 @@ class Definitions {
519524
@tu lazy val newGenericArrayMethod: TermSymbol = DottyArraysModule.requiredMethod("newGenericArray")
520525
@tu lazy val newArrayMethod: TermSymbol = DottyArraysModule.requiredMethod("newArray")
521526

527+
@tu lazy val VarArgsBuilderModule: Symbol = requiredModule("scala.runtime.VarArgsBuilder")
528+
522529
def getWrapVarargsArrayModule: Symbol = ScalaRuntimeModule
523530

524531
// The set of all wrap{X, Ref}Array methods, where X is a value type
@@ -563,11 +570,12 @@ class Definitions {
563570
@tu lazy val Seq_apply : Symbol = SeqClass.requiredMethod(nme.apply)
564571
@tu lazy val Seq_head : Symbol = SeqClass.requiredMethod(nme.head)
565572
@tu lazy val Seq_drop : Symbol = SeqClass.requiredMethod(nme.drop)
573+
@tu lazy val Seq_dropRight : Symbol = SeqClass.requiredMethod(nme.dropRight)
574+
@tu lazy val Seq_takeRight : Symbol = SeqClass.requiredMethod(nme.takeRight)
566575
@tu lazy val Seq_lengthCompare: Symbol = SeqClass.requiredMethod(nme.lengthCompare, List(IntType))
567576
@tu lazy val Seq_length : Symbol = SeqClass.requiredMethod(nme.length)
568577
@tu lazy val Seq_toSeq : Symbol = SeqClass.requiredMethod(nme.toSeq)
569578

570-
571579
@tu lazy val StringOps: Symbol = requiredClass("scala.collection.StringOps")
572580
@tu lazy val StringOps_format: Symbol = StringOps.requiredMethod(nme.format)
573581

@@ -2234,7 +2242,7 @@ class Definitions {
22342242

22352243
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
22362244
@tu lazy val syntheticCoreMethods: List[TermSymbol] =
2237-
AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)
2245+
AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod, spreadMethod)
22382246

22392247
@tu lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet
22402248

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ object Mode {
141141
*/
142142
val InPackageClauseName: Mode = newMode(19, "InPackageClauseName")
143143

144+
/** When creating capset Vars in cc.Setup, mark the variable to be in
145+
* the result type of the context's owner, so that nested vals cannot
146+
* be included in it.
147+
* Reuses the value of InPackageClauseName to save Mode bits.
148+
* This is OK since InPackageClauseName is only set and tested during Typer,
149+
* and CCPreciseOwner only has an effect during phase CheckCaptures.
150+
*/
151+
val CCPreciseOwner = InPackageClauseName
152+
144153
/** We are in the IDE */
145154
val Interactive: Mode = newMode(20, "Interactive")
146155

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ object StdNames {
470470
val doubleHash: N = "doubleHash"
471471
val dotty: N = "dotty"
472472
val drop: N = "drop"
473+
val dropRight: N = "dropRight"
473474
val dynamics: N = "dynamics"
474475
val elem: N = "elem"
475476
val elems: N = "elems"
@@ -619,6 +620,7 @@ object StdNames {
619620
val setSymbol: N = "setSymbol"
620621
val setType: N = "setType"
621622
val setTypeSignature: N = "setTypeSignature"
623+
val spread: N = "spread"
622624
val standardInterpolator: N = "standardInterpolator"
623625
val staticClass : N = "staticClass"
624626
val staticModule : N = "staticModule"
@@ -801,6 +803,7 @@ object StdNames {
801803
val takeModulo: N = "takeModulo"
802804
val takeNot: N = "takeNot"
803805
val takeOr: N = "takeOr"
806+
val takeRight: N = "takeRight"
804807
val takeXor: N = "takeXor"
805808
val testEqual: N = "testEqual"
806809
val testGreaterOrEqualThan: N = "testGreaterOrEqualThan"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,17 +1056,22 @@ object Parsers {
10561056
}
10571057

10581058
/** Is current ident a `*`, and is it followed by a `)`, `, )`, `,EOF`? The latter two are not
1059-
syntactically valid, but we need to include them here for error recovery. */
1059+
syntactically valid, but we need to include them here for error recovery.
1060+
Under experimental.multiSpreads we allow `*`` followed by `,` unconditionally.
1061+
*/
10601062
def followingIsVararg(): Boolean =
10611063
in.isIdent(nme.raw.STAR) && {
10621064
val lookahead = in.LookaheadScanner()
10631065
lookahead.nextToken()
10641066
lookahead.token == RPAREN
10651067
|| lookahead.token == COMMA
1066-
&& {
1067-
lookahead.nextToken()
1068-
lookahead.token == RPAREN || lookahead.token == EOF
1069-
}
1068+
&& (
1069+
in.featureEnabled(Feature.multiSpreads)
1070+
|| {
1071+
lookahead.nextToken()
1072+
lookahead.token == RPAREN || lookahead.token == EOF
1073+
}
1074+
)
10701075
}
10711076

10721077
/** When encountering a `:`, is that in the binding of a lambda?
@@ -3347,7 +3352,9 @@ object Parsers {
33473352
if (in.token == RPAREN) Nil else patterns(location)
33483353

33493354
/** ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
3350-
* | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’
3355+
* | ‘(’ [Patterns ‘,’] PatVar ‘*’ [‘,’ Patterns] ‘)’
3356+
*
3357+
* -- It is checked in Typer that there are no repeated PatVar arguments.
33513358
*/
33523359
def argumentPatterns(): List[Tree] =
33533360
inParensWithCommas(patternsOpt(Location.InPatternArgs))

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ object PatternMatcher {
198198
case object NonNullTest extends Test // scrutinee ne null
199199
case object GuardTest extends Test // scrutinee
200200

201+
val noLengthTest = LengthTest(0, exact = false)
202+
201203
// ------- Generating plans from trees ------------------------
202204

203205
/** A set of variabes that are known to be not null */
@@ -291,38 +293,67 @@ object PatternMatcher {
291293
/** Plan for matching the sequence in `seqSym` against sequence elements `args`.
292294
* If `exact` is true, the sequence is not permitted to have any elements following `args`.
293295
*/
294-
def matchElemsPlan(seqSym: Symbol, args: List[Tree], exact: Boolean, onSuccess: Plan) = {
295-
val selectors = args.indices.toList.map(idx =>
296-
ref(seqSym).select(defn.Seq_apply.matchingMember(seqSym.info)).appliedTo(Literal(Constant(idx))))
297-
TestPlan(LengthTest(args.length, exact), seqSym, seqSym.span,
298-
matchArgsPlan(selectors, args, onSuccess))
299-
}
296+
def matchElemsPlan(seqSym: Symbol, args: List[Tree], lengthTest: LengthTest, onSuccess: Plan) =
297+
val selectors = args.indices.toList.map: idx =>
298+
ref(seqSym).select(defn.Seq_apply.matchingMember(seqSym.info)).appliedTo(Literal(Constant(idx)))
299+
if lengthTest.len == 0 && lengthTest.exact == false then // redundant test
300+
matchArgsPlan(selectors, args, onSuccess)
301+
else
302+
TestPlan(lengthTest, seqSym, seqSym.span,
303+
matchArgsPlan(selectors, args, onSuccess))
300304

301305
/** Plan for matching the sequence in `getResult` against sequence elements
302-
* and a possible last varargs argument `args`.
306+
* `args`. Sequence elements may contain a varargs argument.
307+
* Example:
308+
*
309+
* lst match case Seq(1, xs*, 2, 3) => ...
310+
*
311+
* generates code which is equivalent to:
312+
*
313+
* if lst != null then
314+
* if lst.lengthCompare >= 3 then
315+
* if lst(0) == 1 then
316+
* val x1 = lst.drop(1)
317+
* val xs = x1.dropRight(2)
318+
* val x2 = lst.takeRight(2)
319+
* if x2(0) == 2 && x2(1) == 3 then
320+
* return[matchResult] ...
303321
*/
304-
def unapplySeqPlan(getResult: Symbol, args: List[Tree]): Plan = args.lastOption match {
305-
case Some(VarArgPattern(arg)) =>
306-
val matchRemaining =
307-
if (args.length == 1) {
308-
val toSeq = ref(getResult)
309-
.select(defn.Seq_toSeq.matchingMember(getResult.info))
310-
letAbstract(toSeq) { toSeqResult =>
311-
patternPlan(toSeqResult, arg, onSuccess)
312-
}
313-
}
314-
else {
315-
val dropped = ref(getResult)
316-
.select(defn.Seq_drop.matchingMember(getResult.info))
317-
.appliedTo(Literal(Constant(args.length - 1)))
318-
letAbstract(dropped) { droppedResult =>
319-
patternPlan(droppedResult, arg, onSuccess)
320-
}
321-
}
322-
matchElemsPlan(getResult, args.init, exact = false, matchRemaining)
323-
case _ =>
324-
matchElemsPlan(getResult, args, exact = true, onSuccess)
325-
}
322+
def unapplySeqPlan(getResult: Symbol, args: List[Tree]): Plan =
323+
val (leading, varargAndRest) = args.span:
324+
case VarArgPattern(_) => false
325+
case _ => true
326+
varargAndRest match
327+
case VarArgPattern(arg) :: trailing =>
328+
val remaining =
329+
if leading.isEmpty then
330+
ref(getResult)
331+
.select(defn.Seq_toSeq.matchingMember(getResult.info))
332+
else
333+
ref(getResult)
334+
.select(defn.Seq_drop.matchingMember(getResult.info))
335+
.appliedTo(Literal(Constant(leading.length)))
336+
val matchRemaining =
337+
letAbstract(remaining): remainingResult =>
338+
if trailing.isEmpty then
339+
patternPlan(remainingResult, arg, onSuccess)
340+
else
341+
val seq = ref(remainingResult)
342+
.select(defn.Seq_dropRight.matchingMember(remainingResult.info))
343+
.appliedTo(Literal(Constant(trailing.length)))
344+
letAbstract(seq): seqResult =>
345+
val rest = ref(remainingResult)
346+
.select(defn.Seq_takeRight.matchingMember(remainingResult.info))
347+
.appliedTo(Literal(Constant(trailing.length)))
348+
val matchTrailing =
349+
letAbstract(rest): trailingResult =>
350+
matchElemsPlan(trailingResult, trailing, noLengthTest, onSuccess)
351+
patternPlan(seqResult, arg, matchTrailing)
352+
matchElemsPlan(getResult, leading,
353+
LengthTest(leading.length + trailing.length, exact = false),
354+
matchRemaining)
355+
case _ =>
356+
matchElemsPlan(getResult, args, LengthTest(args.length, exact = true), onSuccess)
326357

327358
/** Plan for matching the sequence in `getResult`
328359
*
@@ -491,7 +522,7 @@ object PatternMatcher {
491522
case WildcardPattern() | This(_) =>
492523
onSuccess
493524
case SeqLiteral(pats, _) =>
494-
matchElemsPlan(scrutinee, pats, exact = true, onSuccess)
525+
matchElemsPlan(scrutinee, pats, LengthTest(pats.length, exact = true), onSuccess)
495526
case _ =>
496527
TestPlan(EqualTest(tree), scrutinee, tree.span, onSuccess)
497528
}

0 commit comments

Comments
 (0)