Skip to content

Commit d2d159e

Browse files
Backport "More careful ClassTag instantiation" to 3.7.3 (#23714)
Backports #23659 to the 3.7.3-RC1. PR submitted by the release tooling. [skip ci]
2 parents 172cecb + 6e675e0 commit d2d159e

File tree

3 files changed

+83
-5
lines changed

3 files changed

+83
-5
lines changed

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import ast.tpd.*
2020
import Synthesizer.*
2121
import sbt.ExtractDependencies.*
2222
import xsbti.api.DependencyContext.*
23+
import TypeComparer.{fullLowerBound, fullUpperBound}
2324

2425
/** Synthesize terms for special classes */
2526
class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
@@ -38,10 +39,32 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
3839
// bounds are usually widened during instantiation.
3940
instArg(tp.tp1)
4041
case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) =>
42+
// If tvar has a lower or upper bound:
43+
// 1. If the bound is not another type variable, use this as approximation.
44+
// 2. Otherwise, if the type can be forced to be fully defined, use that type
45+
// as approximation.
46+
// 3. Otherwise leave argument uninstantiated.
47+
// The reason for (2) is that we observed complicated constraints in i23611.scala
48+
// that get better types if a fully defined type is computed than if several type
49+
// variables are approximated incrementally. This is a minimization of some ZIO code.
50+
// So in order to keep backwards compatibility (where before we _only_ did 2) we
51+
// add that special case.
52+
def isGroundConstr(tp: Type): Boolean = tp.dealias match
53+
case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) => false
54+
case pref: TypeParamRef if ctx.typerState.constraint.contains(pref) => false
55+
case tp: AndOrType => isGroundConstr(tp.tp1) && isGroundConstr(tp.tp2)
56+
case _ => true
4157
instArg(
42-
if tvar.hasLowerBound then tvar.instantiate(fromBelow = true)
43-
else if tvar.hasUpperBound then tvar.instantiate(fromBelow = false)
44-
else NoType)
58+
if tvar.hasLowerBound then
59+
if isGroundConstr(fullLowerBound(tvar.origin)) then tvar.instantiate(fromBelow = true)
60+
else if isFullyDefined(tp, ForceDegree.all) then tp
61+
else NoType
62+
else if tvar.hasUpperBound then
63+
if isGroundConstr(fullUpperBound(tvar.origin)) then tvar.instantiate(fromBelow = false)
64+
else if isFullyDefined(tp, ForceDegree.all) then tp
65+
else NoType
66+
else
67+
NoType)
4568
case _ =>
4669
tp
4770

@@ -569,9 +592,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
569592
resType <:< target
570593
val tparams = poly.paramRefs
571594
val variances = childClass.typeParams.map(_.paramVarianceSign)
572-
val instanceTypes = tparams.lazyZip(variances).map((tparam, variance) =>
595+
val instanceTypes = tparams.lazyZip(variances).map: (tparam, variance) =>
573596
TypeComparer.instanceType(tparam, fromBelow = variance < 0, Widen.Unions)
574-
)
575597
val instanceType = resType.substParams(poly, instanceTypes)
576598
// this is broken in tests/run/i13332intersection.scala,
577599
// because type parameters are not correctly inferred.

tests/pos/i23611.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import java.io.{File, IOException}
2+
import java.net.URI
3+
import java.nio.file.{Path, Paths}
4+
import scala.reflect.ClassTag
5+
6+
trait FileConnectors {
7+
def listPath(path: => Path): ZStream[Any, IOException, Path]
8+
9+
final def listFile(file: => File): ZStream[Any, IOException, File] =
10+
for {
11+
path <- null.asInstanceOf[ZStream[Any, IOException, Path]]
12+
r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie)
13+
} yield r
14+
}
15+
16+
sealed trait ZIO[-R, +E, +A]
17+
extension [R, E <: Throwable, A](self: ZIO[R, E, A])
18+
def refineToOrDie[E1 <: E: ClassTag]: ZIO[R, E1, A] = ???
19+
20+
object ZIO:
21+
def attempt[A](code: => A): ZIO[Any, Throwable, A] = ???
22+
23+
sealed trait ZStream[-R, +E, +A]:
24+
def map[B](f: A => B): ZStream[R, E, B] = ???
25+
def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B]
26+
def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1]

tests/pos/i23611a.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import java.io.{File, IOException}
2+
import java.net.URI
3+
import java.nio.file.{Path, Paths}
4+
import scala.reflect.ClassTag
5+
6+
trait FileConnectors {
7+
def listPath(path: => Path): ZStream[Any, IOException, Path]
8+
9+
final def listFile(file: => File): ZStream[Any, IOException, File] =
10+
for {
11+
path <- null.asInstanceOf[ZStream[Any, IOException, Path]]
12+
r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie)
13+
} yield r
14+
}
15+
16+
sealed abstract class CanFail[-E]
17+
object CanFail:
18+
given [E]: CanFail[E] = ???
19+
20+
sealed trait ZIO[-R, +E, +A]
21+
extension [R, E <: Throwable, A](self: ZIO[R, E, A])
22+
def refineToOrDie[E1 <: E: ClassTag](using CanFail[E]): ZIO[R, E1, A] = ???
23+
24+
object ZIO:
25+
def attempt[A](code: => A): ZIO[Any, Throwable, A] = ???
26+
27+
sealed trait ZStream[-R, +E, +A]:
28+
def map[B](f: A => B): ZStream[R, E, B] = ???
29+
def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B]
30+
def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1]

0 commit comments

Comments
 (0)