diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c2c29f60ed92..fc64c342fbea 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -574,7 +574,7 @@ object desugar { val nu = vparamss.foldLeft(makeNew(classTypeRef)) { (nu, vparams) => val app = Apply(nu, vparams.map(refOfDef)) vparams match { - case vparam :: _ if vparam.mods.is(Given) => app.setGivenApply() + case vparam :: _ if vparam.mods.is(Given) => app.setUsingApply() case _ => app } } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 4fbae3ab09c4..feb3e9280b33 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -439,8 +439,8 @@ object Trees { extends GenericApply[T] { type ThisTree[-T >: Untyped] = Apply[T] - def isGivenApply = hasAttachment(untpd.ApplyGiven) - def setGivenApply() = { putAttachment(untpd.ApplyGiven, ()); this } + def isUsingApply = hasAttachment(untpd.ApplyGiven) + def setUsingApply() = { putAttachment(untpd.ApplyGiven, ()); this } } /** fun[args] */ diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 1f23faa69fd5..03b9ac1bdca4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3311,7 +3311,6 @@ object Types { companion.eq(ContextualMethodType) || companion.eq(ErasedContextualMethodType) - def computeSignature(implicit ctx: Context): Signature = { val params = if (isErasedMethod) Nil else paramInfos resultSignature.prependTermParams(params, isJavaMethod) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 08c5aa87b10c..a1cd62a0792e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2293,7 +2293,7 @@ object Parsers { def mkApply(fn: Tree, args: (List[Tree], Boolean)): Tree = val res = Apply(fn, args._1) - if args._2 then res.setGivenApply() + if args._2 then res.setUsingApply() res val argumentExpr: () => Tree = () => exprInParens() match { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5d0160d168f9..db6825f72478 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -390,7 +390,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else toTextLocal(fun) ~ "(" - ~ Str("using ").provided(app.isGivenApply && !homogenizedView) + ~ Str("using ").provided(app.isUsingApply && !homogenizedView) ~ toTextGlobal(args, ", ") ~ ")" case tree: TypeApply => diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index d325efe9e52a..9fdb447b26b9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -545,7 +545,7 @@ object Erasure { val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else typedExpr(fun, FunProto(args, pt)(this, isGivenApply = false)) match { + else typedExpr(fun, FunProto(args, pt)(this, isUsingApply = false)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 case fun1 => diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 92d19d12a78b..c4e54961585c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -833,7 +833,7 @@ trait Applications extends Compatibility { def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { def realApply(implicit ctx: Context): Tree = { - val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isGivenApply)(argCtx(tree)) + val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isUsingApply)(argCtx(tree)) record("typedApply") val fun1 = typedFunPart(tree.fun, originalProto) @@ -1299,6 +1299,16 @@ trait Applications extends Compatibility { } } + /** Drop any implicit parameter section */ + def stripImplicit(tp: Type)(using Context): Type = tp match { + case mt: MethodType if mt.isImplicitMethod => + stripImplicit(resultTypeApprox(mt)) + case pt: PolyType => + pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType)) + case _ => + tp + } + /** Compare owner inheritance level. * @param sym1 The first owner * @param sym2 The second owner @@ -1466,16 +1476,6 @@ trait Applications extends Compatibility { else tp } - /** Drop any implicit parameter section */ - def stripImplicit(tp: Type): Type = tp match { - case mt: MethodType if mt.isImplicitMethod => - stripImplicit(resultTypeApprox(mt)) - case pt: PolyType => - pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType)) - case _ => - tp - } - def compareWithTypes(tp1: Type, tp2: Type) = { val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner) def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2) @@ -1707,6 +1707,22 @@ trait Applications extends Compatibility { case _ => arg end normArg + /** Resolve overloading by mapping to a different problem where each alternative's + * type is mapped with `f`, alternatives with non-existing types are dropped, and the + * expected type is `pt`. Map the results back to the original alternatives. + */ + def resolveMapped(alts: List[TermRef], f: TermRef => Type, pt: Type): List[TermRef] = + val reverseMapping = alts.flatMap { alt => + val t = f(alt) + if t.exists then + Some((TermRef(NoPrefix, alt.symbol.asTerm.copy(info = t)), alt)) + else + None + } + val mapped = reverseMapping.map(_._1) + overload.println(i"resolve mapped: $mapped") + resolveOverloaded(mapped, pt, targs).map(reverseMapping.toMap) + val candidates = pt match { case pt @ FunProto(args, resultType) => val numArgs = args.length @@ -1747,6 +1763,15 @@ trait Applications extends Compatibility { alts2 } + if pt.isUsingApply then + val alts0 = alts.filterConserve { alt => + val mt = alt.widen.stripPoly + mt.isImplicitMethod || mt.isContextualMethod + } + if alts0 ne alts then return resolveOverloaded(alts0, pt, targs) + else if alts.exists(_.widen.stripPoly.isContextualMethod) then + return resolveMapped(alts, alt => stripImplicit(alt.widen), pt) + val alts1 = narrowBySize(alts) //ctx.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %") if isDetermined(alts1) then alts1 @@ -1783,32 +1808,20 @@ trait Applications extends Compatibility { else compat } - /** For each candidate `C`, a proxy termref paired with `C`. - * The proxy termref has as symbol a copy of the original candidate symbol, - * with an info that strips the first value parameter list away. - * @param argTypes The types of the arguments of the FunProto `pt`. + /** The type of alternative `alt` after instantiating its first parameter + * clause with `argTypes`. */ - def advanceCandidates(argTypes: List[Type]): List[(TermRef, TermRef)] = { - def strippedType(tp: Type): Type = tp match { + def skipParamClause(argTypes: List[Type])(alt: TermRef): Type = + def skip(tp: Type): Type = tp match { case tp: PolyType => - val rt = strippedType(tp.resultType) + val rt = skip(tp.resultType) if (rt.exists) tp.derivedLambdaType(resType = rt) else rt case tp: MethodType => tp.instantiate(argTypes) case _ => NoType } - def cloneCandidate(cand: TermRef): List[(TermRef, TermRef)] = { - val strippedInfo = strippedType(cand.widen) - if (strippedInfo.exists) { - val sym = cand.symbol.asTerm.copy(info = strippedInfo) - (TermRef(NoPrefix, sym), cand) :: Nil - } - else Nil - } - overload.println(i"look at more params: ${candidates.head.symbol}: ${candidates.map(_.widen)}%, % with $pt, [$targs%, %]") - candidates.flatMap(cloneCandidate) - } + skip(alt.widen) def resultIsMethod(tp: Type): Boolean = tp.widen.stripPoly match case tp: MethodType => tp.resultType.isInstanceOf[MethodType] @@ -1821,9 +1834,7 @@ trait Applications extends Compatibility { deepPt match case pt @ FunProto(_, resType: FunProto) => // try to narrow further with snd argument list - val advanced = advanceCandidates(pt.typedArgs().tpes) - resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped - .map(advanced.toMap) // map surviving result(s) back to original candidates + resolveMapped(candidates, skipParamClause(pt.typedArgs().tpes), resType) case _ => // prefer alternatives that need no eta expansion val noCurried = alts.filter(!resultIsMethod(_)) diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 49f82e74f763..ec2cc685fb4f 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -233,7 +233,7 @@ object EtaExpansion extends LiftImpure { if (mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam) ids = ids.init :+ repeated(ids.last) val app = Apply(lifted, ids) - if (mt.isContextualMethod) app.setGivenApply() + if (mt.isContextualMethod) app.setUsingApply() val body = if (isLastApplication) app else PostfixOp(app, Ident(nme.WILDCARD)) val fn = if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given)) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 1beb93d07a71..37b4cce5ff5a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -228,7 +228,7 @@ object ProtoTypes { trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto trait FunOrPolyProto extends ProtoType { // common trait of PolyProto and FunProto - def isGivenApply: Boolean = false + def isUsingApply: Boolean = false } class FunProtoState { @@ -251,7 +251,7 @@ object ProtoTypes { * [](args): resultType */ case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer, - override val isGivenApply: Boolean, state: FunProtoState = new FunProtoState)(implicit val ctx: Context) + override val isUsingApply: Boolean, state: FunProtoState = new FunProtoState)(implicit val ctx: Context) extends UncachedGroundType with ApplyingProto with FunOrPolyProto { override def resultType(implicit ctx: Context): Type = resType @@ -265,7 +265,7 @@ object ProtoTypes { def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer): FunProto = if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this - else new FunProto(args, resultType)(typer, isGivenApply) + else new FunProto(args, resultType)(typer, isUsingApply) /** @return True if all arguments have types. */ @@ -355,7 +355,7 @@ object ProtoTypes { case pt: FunProto => pt case _ => - state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer, isGivenApply) + state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer, isUsingApply) tupled } @@ -390,14 +390,14 @@ object ProtoTypes { override def withContext(newCtx: Context): ProtoType = if (newCtx `eq` ctx) this - else new FunProto(args, resType)(typer, isGivenApply, state)(newCtx) + else new FunProto(args, resType)(typer, isUsingApply, state)(newCtx) } /** A prototype for expressions that appear in function position * * [](args): resultType, where args are known to be typed */ - class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isGivenApply: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isGivenApply)(ctx) { + class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer, isUsingApply: Boolean)(implicit ctx: Context) extends FunProto(args, resultType)(typer, isUsingApply)(ctx) { override def typedArgs(norm: (untpd.Tree, Int) => untpd.Tree)(implicit ctx: Context): List[tpd.Tree] = args override def withContext(ctx: Context): FunProtoTyped = this } @@ -444,7 +444,7 @@ object ProtoTypes { } class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source))(ctx) :: Nil, WildcardType)(typer, isGivenApply = false) + untpd.TypedSplice(dummyTreeOfType(argType)(ctx.source))(ctx) :: Nil, WildcardType)(typer, isUsingApply = false) /** A prototype for expressions [] that are type-parameterized: * diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c245a8bf98d0..f6daca80b20c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1015,7 +1015,7 @@ class Typer extends Namer case _ => given nestedCtx as Context = ctx.fresh.setNewTyperState() val protoArgs = args map (_ withType WildcardType) - val callProto = FunProto(protoArgs, WildcardType)(this, app.isGivenApply) + val callProto = FunProto(protoArgs, WildcardType)(this, app.isUsingApply) val expr1 = typedExpr(expr, callProto) if nestedCtx.reporter.hasErrors then NoType else @@ -2626,7 +2626,7 @@ class Typer extends Namer errorTree(tree, NoMatchingOverload(altDenots, pt)(err)) def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil pt match { - case pt: FunOrPolyProto if !pt.isGivenApply => + case pt: FunOrPolyProto if !pt.isUsingApply => // insert apply or convert qualifier, but only for a regular application tryInsertApplyOrImplicit(tree, pt, locked)(noMatches) case _ => @@ -2777,7 +2777,7 @@ class Typer extends Namer } tryEither { val app = cpy.Apply(tree)(untpd.TypedSplice(tree), namedArgs) - if (wtp.isContextualMethod) app.setGivenApply() + if (wtp.isContextualMethod) app.setUsingApply() typr.println(i"try with default implicit args $app") typed(app, pt, locked) } { (_, _) => @@ -2794,7 +2794,7 @@ class Typer extends Namer } } pt.revealIgnored match { - case pt: FunProto if pt.isGivenApply => + case pt: FunProto if pt.isUsingApply => // We can end up here if extension methods are called with explicit given arguments. // See for instance #7119. tree @@ -3216,8 +3216,8 @@ class Typer extends Namer * Overridden in `ReTyper`, where all applications are treated the same */ protected def matchingApply(methType: MethodOrPoly, pt: FunProto)(implicit ctx: Context): Boolean = - methType.isContextualMethod == pt.isGivenApply || - methType.isImplicitMethod && pt.isGivenApply // for a transition allow `with` arguments for regular implicit parameters + methType.isContextualMethod == pt.isUsingApply || + methType.isImplicitMethod && pt.isUsingApply // for a transition allow `with` arguments for regular implicit parameters /** Check that `tree == x: pt` is typeable. Used when checking a pattern * against a selector of type `pt`. This implementation accounts for diff --git a/tests/pos/i7793.scala b/tests/pos/i7793.scala new file mode 100644 index 000000000000..3458315c48c5 --- /dev/null +++ b/tests/pos/i7793.scala @@ -0,0 +1,9 @@ +trait Foo: + def g(f: Int => Int): Int = 1 + def g(using String)(f: Int => String): String = "2" + +@main def Test = + val m: Foo = ??? + given String = "foo" + m.g(x => "2") + m.g(using summon[String])(x => "2") \ No newline at end of file