Skip to content

Commit e587a81

Browse files
authored
Fix calculation to drop transparent classes (#16344)
Two fixes: 1. Don't forget about refinements 2. Don't dealias Fixes #16342 Fixes #16338 The first fix is essential for #16342. The second fix is just to keep types tidy and not open aliases needlessly. It probably fixes issues #16337 and #16336 as well, but the test cases were not self-contained, so I could not try them out. It might fix other recent regressions as well. The previous incorrect version hid errors in previous regressions #15365 and #16311 which will need to be re-opened now.
2 parents 4ae4dc8 + 2e76eac commit e587a81

File tree

9 files changed

+75
-24
lines changed

9 files changed

+75
-24
lines changed

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

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

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

563566
/** If `tp` is an intersection such that some operands are transparent trait instances
564567
* and others are not, replace as many transparent trait instances as possible with Any
@@ -567,28 +570,27 @@ trait ConstraintHandling {
567570
* types (since in this case the type was not a true intersection of transparent traits
568571
* and other types to start with).
569572
*/
570-
def dropTransparentClasses(tp: Type, bound: Type)(using Context): Type =
573+
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
571574
var kept: Set[Type] = Set() // types to keep since otherwise bound would not fit
572575
var dropped: List[Type] = List() // the types dropped so far, last one on top
573576

574-
def dropOneTransparentClass(tp: Type): Type =
575-
val tpd = tp.dealias
576-
if isTransparent(tpd) && !kept.contains(tpd) then
577-
dropped = tpd :: dropped
577+
def dropOneTransparentTrait(tp: Type): Type =
578+
if isTransparent(tp, traitOnly = true) && !kept.contains(tp) then
579+
dropped = tp :: dropped
578580
defn.AnyType
579-
else tpd match
581+
else tp match
580582
case AndType(tp1, tp2) =>
581-
val tp1w = dropOneTransparentClass(tp1)
583+
val tp1w = dropOneTransparentTrait(tp1)
582584
if tp1w ne tp1 then tp1w & tp2
583585
else
584-
val tp2w = dropOneTransparentClass(tp2)
586+
val tp2w = dropOneTransparentTrait(tp2)
585587
if tp2w ne tp2 then tp1 & tp2w
586-
else tpd
588+
else tp
587589
case _ =>
588590
tp
589591

590592
def recur(tp: Type): Type =
591-
val tpw = dropOneTransparentClass(tp)
593+
val tpw = dropOneTransparentTrait(tp)
592594
if tpw eq tp then tp
593595
else if tpw <:< bound then recur(tpw)
594596
else
@@ -605,7 +607,7 @@ trait ConstraintHandling {
605607
tp
606608
else
607609
tpw
608-
end dropTransparentClasses
610+
end dropTransparentTraits
609611

610612
/** If `tp` is an applied match type alias which is also an unreducible application
611613
* of a higher-kinded type to a wildcard argument, widen to the match type's bound,
@@ -631,7 +633,7 @@ trait ConstraintHandling {
631633
* union type (except for unions | Null, which are kept in the state they were).
632634
* 3. Widen some irreducible applications of higher-kinded types to wildcard arguments
633635
* (see @widenIrreducible).
634-
* 4. Drop transparent traits from intersections (see @dropTransparentClasses).
636+
* 4. Drop transparent traits from intersections (see @dropTransparentTraits).
635637
*
636638
* Don't do these widenings if `bound` is a subtype of `scala.Singleton`.
637639
* Also, if the result of these widenings is a TypeRef to a module class,
@@ -662,10 +664,10 @@ trait ConstraintHandling {
662664
val widenedFromSingle = widenSingle(inst)
663665
val widenedFromUnion = widenOr(widenedFromSingle)
664666
val widened =
665-
if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion) then
667+
if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion, traitOnly = false) then
666668
widenedFromSingle
667669
else
668-
dropTransparentClasses(widenedFromUnion, bound)
670+
dropTransparentTraits(widenedFromUnion, bound)
669671
widenIrreducible(widened)
670672

671673
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.
File renamed without changes.
File renamed without changes.

tests/pos/i16338.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package de.sciss.kollflitz
2+
3+
import scala.collection.*
4+
5+
type Tagged[U] = { type Tag = U }
6+
type @@ [+T, U] = T with Tagged[U]
7+
private val anyTagger = new Tagger[Any]
8+
final class Tagger[U] private[kollflitz] {
9+
def apply[T](t : T): T @@ U = t.asInstanceOf[T @@ U]
10+
}
11+
def tag[U]: Tagger[U] = anyTagger.asInstanceOf[Tagger[U]]
12+
13+
sealed trait Sorted
14+
15+
16+
/** Enrichment methods for random access collections. */
17+
implicit final class KollFlitzSortedIndexedSeq[A, CC[_], Repr](val self: SeqOps[A, CC, Repr] @@ Sorted)
18+
extends AnyVal {
19+
20+
/** Nearest percentile (rounded index, no interpolation). */
21+
def percentile(n: Int): A = self((self.size * n - 50) / 100)
22+
23+
/** Median found by rounding the index (no interpolation). */
24+
def median: A = percentile(50)
25+
}

tests/pos/i16342.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
type Opaque = Base with Tag
2+
3+
type Base = Any {
4+
type Hack
5+
}
6+
7+
trait Tag
8+
9+
object Opaque {
10+
def apply(value: String): Opaque = value.asInstanceOf[Opaque]
11+
12+
def unapply(userId: Opaque): Option[String] = Option(userId).map(_.value)
13+
def unappy2(userId: Base with Tag): Option[String] = Option(userId).map(_.value)
14+
}
15+
16+
final implicit class Ops(private val userId: Opaque) extends AnyVal {
17+
def value: String = userId.asInstanceOf[String]
18+
}

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)