Skip to content

Commit f1faab6

Browse files
committed
safe_&: handle higher-kinded arguments like regular &
Unfortunately, i9346.scala has to be put in pending because it still crashes (due to a cycle involving a LazyRef) after this fix, and because `safe_&` is only called from `recoverable_&` when there is some sort of cycle in the first place, I haven't been able to make another testcase that exercises this codepath. It would be good if we could figure out how to get i9346.scala to compile (see also #9346 for discussions). It is is a minimization of a pattern heavily used in akka-stream, similar to the use of the `CC` type parameter in the scala-library collections but using a type member instead. Unfortunately, it seems that Dotty is not really prepared to handle F-bounds in type members currently. I was able to get the testcase as well as akka-stream to compile by tweaking `findMember` to not compute an intersection when the refinement is an alias: ```diff --- compiler/src/dotty/tools/dotc/core/Types.scala +++ compiler/src/dotty/tools/dotc/core/Types.scala @@ -671,7 +671,8 @@ object Types { val rinfo = tp.refinedInfo if (name.isTypeName && !pinfo.isInstanceOf[ClassInfo]) { // simplified case that runs more efficiently val jointInfo = - if (ctx.base.pendingMemberSearches.contains(name)) pinfo safe_& rinfo + if (rinfo.isInstanceOf[TypeAlias]) rinfo + else if (ctx.base.pendingMemberSearches.contains(name)) pinfo safe_& rinfo else pinfo recoverable_& rinfo pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } ``` This seems to work, but to be sound it means that we need to check for invalid bounds in PostTyper (see tests/neg/i5556.scala for an example where this matters), like we do for type parameters in `checkAppliedType`.
1 parent dfcc0b1 commit f1faab6

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2078,7 +2078,7 @@ class TypeComparer(using val comparerCtx: Context) extends ConstraintHandling wi
20782078
*
20792079
* [X1, ..., Xn] -> op(tp1[X1, ..., Xn], tp2[X1, ..., Xn])
20802080
*/
2081-
private def liftIfHK(tp1: Type, tp2: Type,
2081+
def liftIfHK(tp1: Type, tp2: Type,
20822082
op: (Type, Type) => Type, original: (Type, Type) => Type, combineVariance: (Variance, Variance) => Variance) = {
20832083
val tparams1 = tp1.typeParams
20842084
val tparams2 = tp2.typeParams

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,11 @@ object Types {
10181018
*/
10191019
def safe_& (that: Type)(using Context): Type = (this, that) match {
10201020
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) =>
1021-
TypeBounds(OrType(lo1.stripLazyRef, lo2.stripLazyRef), AndType(hi1.stripLazyRef, hi2.stripLazyRef))
1022-
case _ => this & that
1021+
TypeBounds(
1022+
OrType.makeHk(lo1.stripLazyRef, lo2.stripLazyRef),
1023+
AndType.makeHk(hi1.stripLazyRef, hi2.stripLazyRef))
1024+
case _ =>
1025+
this & that
10231026
}
10241027

10251028
/** `this & that`, but handle CyclicReferences by falling back to `safe_&`.
@@ -2934,6 +2937,10 @@ object Types {
29342937
tp2
29352938
else
29362939
if (checkValid) apply(tp1, tp2) else unchecked(tp1, tp2)
2940+
2941+
/** Like `make`, but also supports higher-kinded types as argument */
2942+
def makeHk(tp1: Type, tp2: Type)(using Context): Type =
2943+
ctx.typeComparer.liftIfHK(tp1, tp2, AndType.make(_, _, checkValid = false), makeHk, _ | _)
29372944
}
29382945

29392946
abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType {
@@ -3020,6 +3027,10 @@ object Types {
30203027
def make(tp1: Type, tp2: Type)(using Context): Type =
30213028
if (tp1 eq tp2) tp1
30223029
else apply(tp1, tp2)
3030+
3031+
/** Like `make`, but also supports higher-kinded types as argument */
3032+
def makeHk(tp1: Type, tp2: Type)(using Context): Type =
3033+
ctx.typeComparer.liftIfHK(tp1, tp2, OrType(_, _), makeHk, _ & _)
30233034
}
30243035

30253036
/** An extractor object to pattern match against a nullable union.

tests/pending/pos/i9346.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait Foo[+A] {
2+
type Repr[+O] <: Foo[O] {
3+
type Repr[+OO] = Foo.this.Repr[OO]
4+
}
5+
6+
def foo: Repr[A]
7+
8+
def bar: Repr[A] = this.foo.foo
9+
}

0 commit comments

Comments
 (0)