Skip to content

Commit bdc8092

Browse files
Merge pull request #9979 from dotty-staging/fix-#9958
Fix #9958: Avoid eta contractions when printing
2 parents 68a7c03 + 1750957 commit bdc8092

File tree

4 files changed

+64
-18
lines changed

4 files changed

+64
-18
lines changed

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,46 @@ object TypeApplications {
2626

2727
/** Extractor for
2828
*
29-
* [v1 X1: B1, ..., vn Xn: Bn] -> C[X1, ..., Xn]
29+
* [X1: B1, ..., Xn: Bn] -> C[X1, ..., Xn]
3030
*
31-
* where v1, ..., vn and B1, ..., Bn are the variances and bounds of the type parameters
32-
* of the class C.
31+
* where B1, ..., Bn are bounds of the type parameters of the class C.
3332
*
3433
* @param tycon C
3534
*/
36-
object EtaExpansion {
37-
def apply(tycon: Type)(using Context): Type = {
35+
object EtaExpansion:
36+
37+
def apply(tycon: Type)(using Context): Type =
3838
assert(tycon.typeParams.nonEmpty, tycon)
3939
tycon.EtaExpand(tycon.typeParamSymbols)
40-
}
4140

42-
def unapply(tp: Type)(using Context): Option[Type] = tp match {
41+
/** Test that the parameter bounds in a hk type lambda `[X1,...,Xn] => C[X1, ..., Xn]`
42+
* contain the bounds of the type parameters of `C`. This is necessary to be able to
43+
* contract the hk lambda to `C`.
44+
*/
45+
private def weakerBounds(tp: HKTypeLambda, tparams: List[ParamInfo])(using Context): Boolean =
46+
val onlyEmptyBounds = tp.typeParams.forall(_.paramInfo == TypeBounds.empty)
47+
onlyEmptyBounds
48+
// Note: this pre-test helps efficiency. It is also necessary to workaround #9965 since in some cases
49+
// tparams is empty. This can happen when we change the owners of inlined local
50+
// classes in mapSymbols. See pos/reference/delegates.scala for an example.
51+
// In this case, we can still return true if we know that the hk lambda bounds
52+
// are empty anyway.
53+
|| {
54+
val paramRefs = tparams.map(_.paramRef)
55+
tp.typeParams.corresponds(tparams) { (param1, param2) =>
56+
param2.paramInfo <:< param1.paramInfo.substParams(tp, paramRefs)
57+
}
58+
}
59+
60+
def unapply(tp: Type)(using Context): Option[Type] = tp match
4361
case tp @ HKTypeLambda(tparams, AppliedType(fn: Type, args))
44-
if args.lazyZip(tparams).forall((arg, tparam) => arg == tparam.paramRef) => Some(fn)
62+
if fn.typeSymbol.isClass
63+
&& tparams.hasSameLengthAs(args)
64+
&& args.lazyZip(tparams).forall((arg, tparam) => arg == tparam.paramRef)
65+
&& weakerBounds(tp, fn.typeParams) => Some(fn)
4566
case _ => None
46-
}
47-
}
67+
68+
end EtaExpansion
4869

4970
/** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK
5071
*/

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,26 +192,33 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
192192
}
193193
}
194194

195-
homogenize(tp) match {
195+
def appliedText(tp: Type): Text = tp match
196196
case tp @ AppliedType(tycon, args) =>
197197
val cls = tycon.typeSymbol
198-
if (tycon.isRepeatedParam) toTextLocal(args.head) ~ "*"
199-
else if (defn.isFunctionClass(cls)) toTextFunction(args, cls.name.isContextFunction, cls.name.isErasedFunction)
200-
else if (tp.tupleArity >= 2 && !printDebug) toTextTuple(tp.tupleElementTypes)
201-
else if (isInfixType(tp)) {
198+
if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*"
199+
else if defn.isFunctionClass(cls) then toTextFunction(args, cls.name.isContextFunction, cls.name.isErasedFunction)
200+
else if tp.tupleArity >= 2 && !printDebug then toTextTuple(tp.tupleElementTypes)
201+
else if isInfixType(tp) then
202202
val l :: r :: Nil = args
203203
val opName = tyconName(tycon)
204204
toTextInfixType(tyconName(tycon), l, r) { simpleNameString(tycon.typeSymbol) }
205-
}
206-
else super.toText(tp)
205+
else Str("")
206+
case _ =>
207+
Str("")
207208

209+
homogenize(tp) match {
210+
case tp: AppliedType =>
211+
val refined = appliedText(tp)
212+
if refined.isEmpty then super.toText(tp) else refined
208213
// Since RefinedPrinter, unlike PlainPrinter, can output right-associative type-operators, we must override handling
209214
// of AndType and OrType to account for associativity
210215
case AndType(tp1, tp2) =>
211216
toTextInfixType(tpnme.raw.AMP, tp1, tp2) { toText(tpnme.raw.AMP) }
212217
case OrType(tp1, tp2) =>
213218
toTextInfixType(tpnme.raw.BAR, tp1, tp2) { toText(tpnme.raw.BAR) }
214-
case EtaExpansion(tycon) if !printDebug =>
219+
case tp @ EtaExpansion(tycon)
220+
if !printDebug && appliedText(tp.asInstanceOf[HKLambda].resType).isEmpty =>
221+
// don't eta contract if the application would be printed specially
215222
toText(tycon)
216223
case tp: RefinedType if defn.isFunctionType(tp) && !printDebug =>
217224
toTextDependentFunction(tp.refinedInfo.asInstanceOf[MethodType])

tests/neg/i9958.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Error: tests/neg/i9958.scala:1:30 -----------------------------------------------------------------------------------
2+
1 |val x = summon[[X] =>> (X, X)] // error
3+
| ^
4+
| no implicit argument of type [X] =>> (X, X) was found for parameter x of method summon in object DottyPredef
5+
-- [E007] Type Mismatch Error: tests/neg/i9958.scala:8:10 --------------------------------------------------------------
6+
8 |def b = f(a) // error
7+
| ^
8+
| Found: G[[A <: Int] =>> List[A]]
9+
| Required: G[List]

tests/neg/i9958.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
val x = summon[[X] =>> (X, X)] // error
2+
// #9971:
3+
trait G[F[_]]
4+
5+
def f(x: G[List]) = ???
6+
7+
def a: G[[A <: Int] =>> List[A]] = ???
8+
def b = f(a) // error
9+

0 commit comments

Comments
 (0)