Skip to content

Commit 4bd275b

Browse files
authored
Merge pull request #15036 from dotty-staging/relax-avoidance
Relax avoidance checks more for match type reduction
2 parents 0e83a80 + f7e141d commit 4bd275b

File tree

4 files changed

+57
-19
lines changed

4 files changed

+57
-19
lines changed

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,28 @@ trait ConstraintHandling {
8181
assert(homogenizeArgs == false)
8282
assert(comparedTypeLambdas == Set.empty)
8383

84-
def nestingLevel(param: TypeParamRef) = constraint.typeVarOfParam(param) match
84+
def nestingLevel(param: TypeParamRef)(using Context) = constraint.typeVarOfParam(param) match
8585
case tv: TypeVar => tv.nestingLevel
86-
case _ => Int.MaxValue
86+
case _ =>
87+
// This should only happen when reducing match types (in
88+
// TrackingTypeComparer#matchCases) or in uncommitable TyperStates (as
89+
// asserted in ProtoTypes.constrained) and is special-cased in `levelOK`
90+
// below.
91+
Int.MaxValue
92+
93+
/** Is `level` <= `maxLevel` or legal in the current context? */
94+
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.
8798

8899
/** If `param` is nested deeper than `maxLevel`, try to instantiate it to a
89100
* fresh type variable of level `maxLevel` and return the new variable.
90101
* If this isn't possible, throw a TypeError.
91102
*/
92103
def atLevel(maxLevel: Int, param: TypeParamRef)(using Context): TypeParamRef =
93-
if nestingLevel(param) <= maxLevel then return param
104+
if levelOK(nestingLevel(param), maxLevel) then
105+
return param
94106
LevelAvoidMap(0, maxLevel)(param) match
95107
case freshVar: TypeVar => freshVar.origin
96108
case _ => throw new TypeError(
@@ -129,18 +141,12 @@ trait ConstraintHandling {
129141

130142
/** An approximating map that prevents types nested deeper than maxLevel as
131143
* well as WildcardTypes from leaking into the constraint.
132-
* Note that level-checking is turned off after typer and in uncommitable
133-
* TyperState since these leaks should be safe.
134144
*/
135145
class LevelAvoidMap(topLevelVariance: Int, maxLevel: Int)(using Context) extends TypeOps.AvoidMap:
136146
variance = topLevelVariance
137147

138-
/** Are we allowed to refer to types of the given `level`? */
139-
private def levelOK(level: Int): Boolean =
140-
level <= maxLevel || ctx.isAfterTyper || !ctx.typerState.isCommittable
141-
142148
def toAvoid(tp: NamedType): Boolean =
143-
tp.prefix == NoPrefix && !tp.symbol.isStatic && !levelOK(tp.symbol.nestingLevel)
149+
tp.prefix == NoPrefix && !tp.symbol.isStatic && !levelOK(tp.symbol.nestingLevel, maxLevel)
144150

145151
/** Return a (possibly fresh) type variable of a level no greater than `maxLevel` which is:
146152
* - lower-bounded by `tp` if variance >= 0
@@ -185,7 +191,7 @@ trait ConstraintHandling {
185191
end legalVar
186192

187193
override def apply(tp: Type): Type = tp match
188-
case tp: TypeVar if !tp.isInstantiated && !levelOK(tp.nestingLevel) =>
194+
case tp: TypeVar if !tp.isInstantiated && !levelOK(tp.nestingLevel, maxLevel) =>
189195
legalVar(tp)
190196
// TypeParamRef can occur in tl bounds
191197
case tp: TypeParamRef =>
@@ -431,7 +437,6 @@ trait ConstraintHandling {
431437
final def approximation(param: TypeParamRef, fromBelow: Boolean)(using Context): Type =
432438
constraint.entry(param) match
433439
case entry: TypeBounds =>
434-
val maxLevel = nestingLevel(param)
435440
val useLowerBound = fromBelow || param.occursIn(entry.hi)
436441
val inst = if useLowerBound then fullLowerBound(param) else fullUpperBound(param)
437442
typr.println(s"approx ${param.show}, from below = $fromBelow, inst = ${inst.show}")

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -637,13 +637,15 @@ trait Inferencing { this: Typer =>
637637
else if v.intValue != 0 then
638638
typr.println(i"interpolate $tvar in $state in $tree: $tp, fromBelow = ${v.intValue == 1}, $constraint")
639639
toInstantiate += ((tvar, v.intValue == 1))
640-
else if tvar.nestingLevel > ctx.nestingLevel then
641-
// Invariant: a type variable of level N can only appear
642-
// in the type of a tree whose enclosing scope is level <= N.
643-
typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint")
644-
comparing(_.atLevel(ctx.nestingLevel, tvar.origin))
645-
else
646-
typr.println(i"no interpolation for nonvariant $tvar in $state")
640+
else comparing(cmp =>
641+
if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then
642+
// Invariant: The type of a tree whose enclosing scope is level
643+
// N only contains type variables of level <= N.
644+
typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint")
645+
cmp.atLevel(ctx.nestingLevel, tvar.origin)
646+
else
647+
typr.println(i"no interpolation for nonvariant $tvar in $state")
648+
)
647649

648650
/** Instantiate all type variables in `buf` in the indicated directions.
649651
* If a type variable A is instantiated from below, and there is another

tests/pos/i14921/A_1.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.compiletime.ops.int.*
2+
3+
final class Label (val getLabel: String)
4+
5+
trait ShapelessPolyfill {
6+
7+
type Represented[R] = R match {
8+
case IndexedSeq[a] => a
9+
}
10+
11+
type TupleSized[R, A, N <: Int] <: Tuple = N match {
12+
case 0 => EmptyTuple
13+
case S[n] => A *: TupleSized[R, A, n]
14+
}
15+
16+
extension [R, A, N <: Int] (s: TupleSized[R, A, N]) {
17+
def unsized: IndexedSeq[A] = s.productIterator.toIndexedSeq.asInstanceOf[IndexedSeq[A]]
18+
}
19+
20+
type Nat = Int
21+
22+
type Sized[Repr, L <: Nat] = TupleSized[Repr, Represented[Repr], L]
23+
24+
object Sized {
25+
def apply[A](a1: A): Sized[IndexedSeq[A], 1] = Tuple1(a1)
26+
}
27+
}
28+
object poly extends ShapelessPolyfill

tests/pos/i14921/B_2.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import poly.*
2+
3+
def failing: Tuple1[Label] = Sized(new Label("foo"))

0 commit comments

Comments
 (0)