Skip to content

Commit f84e673

Browse files
authored
Merge pull request #15388 from dotty-staging/backport-15343
Backport #15343: Attempt to revert 14026
2 parents 91ea5b6 + d3e0483 commit f84e673

File tree

13 files changed

+99
-5
lines changed

13 files changed

+99
-5
lines changed

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,10 @@ object Config {
226226
* reduces the number of allocated denotations by ~50%.
227227
*/
228228
inline val reuseSymDenotations = true
229+
230+
/** If true, check levels of type variables and create fresh ones as needed.
231+
* This is necessary for soundness (see 3ab18a9), but also causes several
232+
* regressions that should be fixed before turning this on.
233+
*/
234+
inline val checkLevels = false
229235
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,10 @@ trait ConstraintHandling {
9292

9393
/** Is `level` <= `maxLevel` or legal in the current context? */
9494
def levelOK(level: Int, maxLevel: Int)(using Context): Boolean =
95-
level <= maxLevel ||
96-
ctx.isAfterTyper || !ctx.typerState.isCommittable || // Leaks in these cases shouldn't break soundness
97-
level == Int.MaxValue // See `nestingLevel` above.
95+
level <= maxLevel
96+
|| ctx.isAfterTyper || !ctx.typerState.isCommittable // Leaks in these cases shouldn't break soundness
97+
|| level == Int.MaxValue // See `nestingLevel` above.
98+
|| !Config.checkLevels
9899

99100
/** If `param` is nested deeper than `maxLevel`, try to instantiate it to a
100101
* fresh type variable of level `maxLevel` and return the new variable.

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Comments.{Comment, CommentsContext}
1515
import NameKinds._
1616
import StdNames.nme
1717
import transform.SymUtils._
18+
import config.Config
1819
import collection.mutable
1920
import dotty.tools.tasty.TastyFormat.ASTsSection
2021

@@ -85,6 +86,11 @@ class TreePickler(pickler: TastyPickler) {
8586
case Some(label) =>
8687
if (label != NoAddr) writeRef(label) else pickleForwardSymRef(sym)
8788
case None =>
89+
// See pos/t1957.scala for an example where this can happen.
90+
// I believe it's a bug in typer: the type of an implicit argument refers
91+
// to a closure parameter outside the closure itself. TODO: track this down, so that we
92+
// can eliminate this case.
93+
report.log(i"pickling reference to as yet undefined $sym in ${sym.owner}", sym.srcPos)
8894
pickleForwardSymRef(sym)
8995
}
9096

@@ -197,7 +203,7 @@ class TreePickler(pickler: TastyPickler) {
197203
}
198204
else if (tpe.prefix == NoPrefix) {
199205
writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect)
200-
if !symRefs.contains(sym) && !sym.isPatternBound && !sym.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) then
206+
if Config.checkLevels && !symRefs.contains(sym) && !sym.isPatternBound && !sym.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) then
201207
report.error(i"pickling reference to as yet undefined $tpe with symbol ${sym}", sym.srcPos)
202208
pickleSymRef(sym)
203209
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,23 @@ class Namer { typer: Typer =>
15341534
// This case applies if the closure result type contains uninstantiated
15351535
// type variables. In this case, constrain the closure result from below
15361536
// by the parameter-capture-avoiding type of the body.
1537-
typedAheadExpr(mdef.rhs, tpt.tpe).tpe
1537+
val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe
1538+
1539+
// The following part is important since otherwise we might instantiate
1540+
// the closure result type with a plain functon type that refers
1541+
// to local parameters. An example where this happens in `dependent-closures.scala`
1542+
// If the code after `val rhsType` is commented out, this file fails pickling tests.
1543+
// AVOIDANCE TODO: Follow up why this happens, and whether there
1544+
// are better ways to achieve this. It would be good if we could get rid of this code.
1545+
// It seems at least partially redundant with the nesting level checking on TypeVar
1546+
// instantiation.
1547+
if !Config.checkLevels then
1548+
val hygienicType = TypeOps.avoid(rhsType, termParamss.flatten)
1549+
if (!hygienicType.isValueType || !(hygienicType <:< tpt.tpe))
1550+
report.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" +
1551+
i"it is not a supertype of the hygienic type $hygienicType", mdef.srcPos)
1552+
//println(i"lifting $rhsType over $termParamss -> $hygienicType = ${tpt.tpe}")
1553+
//println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe })
15381554
case _ =>
15391555
}
15401556
WildcardType

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,6 @@ i4176-gadt.scala
8282
i13974a.scala
8383

8484
java-inherited-type1
85+
86+
# avoidance bug
87+
i15174.scala
File renamed without changes.
File renamed without changes.

tests/pos/i14494.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object ImplNotFound:
2+
def main(args: Array[String]): Unit =
3+
val res: Seq[String | Int] = (??? : Seq[Int]).collect {
4+
case 1 => Seq("")
5+
case 2 => Seq(1)
6+
}.flatten

tests/pos/i15178.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This should be a neg test once level checking is re-enabled.
2+
3+
trait E[F[_]] {
4+
type T
5+
val value: F[T]
6+
}
7+
8+
object E {
9+
def apply[F[_], T1](value1: F[T1]) = new E[F] {
10+
type T = T1
11+
val value = value1
12+
}
13+
}
14+
15+
val a: Option[E[Ordering]] = Option(E(Ordering[Int]))
16+
val _ = a.map(it => E(it.value)) // there should be an error here
17+

tests/pos/i15184.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
def test() = {
2+
func(_ => Box(Seq.empty[String]) )
3+
}
4+
5+
def func[R0](to0: Unit => R0): Unit = ???
6+
7+
trait JsonFormat[T]
8+
object JsonFormat{
9+
implicit def immSeqFormat: JsonFormat[Seq[String]] = ???
10+
11+
implicit def iterableFormat: JsonFormat[Iterable[String]] = ???
12+
}
13+
14+
case class Box[A1: JsonFormat](elem: A1)

tests/pos/i15216.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
sealed abstract class Free[S[_], A] {
2+
final def map[B](f: A => B): Free[S, B] = ???
3+
final def flatMap[B](f: A => Free[S, B]): Free[S, B] = new Free[S, B] {}
4+
}
5+
6+
trait Parameter[T]
7+
def namedDouble(name: String): Free[Parameter, Double] = ???
8+
9+
type Double2 = (Double, Double)
10+
type Double3 = (Double, Double, Double)
11+
val spec: Free[Parameter, Either[Double3, Double2]] = for {
12+
result <-
13+
if (???) {
14+
for {
15+
x <- namedDouble("X")
16+
y <- namedDouble("Y")
17+
z <- namedDouble("Z")
18+
} yield Left((x, y, z))
19+
} else {
20+
for {
21+
x <- namedDouble("X")
22+
y <- namedDouble("Y")
23+
} yield Right((x, y))
24+
}
25+
} yield result

0 commit comments

Comments
 (0)