Skip to content

Commit 7f8223d

Browse files
committed
Merge branch 'release-3.3.4' into lts-3.3
2 parents 53791b0 + e76de95 commit 7f8223d

28 files changed

+1363
-182
lines changed

changelogs/3.3.4-RC1.md

Lines changed: 542 additions & 0 deletions
Large diffs are not rendered by default.

changelogs/3.3.4-RC2.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Backported fixes
2+
3+
- Fix a bundle of patmat issues [#21000](https://github.com/scala/scala3/pull/21000)
4+
- Fixed false positive unreachable local object [#21532](https://github.com/scala/scala3/pull/21532)
5+
- Reimplement support for type aliases in SAM types [#18317](https://github.com/scala/scala3/pull/18317)
6+
- Allow SAM types to contain match alias refinements [#20092](https://github.com/scala/scala3/pull/20092)
7+
- Allow SAM types to contain multiple refinements [#20172](https://github.com/scala/scala3/pull/20172)
8+
9+
# Contributors
10+
11+
Thank you to all the contributors who made this release possible 🎉
12+
13+
According to `git shortlog -sn --no-merges 3.3.4-RC1..3.3.4-RC2` these are:
14+
15+
```
16+
11 Dale Wijnand
17+
7 Wojciech Mazur
18+
1 Guillaume Martres
19+
```

changelogs/3.3.4-RC3.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Reverted changes
2+
3+
- Revert pattern matching improvements introducing regressions in 3.3.4 [#21573](https://github.com/scala/scala3/pull/21573)
4+
5+
# Contributors
6+
7+
Thank you to all the contributors who made this release possible 🎉
8+
9+
According to `git shortlog -sn --no-merges 3.3.4-RC2..3.3.4-RC3` these are:
10+
11+
```
12+
4 Wojciech Mazur
13+
```

changelogs/3.3.4-RC4.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Reverted changes
2+
3+
- Revert "Reduce projections of type aliases with class type prefixes" in LTS [#21609](https://github.com/scala/scala3/pull/21609)
4+
5+
# Contributors
6+
7+
Thank you to all the contributors who made this release possible 🎉
8+
9+
According to `git shortlog -sn --no-merges 3.3.4-RC3..3.3.4-RC4` these are:
10+
11+
```
12+
4 Wojciech Mazur
13+
```

changelogs/3.3.4.md

Lines changed: 547 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,24 +344,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
344344

345345
/** An anonymous class
346346
*
347-
* new parents { forwarders }
347+
* new parents { termForwarders; typeAliases }
348348
*
349-
* where `forwarders` contains forwarders for all functions in `fns`.
350-
* @param parents a non-empty list of class types
351-
* @param fns a non-empty of functions for which forwarders should be defined in the class.
352-
* The class has the same owner as the first function in `fns`.
353-
* Its position is the union of all functions in `fns`.
349+
* @param parents a non-empty list of class types
350+
* @param termForwarders a non-empty list of forwarding definitions specified by their name and the definition they forward to.
351+
* @param typeMembers a possibly-empty list of type members specified by their name and their right hand side.
352+
*
353+
* The class has the same owner as the first function in `termForwarders`.
354+
* Its position is the union of all symbols in `termForwarders`.
354355
*/
355-
def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(using Context): Block = {
356-
AnonClass(fns.head.owner, parents, fns.map(_.span).reduceLeft(_ union _)) { cls =>
357-
def forwarder(fn: TermSymbol, name: TermName) = {
356+
def AnonClass(parents: List[Type], termForwarders: List[(TermName, TermSymbol)],
357+
typeMembers: List[(TypeName, TypeBounds)] = Nil)(using Context): Block = {
358+
AnonClass(termForwarders.head._2.owner, parents, termForwarders.map(_._2.span).reduceLeft(_ union _)) { cls =>
359+
def forwarder(name: TermName, fn: TermSymbol) = {
358360
val fwdMeth = fn.copy(cls, name, Synthetic | Method | Final).entered.asTerm
359361
for overridden <- fwdMeth.allOverriddenSymbols do
360362
if overridden.is(Extension) then fwdMeth.setFlag(Extension)
361363
if !overridden.is(Deferred) then fwdMeth.setFlag(Override)
362364
DefDef(fwdMeth, ref(fn).appliedToArgss(_))
363365
}
364-
fns.lazyZip(methNames).map(forwarder)
366+
termForwarders.map((name, sym) => forwarder(name, sym)) ++
367+
typeMembers.map((name, info) => TypeDef(newSymbol(cls, name, Synthetic, info).entered))
365368
}
366369
}
367370

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

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,22 +2598,14 @@ object Types extends TypeUtils {
25982598
case _ => true
25992599
}
26002600

2601-
/** Reduce a type ref P # X, where X is a type alias and P is a refined type or
2602-
* a class type. If P is a refined type `T { X = U; ... }`, reduce P to U,
2603-
* provided U does not refer with a RecThis to the same refined type. If P is a
2604-
* class type, reduce it to the dealiasd version of P # X. This means that at typer
2605-
* we create projections only for inner classes with class prefixes, since projections
2606-
* of P # X where X is an abstract type are handled by skolemization. At later phases
2607-
* these projections might arise, though.
2601+
/** Reduce a type-ref `T { X = U; ... } # X` to `U`
2602+
* provided `U` does not refer with a RecThis to the
2603+
* refinement type `T { X = U; ... }`
26082604
*/
26092605
def reduceProjection(using Context): Type =
26102606
if (isType) {
26112607
val reduced = prefix.lookupRefined(name)
2612-
if reduced.exists then reduced
2613-
else prefix.stripTypeVar match
2614-
case pre: (AppliedType | TypeRef)
2615-
if prefix.dealias.typeSymbol.isClass && this.symbol.isAliasType => dealias
2616-
case _ => this
2608+
if reduced.exists then reduced else this
26172609
}
26182610
else this
26192611

@@ -5534,13 +5526,16 @@ object Types extends TypeUtils {
55345526
* and PolyType not allowed!) according to `possibleSamMethods`.
55355527
* - can be instantiated without arguments or with just () as argument.
55365528
*
5529+
* Additionally, a SAM type may contain type aliases refinements if they refine
5530+
* an existing type member.
5531+
*
55375532
* The pattern `SAMType(samMethod, samParent)` matches a SAM type, where `samMethod` is the
55385533
* type of the single abstract method and `samParent` is a subtype of the matched
55395534
* SAM type which has been stripped of wildcards to turn it into a valid parent
55405535
* type.
55415536
*/
55425537
object SAMType {
5543-
/** If possible, return a type which is both a subtype of `origTp` and a type
5538+
/** If possible, return a type which is both a subtype of `origTp` and a (possibly refined) type
55445539
* application of `samClass` where none of the type arguments are
55455540
* wildcards (thus making it a valid parent type), otherwise return
55465541
* NoType.
@@ -5570,27 +5565,41 @@ object Types extends TypeUtils {
55705565
* we arbitrarily pick the upper-bound.
55715566
*/
55725567
def samParent(origTp: Type, samClass: Symbol, samMeth: Symbol)(using Context): Type =
5573-
val tp = origTp.baseType(samClass)
5568+
val tp0 = origTp.baseType(samClass)
5569+
5570+
/** Copy type aliases refinements to `toTp` from `fromTp` */
5571+
def withRefinements(toType: Type, fromTp: Type): Type = fromTp.dealias match
5572+
case RefinedType(fromParent, name, info: AliasingBounds) if tp0.member(name).exists =>
5573+
val parent1 = withRefinements(toType, fromParent)
5574+
RefinedType(parent1, name, info)
5575+
case _ => toType
5576+
val tp = withRefinements(tp0, origTp)
5577+
55745578
if !(tp <:< origTp) then NoType
5575-
else tp match
5576-
case tp @ AppliedType(tycon, args) if tp.hasWildcardArg =>
5577-
val accu = new TypeAccumulator[VarianceMap[Symbol]]:
5578-
def apply(vmap: VarianceMap[Symbol], t: Type): VarianceMap[Symbol] = t match
5579-
case tp: TypeRef if tp.symbol.isAllOf(ClassTypeParam) =>
5580-
vmap.recordLocalVariance(tp.symbol, variance)
5581-
case _ =>
5582-
foldOver(vmap, t)
5583-
val vmap = accu(VarianceMap.empty, samMeth.info)
5584-
val tparams = tycon.typeParamSymbols
5585-
val args1 = args.zipWithConserve(tparams):
5586-
case (arg @ TypeBounds(lo, hi), tparam) =>
5587-
val v = vmap.computedVariance(tparam)
5588-
if v.uncheckedNN < 0 then lo
5589-
else hi
5590-
case (arg, _) => arg
5591-
tp.derivedAppliedType(tycon, args1)
5592-
case _ =>
5593-
tp
5579+
else
5580+
def approxWildcardArgs(tp: Type): Type = tp match
5581+
case tp @ AppliedType(tycon, args) if tp.hasWildcardArg =>
5582+
val accu = new TypeAccumulator[VarianceMap[Symbol]]:
5583+
def apply(vmap: VarianceMap[Symbol], t: Type): VarianceMap[Symbol] = t match
5584+
case tp: TypeRef if tp.symbol.isAllOf(ClassTypeParam) =>
5585+
vmap.recordLocalVariance(tp.symbol, variance)
5586+
case _ =>
5587+
foldOver(vmap, t)
5588+
val vmap = accu(VarianceMap.empty, samMeth.info)
5589+
val tparams = tycon.typeParamSymbols
5590+
val args1 = args.zipWithConserve(tparams):
5591+
case (arg @ TypeBounds(lo, hi), tparam) =>
5592+
val v = vmap.computedVariance(tparam)
5593+
if v.uncheckedNN < 0 then lo
5594+
else hi
5595+
case (arg, _) => arg
5596+
tp.derivedAppliedType(tycon, args1)
5597+
case tp @ RefinedType(parent, name, info) =>
5598+
tp.derivedRefinedType(approxWildcardArgs(parent), name, info)
5599+
case _ =>
5600+
tp
5601+
approxWildcardArgs(tp)
5602+
end samParent
55945603

55955604
def samClass(tp: Type)(using Context): Symbol = tp match
55965605
case tp: ClassInfo =>

compiler/src/dotty/tools/dotc/reporting/Message.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self =>
374374
override def canExplain = true
375375

376376
/** Override with `true` for messages that should always be shown even if their
377-
* position overlaps another message of a different class. On the other hand
377+
* position overlaps another messsage of a different class. On the other hand
378378
* multiple messages of the same class with overlapping positions will lead
379379
* to only a single message of that class to be issued.
380380
*/

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

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import core.*
66
import Scopes.newScope
77
import Contexts.*, Symbols.*, Types.*, Flags.*, Decorators.*, StdNames.*, Constants.*
88
import MegaPhase.*
9+
import Names.TypeName
10+
import Symbols.*
911
import NullOpsDecorator.*
1012
import ast.untpd
1113

@@ -50,16 +52,28 @@ class ExpandSAMs extends MiniPhase:
5052
case tpe if defn.isContextFunctionType(tpe) =>
5153
tree
5254
case SAMType(_, tpe) if tpe.isRef(defn.PartialFunctionClass) =>
53-
val tpe1 = checkRefinements(tpe, fn)
54-
toPartialFunction(tree, tpe1)
55+
toPartialFunction(tree, tpe)
5556
case SAMType(_, tpe) if ExpandSAMs.isPlatformSam(tpe.classSymbol.asClass) =>
56-
checkRefinements(tpe, fn)
5757
tree
5858
case tpe =>
59-
val tpe1 = checkRefinements(tpe.stripNull, fn)
59+
// A SAM type is allowed to have type aliases refinements (see
60+
// SAMType#samParent) which must be converted into type members if
61+
// the closure is desugared into a class.
62+
val refinements = collection.mutable.ListBuffer[(TypeName, TypeAlias)]()
63+
def collectAndStripRefinements(tp: Type): Type = tp match
64+
case RefinedType(parent, name, info: TypeAlias) =>
65+
val res = collectAndStripRefinements(parent)
66+
refinements += ((name.asTypeName, info))
67+
res
68+
case _ => tp
69+
val tpe1 = collectAndStripRefinements(tpe)
6070
val Seq(samDenot) = tpe1.possibleSamMethods
6171
cpy.Block(tree)(stats,
62-
AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil))
72+
AnonClass(List(tpe1),
73+
List(samDenot.symbol.asTerm.name -> fn.symbol.asTerm),
74+
refinements.toList
75+
)
76+
)
6377
}
6478
case _ =>
6579
tree
@@ -170,13 +184,4 @@ class ExpandSAMs extends MiniPhase:
170184
List(isDefinedAtDef, applyOrElseDef)
171185
}
172186
}
173-
174-
private def checkRefinements(tpe: Type, tree: Tree)(using Context): Type = tpe.dealias match {
175-
case RefinedType(parent, name, _) =>
176-
if (name.isTermName && tpe.member(name).symbol.ownersIterator.isEmpty) // if member defined in the refinement
177-
report.error(em"Lambda does not define $name", tree.srcPos)
178-
checkRefinements(parent, tree)
179-
case tpe =>
180-
tpe
181-
}
182187
end ExpandSAMs

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ object TypeTestsCasts {
7474
}.apply(tp)
7575

7676
/** Returns true if the type arguments of `P` can be determined from `X` */
77-
def typeArgsDeterminable(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) {
77+
def typeArgsTrivial(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) {
7878
val AppliedType(tycon, _) = P
7979

8080
def underlyingLambda(tp: Type): TypeLambda = tp.ensureLambdaSub match {
@@ -155,7 +155,7 @@ object TypeTestsCasts {
155155
case x =>
156156
// always false test warnings are emitted elsewhere
157157
TypeComparer.provablyDisjoint(x, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType)))
158-
|| typeArgsDeterminable(X, tpe)
158+
|| typeArgsTrivial(X, tpe)
159159
||| i"its type arguments can't be determined from $X"
160160
}
161161
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ object SpaceEngine {
379379
project(pat)
380380

381381
case Typed(_, tpt) =>
382-
Typ(erase(tpt.tpe.stripAnnots, isValue = true, isTyped = true), decomposed = false)
382+
Typ(erase(tpt.tpe.stripAnnots, isValue = true), decomposed = false)
383383

384384
case This(_) =>
385385
Typ(pat.tpe.stripAnnots, decomposed = false)
@@ -464,13 +464,13 @@ object SpaceEngine {
464464
tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2)
465465

466466
case tp @ OrType(tp1, tp2) =>
467-
OrType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped), tp.isSoft)
467+
OrType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue), tp.isSoft)
468468

469469
case AndType(tp1, tp2) =>
470-
AndType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped))
470+
AndType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue))
471471

472472
case tp @ RefinedType(parent, _, _) =>
473-
erase(parent, inArray, isValue, isTyped)
473+
erase(parent, inArray, isValue)
474474

475475
case tref: TypeRef if tref.symbol.isPatternBound =>
476476
if inArray then erase(tref.underlying, inArray, isValue, isTyped)
@@ -905,7 +905,7 @@ object SpaceEngine {
905905
def checkMatch(m: Match)(using Context): Unit =
906906
checkMatchExhaustivityOnly(m)
907907
if reachabilityCheckable(m.selector) then checkReachability(m)
908-
908+
909909
def checkMatchExhaustivityOnly(m: Match)(using Context): Unit =
910910
if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
911911
}

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionScalaCliSuite.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class CompletionScalaCliSuite extends BaseCompletionSuite:
5757
|""".stripMargin
5858
)
5959

60+
@Ignore
6061
@Test def `version` =
6162
check(
6263
"""|//> using lib "io.circe::circe-core_sjs1:0.14.1@@"
@@ -66,6 +67,7 @@ class CompletionScalaCliSuite extends BaseCompletionSuite:
6667
)
6768

6869
// We don't to add `::` before version if `sjs1` is specified
70+
@Ignore
6971
@Test def `version-edit` =
7072
checkEdit(
7173
"""|//> using lib "io.circe::circe-core_sjs1:0.14.1@@"

tests/pending/neg/i16451.check

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Error: tests/neg/i16451.scala:13:9 ----------------------------------------------------------------------------------
2+
13 | case x: Wrapper[Color.Red.type] => Some(x) // error
3+
| ^
4+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color]
5+
-- Error: tests/neg/i16451.scala:21:9 ----------------------------------------------------------------------------------
6+
21 | case x: Wrapper[Color.Red.type] => Some(x) // error
7+
| ^
8+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any
9+
-- Error: tests/neg/i16451.scala:25:9 ----------------------------------------------------------------------------------
10+
25 | case x: Wrapper[Color.Red.type] => Some(x) // error
11+
| ^
12+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color]
13+
-- Error: tests/neg/i16451.scala:29:9 ----------------------------------------------------------------------------------
14+
29 | case x: Wrapper[Color.Red.type] => Some(x) // error
15+
| ^
16+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1
17+
-- Error: tests/neg/i16451.scala:34:11 ---------------------------------------------------------------------------------
18+
34 | case x: Wrapper[Color.Red.type] => x // error
19+
| ^
20+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color]
21+
-- Error: tests/neg/i16451.scala:39:11 ---------------------------------------------------------------------------------
22+
39 | case x: Wrapper[Color.Red.type] => x // error
23+
| ^
24+
|the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color]

0 commit comments

Comments
 (0)