From 60444cba49062db6602b5c77c81014163c7088d7 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 20 Nov 2025 13:14:56 +0100 Subject: [PATCH] Ignore selection prototypes when typing type applications Ignore selection prototypes at first when typing type applications. If we need them later for overloading disambiguation, reveal the ignored type. The reason for doing this is that a selection might come from an extension method, and in this case we should not require the selected name as a member of the result. This change breaks one test (overloading-specifity-2.scala) that explicitly tested that we don't consult implicit arguments for disambiguation since the expected type was a selection that already determined the outcome. This is logic no longer holds. We have to see whether this change breaks any code in practice. Fixes #23773 --- compiler/src/dotty/tools/dotc/core/Types.scala | 5 +++++ .../src/dotty/tools/dotc/printing/RefinedPrinter.scala | 8 ++++++-- compiler/src/dotty/tools/dotc/typer/Applications.scala | 3 +-- compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala | 9 +++++++++ tests/{run => neg}/overloading-specifity-2.scala | 2 +- tests/pos/i23773.scala | 10 ++++++++++ 6 files changed, 32 insertions(+), 5 deletions(-) rename tests/{run => neg}/overloading-specifity-2.scala (94%) create mode 100644 tests/pos/i23773.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6bf19c5a27a1..43a14e6d213e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1956,6 +1956,11 @@ object Types extends TypeUtils { /** If this is a proto type, WildcardType, otherwise the type itself */ def dropIfProto: Type = this + /** If this is a (possibly applied) selection proto type, ignore the + * selection part + */ + def ignoreSelectionProto(using Context): Type = this + /** If this is an AndType, the number of factors, 1 for all other types */ def andFactorCount: Int = 1 diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9f4cc2086dc1..37222fba3ee3 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -354,8 +354,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ~ "]" case IgnoredProto(ignored) => "?" ~ ("(ignored: " ~ toText(ignored) ~ ")").provided(printDebug) - case tp @ PolyProto(targs, resType) => - "[applied to [" ~ toTextGlobal(targs, ", ") ~ "] returning " ~ toText(resType) + case tp @ PolyProto(targs, resultType) => + "[applied to [" + ~ toTextGlobal(targs, ", ") + ~ "] returning " + ~ toText(resultType) + ~ "]" case _ => super.toText(tp) } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 5944a586d1cf..b8dbfc3786fe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1422,7 +1422,7 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") - typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { + typedExpr(tree.fun, PolyProto(typedArgs, pt.ignoreSelectionProto)) match case fun: TypeApply if !ctx.isAfterTyper => val function = fun.fun val args = (fun.args ++ tree.args).map(_.show).mkString(", ") @@ -1446,7 +1446,6 @@ trait Applications extends Compatibility { } if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply() else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) - } } /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray. diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 4fc092d16007..140d4beed4a2 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -286,6 +286,9 @@ object ProtoTypes { override def deepenProtoTrans(using Context): SelectionProto = derivedSelectionProto(name, memberProto.deepenProtoTrans, compat, nameSpan) + override def ignoreSelectionProto(using Context): IgnoredProto = + IgnoredProto(this) + override def computeHash(bs: Hashable.Binders): Int = { val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0) addDelta(doHash(bs, name, memberProto), delta) @@ -620,6 +623,9 @@ object ProtoTypes { override def deepenProtoTrans(using Context): FunProto = derivedFunProto(args, resultType.deepenProtoTrans, constrainResultDeep = true) + override def ignoreSelectionProto(using Context): FunProto = + derivedFunProto(args, resultType.ignoreSelectionProto) + override def withContext(newCtx: Context): ProtoType = if newCtx `eq` protoCtx then this else new FunProto(args, resType)(typer, applyKind, state)(using newCtx) @@ -734,6 +740,9 @@ object ProtoTypes { override def deepenProtoTrans(using Context): PolyProto = derivedPolyProto(targs, resultType.deepenProtoTrans) + + override def ignoreSelectionProto(using Context): PolyProto = + derivedPolyProto(targs, resultType.ignoreSelectionProto) } /** A prototype for expressions [] that are known to be functions: diff --git a/tests/run/overloading-specifity-2.scala b/tests/neg/overloading-specifity-2.scala similarity index 94% rename from tests/run/overloading-specifity-2.scala rename to tests/neg/overloading-specifity-2.scala index 9b04d82bfa66..c6ae9799c06e 100644 --- a/tests/run/overloading-specifity-2.scala +++ b/tests/neg/overloading-specifity-2.scala @@ -23,5 +23,5 @@ object Test extends App { def foo[T]: Show[T] = new Show[T](2) } - assert(a.foo[Int].i == 1) // error: no implicit argument of type Test.Context was found for parameter ctx + assert(a.foo[Int].i == 1) // error: no implicit argument of type Test.Context was found for parameter ctx, was OK } \ No newline at end of file diff --git a/tests/pos/i23773.scala b/tests/pos/i23773.scala new file mode 100644 index 000000000000..7f3ce30ecc13 --- /dev/null +++ b/tests/pos/i23773.scala @@ -0,0 +1,10 @@ +trait Foo[T] + +def foo[A]: Int = ??? +def foo[A: Foo]: Int = ??? + +extension (x: Int) + def succ: Int = x + 1 + +val a = foo[Int] +val b = foo[Int].succ // error \ No newline at end of file