Skip to content

Commit 2e76eac

Browse files
committed
Drop only transparent traits from intersections
Don't drop transparent classes. This means that a type such as ``` Any { type T = A } & Object ``` will be kept as is. Previously it was reduced to `Any { type T = A }` but that feels wrong. Transparent classes now only enter the picture for typing union types. If the join of a union types consists only of transparent traits or classes, keep it instead of widening it.
1 parent 1717ed7 commit 2e76eac

File tree

5 files changed

+27
-20
lines changed

5 files changed

+27
-20
lines changed

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,12 @@ trait ConstraintHandling {
556556
inst
557557
end approximation
558558

559-
private def isTransparent(tp: Type)(using Context): Boolean = tp match
559+
private def isTransparent(tp: Type, traitOnly: Boolean)(using Context): Boolean = tp match
560560
case AndType(tp1, tp2) =>
561-
isTransparent(tp1) && isTransparent(tp2)
561+
isTransparent(tp1, traitOnly) && isTransparent(tp2, traitOnly)
562562
case _ =>
563-
tp.underlyingClassRef(refinementOK = false).typeSymbol.isTransparentClass
563+
val cls = tp.underlyingClassRef(refinementOK = false).typeSymbol
564+
cls.isTransparentClass && (!traitOnly || cls.is(Trait))
564565

565566
/** If `tp` is an intersection such that some operands are transparent trait instances
566567
* and others are not, replace as many transparent trait instances as possible with Any
@@ -569,27 +570,27 @@ trait ConstraintHandling {
569570
* types (since in this case the type was not a true intersection of transparent traits
570571
* and other types to start with).
571572
*/
572-
def dropTransparentClasses(tp: Type, bound: Type)(using Context): Type =
573+
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
573574
var kept: Set[Type] = Set() // types to keep since otherwise bound would not fit
574575
var dropped: List[Type] = List() // the types dropped so far, last one on top
575576

576-
def dropOneTransparentClass(tp: Type): Type =
577-
if isTransparent(tp) && !kept.contains(tp) then
577+
def dropOneTransparentTrait(tp: Type): Type =
578+
if isTransparent(tp, traitOnly = true) && !kept.contains(tp) then
578579
dropped = tp :: dropped
579580
defn.AnyType
580581
else tp match
581582
case AndType(tp1, tp2) =>
582-
val tp1w = dropOneTransparentClass(tp1)
583+
val tp1w = dropOneTransparentTrait(tp1)
583584
if tp1w ne tp1 then tp1w & tp2
584585
else
585-
val tp2w = dropOneTransparentClass(tp2)
586+
val tp2w = dropOneTransparentTrait(tp2)
586587
if tp2w ne tp2 then tp1 & tp2w
587588
else tp
588589
case _ =>
589590
tp
590591

591592
def recur(tp: Type): Type =
592-
val tpw = dropOneTransparentClass(tp)
593+
val tpw = dropOneTransparentTrait(tp)
593594
if tpw eq tp then tp
594595
else if tpw <:< bound then recur(tpw)
595596
else
@@ -606,7 +607,7 @@ trait ConstraintHandling {
606607
tp
607608
else
608609
tpw
609-
end dropTransparentClasses
610+
end dropTransparentTraits
610611

611612
/** If `tp` is an applied match type alias which is also an unreducible application
612613
* of a higher-kinded type to a wildcard argument, widen to the match type's bound,
@@ -632,7 +633,7 @@ trait ConstraintHandling {
632633
* union type (except for unions | Null, which are kept in the state they were).
633634
* 3. Widen some irreducible applications of higher-kinded types to wildcard arguments
634635
* (see @widenIrreducible).
635-
* 4. Drop transparent traits from intersections (see @dropTransparentClasses).
636+
* 4. Drop transparent traits from intersections (see @dropTransparentTraits).
636637
*
637638
* Don't do these widenings if `bound` is a subtype of `scala.Singleton`.
638639
* Also, if the result of these widenings is a TypeRef to a module class,
@@ -663,10 +664,10 @@ trait ConstraintHandling {
663664
val widenedFromSingle = widenSingle(inst)
664665
val widenedFromUnion = widenOr(widenedFromSingle)
665666
val widened =
666-
if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion) then
667+
if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion, traitOnly = false) then
667668
widenedFromSingle
668669
else
669-
dropTransparentClasses(widenedFromUnion, bound)
670+
dropTransparentTraits(widenedFromUnion, bound)
670671
widenIrreducible(widened)
671672

672673
wideInst match

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,8 +3011,8 @@ object TypeComparer {
30113011
def widenInferred(inst: Type, bound: Type, widenUnions: Boolean)(using Context): Type =
30123012
comparing(_.widenInferred(inst, bound, widenUnions))
30133013

3014-
def dropTransparentClasses(tp: Type, bound: Type)(using Context): Type =
3015-
comparing(_.dropTransparentClasses(tp, bound))
3014+
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
3015+
comparing(_.dropTransparentTraits(tp, bound))
30163016

30173017
def constrainPatternType(pat: Type, scrut: Type, forceInvariantRefinement: Boolean = false)(using Context): Boolean =
30183018
comparing(_.constrainPatternType(pat, scrut, forceInvariantRefinement))

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1210,7 +1210,7 @@ trait Applications extends Compatibility {
12101210
&& tree.tpe.classSymbol.isEnumCase
12111211
&& tree.tpe.widen.isValueType
12121212
then
1213-
val widened = TypeComparer.dropTransparentClasses(
1213+
val widened = TypeComparer.dropTransparentTraits(
12141214
tree.tpe.parents.reduceLeft(TypeComparer.andType(_, _)),
12151215
pt)
12161216
if widened <:< pt then Typed(tree, TypeTree(widened))

docs/_docs/reference/other-new-features/transparent-traits.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ declared transparent.
8585

8686
Transparent traits and classes can be given as explicit types as usual. But they are often elided when types are inferred. Roughly, the rules for type inference imply the following.
8787

88-
- Transparent traits and classes are dropped from intersections where possible.
88+
- Transparent traits are dropped from intersections where possible.
8989
- Union types are not widened if widening would result in only transparent supertypes.
9090

9191
The precise rules are as follows:
@@ -94,8 +94,8 @@ The precise rules are as follows:
9494
- where that type is not higher-kinded,
9595
- and where `B` is its known upper bound or `Any` if none exists:
9696
- If the type inferred so far is of the form `T1 & ... & Tn` where
97-
`n >= 1`, replace the maximal number of transparent `Ti`s by `Any`, while ensuring that
97+
`n >= 1`, replace the maximal number of transparent traits `Ti`s by `Any`, while ensuring that
9898
the resulting type is still a subtype of the bound `B`.
99-
- However, do not perform this widening if all transparent types `Ti` can get replaced in that way. This clause ensures that a single transparent trait instance such as [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) is not widened to [`Any`](https://scala-lang.org/api/3.x/scala/Any.html). Transparent trait instances are only dropped when they appear in conjunction with some other type.
99+
- However, do not perform this widening if all types `Ti` can get replaced in that way. This clause ensures that a single transparent trait instance such as [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) is not widened to [`Any`](https://scala-lang.org/api/3.x/scala/Any.html). Transparent trait instances are only dropped when they appear in conjunction with some other type.
100100

101-
- If the original type was a is union type that got widened in a previous step to a product consisting only of transparent types, keep the original union type instead of its widened form.
101+
- If the original type was a is union type that got widened in a previous step to a product consisting only of transparent traits and classes, keep the original union type instead of its widened form.

tests/pos/transparent-intersect.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test:
2+
3+
val x: Any { type T = Int } & Object = ???
4+
val y = if ??? then x else x
5+
val _ : Object { type T = Int } = y
6+

0 commit comments

Comments
 (0)