diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ca9eda247cc0..5d630cad6368 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..878126b6331f 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -162,13 +162,15 @@ object ProtoTypes { override def viewExists(tp: Type, pt: Type)(using Context): Boolean = false } - /** A trait for prototypes that match all types */ - trait MatchAlways extends ProtoType { - def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true + /** A trait for prototypes that map to themselves */ + trait FixedProto extends ProtoType: def map(tm: TypeMap)(using Context): ProtoType = this def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T = x override def toString: String = getClass.toString - } + + /** A trait for prototypes that match all types */ + trait MatchAlways extends FixedProto: + def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true /** A class marking ignored prototypes that can be revealed by `deepenProto` */ abstract case class IgnoredProto(ignored: Type) extends CachedGroundType with MatchAlways: @@ -179,6 +181,21 @@ object ProtoTypes { ignored override def deepenProtoTrans(using Context): Type = ignored.deepenProtoTrans + override def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = + def takesParams(tp: Type): Boolean = tp match + case tp: PolyType => takesParams(tp.resType) + case MethodType(pnames) => pnames.nonEmpty && !tp.isImplicitMethod + case _ => false + ignored match + case ignored: SelectionProto if ignored.name != nme.apply => + // Non-implicit methods that take at least one parameter don't match ignored + // selection protos unless the selection is via `apply`. This is because a + // match of a different selection would require an eta expansion _and_ an + // implicit conversion, which is not allowed. So the prototype would not + // match even if implicit conversions were present. Test case: i23773a.scala. + !takesParams(tp1.widen) + case _ => true + /** Did someone look inside via deepenProto? Used for error deagniostics * to give a more extensive expected type. */ @@ -286,6 +303,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 +640,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 +757,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 diff --git a/tests/pos/i23773a.scala b/tests/pos/i23773a.scala new file mode 100644 index 000000000000..eaf0d98b2956 --- /dev/null +++ b/tests/pos/i23773a.scala @@ -0,0 +1,19 @@ +trait NumericDate +trait JWSDecoded[H] + +trait StandardHeaderWrite[H]: + def setAlgorithm(header: H, algorithm: Algorithm): H + +object StandardHeaderWrite: + def apply[H](using sh: StandardHeaderWrite[H]): StandardHeaderWrite[H] = ??? + // unused - required to reproduce + def apply[H](setAlg: (H, Algorithm) => H): StandardHeaderWrite[H] = ??? + +final case class JWK(algorithm: Option[Algorithm]) +sealed trait Algorithm + +def Test[F[_], H](key: JWK, header: H)(using StandardHeaderWrite[H]) = { + key.algorithm + .map(StandardHeaderWrite[H].setAlgorithm(header, _)) + .getOrElse(header) +}