Skip to content

Commit 6501bdb

Browse files
authored
Merge pull request #549 from scala/backport-lts-3.3-23659
Backport "More careful ClassTag instantiation" to 3.3 LTS
2 parents 18bc208 + d6bd65e commit 6501bdb

File tree

3 files changed

+82
-3
lines changed

3 files changed

+82
-3
lines changed

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import annotation.{tailrec, constructorOnly}
1818
import ast.tpd
1919
import ast.tpd.*
2020
import Synthesizer.*
21+
import TypeComparer.{fullLowerBound, fullUpperBound}
2122

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

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)