Skip to content

Commit 0eb2d76

Browse files
committed
Proparage function result constrains when inferring parameter types
If an application has functions with implicit parameter types we need to be more aggressive about propagating knowledge of the expected result type into the constraint. Fixes #1378.
1 parent bd45ecc commit 0eb2d76

File tree

5 files changed

+33
-2
lines changed

5 files changed

+33
-2
lines changed

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
278278
}
279279

280280
trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] =>
281-
// todo: fill with methods from TreeInfo that only apply to untpd.Tree's
281+
import TreeInfo._
282+
283+
def isFunctionWithImplicitParamType(tree: Tree) = tree match {
284+
case untpd.Function(args, _) =>
285+
args.exists {
286+
case ValDef(_, tpt, _) => tpt.isEmpty
287+
case _ => false
288+
}
289+
case _ => false
290+
}
291+
292+
// todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
282293
}
283294

284295
trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
553553
// a modified tree but this would be more convoluted and less efficient.
554554
if (proto.isTupled) proto = proto.tupled
555555

556+
// If some of the application's arguments are function literals without explicitly declared
557+
// parameter types, and the expected type is a value type, relate the
558+
// normalized result type of the application with the expected type through `<:<`.
559+
// This can add more constraints which help sharpen the inferred parameter
560+
// types for the argument function literal(s).
561+
// This tweak is needed to make i1348 compile.
562+
if (tree.args.exists(untpd.isFunctionWithImplicitParamType(_)))
563+
if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt)))
564+
typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt")
565+
556566
fun1.tpe match {
557567
case ErrorType => tree.withType(ErrorType)
558568
case TryDynamicCallType =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ object ProtoTypes {
175175
def isMatchedBy(tp: Type)(implicit ctx: Context) =
176176
typer.isApplicable(tp, Nil, typedArgs, resultType)
177177

178-
def derivedFunProto(args: List[untpd.Tree], resultType: Type, typer: Typer) =
178+
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) =
179179
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
180180
else new FunProto(args, resultType, typer)
181181

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
597597
untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt)
598598
else {
599599
val params = args.asInstanceOf[List[untpd.ValDef]]
600+
601+
pt match {
602+
case pt: TypeVar if untpd.isFunctionWithImplicitParamType(tree) =>
603+
isFullyDefined(pt, ForceDegree.noBottom)
604+
case _ =>
605+
}
606+
600607
val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length)
601608

602609
def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match {

tests/pos/i1378.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test {
2+
(1, x => 2): (Int, Int => Int)
3+
}

0 commit comments

Comments
 (0)