Skip to content

Commit f74a4a9

Browse files
Backport "Ignore selection prototypes when typing type applications" to 3.8.0 (#24532)
Backports #24489 to the 3.8.0-RC2. PR submitted by the release tooling. [skip ci]
2 parents c32f619 + 80f6a9b commit f74a4a9

File tree

7 files changed

+72
-9
lines changed

7 files changed

+72
-9
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,11 @@ object Types extends TypeUtils {
19541954
/** If this is a proto type, WildcardType, otherwise the type itself */
19551955
def dropIfProto: Type = this
19561956

1957+
/** If this is a (possibly applied) selection proto type, ignore the
1958+
* selection part
1959+
*/
1960+
def ignoreSelectionProto(using Context): Type = this
1961+
19571962
/** If this is an AndType, the number of factors, 1 for all other types */
19581963
def andFactorCount: Int = 1
19591964

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
352352
~ "]"
353353
case IgnoredProto(ignored) =>
354354
"?" ~ ("(ignored: " ~ toText(ignored) ~ ")").provided(printDebug)
355-
case tp @ PolyProto(targs, resType) =>
356-
"[applied to [" ~ toTextGlobal(targs, ", ") ~ "] returning " ~ toText(resType)
355+
case tp @ PolyProto(targs, resultType) =>
356+
"[applied to ["
357+
~ toTextGlobal(targs, ", ")
358+
~ "] returning "
359+
~ toText(resultType)
360+
~ "]"
357361
case _ =>
358362
super.toText(tp)
359363
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,7 @@ trait Applications extends Compatibility {
14221422
val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
14231423
record("typedTypeApply")
14241424

1425-
typedExpr(tree.fun, PolyProto(typedArgs, pt)) match {
1425+
typedExpr(tree.fun, PolyProto(typedArgs, pt.ignoreSelectionProto)) match
14261426
case fun: TypeApply if !ctx.isAfterTyper =>
14271427
val function = fun.fun
14281428
val args = (fun.args ++ tree.args).map(_.show).mkString(", ")
@@ -1446,7 +1446,6 @@ trait Applications extends Compatibility {
14461446
}
14471447
if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
14481448
else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
1449-
}
14501449
}
14511450

14521451
/** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray.

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,15 @@ object ProtoTypes {
162162
override def viewExists(tp: Type, pt: Type)(using Context): Boolean = false
163163
}
164164

165-
/** A trait for prototypes that match all types */
166-
trait MatchAlways extends ProtoType {
167-
def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true
165+
/** A trait for prototypes that map to themselves */
166+
trait FixedProto extends ProtoType:
168167
def map(tm: TypeMap)(using Context): ProtoType = this
169168
def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T = x
170169
override def toString: String = getClass.toString
171-
}
170+
171+
/** A trait for prototypes that match all types */
172+
trait MatchAlways extends FixedProto:
173+
def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true
172174

173175
/** A class marking ignored prototypes that can be revealed by `deepenProto` */
174176
abstract case class IgnoredProto(ignored: Type) extends CachedGroundType with MatchAlways:
@@ -179,6 +181,21 @@ object ProtoTypes {
179181
ignored
180182
override def deepenProtoTrans(using Context): Type = ignored.deepenProtoTrans
181183

184+
override def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean =
185+
def takesParams(tp: Type): Boolean = tp match
186+
case tp: PolyType => takesParams(tp.resType)
187+
case MethodType(pnames) => pnames.nonEmpty && !tp.isImplicitMethod
188+
case _ => false
189+
ignored match
190+
case ignored: SelectionProto if ignored.name != nme.apply =>
191+
// Non-implicit methods that take at least one parameter don't match ignored
192+
// selection protos unless the selection is via `apply`. This is because a
193+
// match of a different selection would require an eta expansion _and_ an
194+
// implicit conversion, which is not allowed. So the prototype would not
195+
// match even if implicit conversions were present. Test case: i23773a.scala.
196+
!takesParams(tp1.widen)
197+
case _ => true
198+
182199
/** Did someone look inside via deepenProto? Used for error deagniostics
183200
* to give a more extensive expected type.
184201
*/
@@ -286,6 +303,9 @@ object ProtoTypes {
286303
override def deepenProtoTrans(using Context): SelectionProto =
287304
derivedSelectionProto(name, memberProto.deepenProtoTrans, compat, nameSpan)
288305

306+
override def ignoreSelectionProto(using Context): IgnoredProto =
307+
IgnoredProto(this)
308+
289309
override def computeHash(bs: Hashable.Binders): Int = {
290310
val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0)
291311
addDelta(doHash(bs, name, memberProto), delta)
@@ -620,6 +640,9 @@ object ProtoTypes {
620640
override def deepenProtoTrans(using Context): FunProto =
621641
derivedFunProto(args, resultType.deepenProtoTrans, constrainResultDeep = true)
622642

643+
override def ignoreSelectionProto(using Context): FunProto =
644+
derivedFunProto(args, resultType.ignoreSelectionProto)
645+
623646
override def withContext(newCtx: Context): ProtoType =
624647
if newCtx `eq` protoCtx then this
625648
else new FunProto(args, resType)(typer, applyKind, state)(using newCtx)
@@ -734,6 +757,9 @@ object ProtoTypes {
734757

735758
override def deepenProtoTrans(using Context): PolyProto =
736759
derivedPolyProto(targs, resultType.deepenProtoTrans)
760+
761+
override def ignoreSelectionProto(using Context): PolyProto =
762+
derivedPolyProto(targs, resultType.ignoreSelectionProto)
737763
}
738764

739765
/** A prototype for expressions [] that are known to be functions:

tests/run/overloading-specifity-2.scala renamed to tests/neg/overloading-specifity-2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ object Test extends App {
2323
def foo[T]: Show[T] = new Show[T](2)
2424
}
2525

26-
assert(a.foo[Int].i == 1) // error: no implicit argument of type Test.Context was found for parameter ctx
26+
assert(a.foo[Int].i == 1) // error: no implicit argument of type Test.Context was found for parameter ctx, was OK
2727
}

tests/pos/i23773.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait Foo[T]
2+
3+
def foo[A]: Int = ???
4+
def foo[A: Foo]: Int = ???
5+
6+
extension (x: Int)
7+
def succ: Int = x + 1
8+
9+
val a = foo[Int]
10+
val b = foo[Int].succ // error

tests/pos/i23773a.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
trait NumericDate
2+
trait JWSDecoded[H]
3+
4+
trait StandardHeaderWrite[H]:
5+
def setAlgorithm(header: H, algorithm: Algorithm): H
6+
7+
object StandardHeaderWrite:
8+
def apply[H](using sh: StandardHeaderWrite[H]): StandardHeaderWrite[H] = ???
9+
// unused - required to reproduce
10+
def apply[H](setAlg: (H, Algorithm) => H): StandardHeaderWrite[H] = ???
11+
12+
final case class JWK(algorithm: Option[Algorithm])
13+
sealed trait Algorithm
14+
15+
def Test[F[_], H](key: JWK, header: H)(using StandardHeaderWrite[H]) = {
16+
key.algorithm
17+
.map(StandardHeaderWrite[H].setAlgorithm(header, _))
18+
.getOrElse(header)
19+
}

0 commit comments

Comments
 (0)