Skip to content

Commit 3ddb21c

Browse files
authored
Merge pull request #15343 from dotty-staging/revert-14026
Attempt to revert 14026
2 parents 89c3c10 + 44c2a3b commit 3ddb21c

File tree

14 files changed

+100
-6
lines changed

14 files changed

+100
-6
lines changed

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

+6
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

+4-3
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

+7-1
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

+17-1
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,23 @@ class Namer { typer: Typer =>
16721672
// This case applies if the closure result type contains uninstantiated
16731673
// type variables. In this case, constrain the closure result from below
16741674
// by the parameter-capture-avoiding type of the body.
1675-
typedAheadExpr(mdef.rhs, tpt.tpe).tpe
1675+
val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe
1676+
1677+
// The following part is important since otherwise we might instantiate
1678+
// the closure result type with a plain functon type that refers
1679+
// to local parameters. An example where this happens in `dependent-closures.scala`
1680+
// If the code after `val rhsType` is commented out, this file fails pickling tests.
1681+
// AVOIDANCE TODO: Follow up why this happens, and whether there
1682+
// are better ways to achieve this. It would be good if we could get rid of this code.
1683+
// It seems at least partially redundant with the nesting level checking on TypeVar
1684+
// instantiation.
1685+
if !Config.checkLevels then
1686+
val hygienicType = TypeOps.avoid(rhsType, termParamss.flatten)
1687+
if (!hygienicType.isValueType || !(hygienicType <:< tpt.tpe))
1688+
report.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" +
1689+
i"it is not a supertype of the hygienic type $hygienicType", mdef.srcPos)
1690+
//println(i"lifting $rhsType over $termParamss -> $hygienicType = ${tpt.tpe}")
1691+
//println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe })
16761692
case _ =>
16771693
}
16781694
WildcardType

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

+3
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,6 @@ i4176-gadt.scala
8484
i13974a.scala
8585

8686
java-inherited-type1
87+
88+
# avoidance bug
89+
i15174.scala

tests/neg/i12640.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ case class Empty[F[_]]() extends CpsStream[F,Nothing]
1212

1313
def unfold[S,F[_]:CpsMonad,T](s0:S)(f:S => F[Option[(S,T)]]):F[CpsStream[F,T]] =
1414
summon[CpsMonad[F]].flatMap(f(s0)){
15-
case Some(s1,a) => Cons(a, () => unfold(s1,f)) // error (used to crash)
15+
case Some(s1,a) => Cons(a, () => unfold(s1,f)) // error (used to crash) // error
1616
case None => summon[CpsMonad[F]].pure(Empty[F]())
1717
}
File renamed without changes.
File renamed without changes.

tests/pos/i14494.scala

+6
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

+17
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

+14
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

+25
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)