Skip to content

Fix #1378: Propagate more knowledge of result type into applications #1395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
}

trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] =>
// todo: fill with methods from TreeInfo that only apply to untpd.Tree's
import TreeInfo._

def isFunctionWithUnknownParamType(tree: Tree) = tree match {
case untpd.Function(args, _) =>
args.exists {
case ValDef(_, tpt, _) => tpt.isEmpty
case _ => false
}
case _ => false
}

// todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
}

trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/core/Constants.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ object Constants {
ctx.typerState.constraint.entry(param) match {
case TypeBounds(lo, hi) =>
if (hi.classSymbol.isPrimitiveValueClass) hi //constrain further with high bound
else lo
case NoType => param.binder.paramBounds(param.paramNum).lo
else classBound(lo)
case NoType => classBound(param.binder.paramBounds(param.paramNum).lo)
case inst => classBound(inst)
}
case pt => pt
Expand Down
9 changes: 9 additions & 0 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
// a modified tree but this would be more convoluted and less efficient.
if (proto.isTupled) proto = proto.tupled

// If some of the application's arguments are function literals without explicitly declared
// parameter types, relate the normalized result type of the application with the
// expected type through `constrainResult`. This can add more constraints which
// help sharpen the inferred parameter types for the argument function literal(s).
// This tweak is needed to make i1378 compile.
if (tree.args.exists(untpd.isFunctionWithUnknownParamType(_)))
if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt)))
typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt")

fun1.tpe match {
case ErrorType => tree.withType(ErrorType)
case TryDynamicCallType =>
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ object ProtoTypes {
def isMatchedBy(tp: Type)(implicit ctx: Context) =
typer.isApplicable(tp, Nil, typedArgs, resultType)

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

Expand Down
10 changes: 10 additions & 0 deletions src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt)
else {
val params = args.asInstanceOf[List[untpd.ValDef]]

pt match {
case pt: TypeVar if untpd.isFunctionWithUnknownParamType(tree) =>
// try to instantiate `pt` if this is possible. If it does not
// work the error will be reported later in `inferredParam`,
// when we try to infer the parameter type.
isFullyDefined(pt, ForceDegree.noBottom)
Copy link
Member

@smarter smarter Jul 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment should clarify that this method is called for its side-effects, and why we don't need to do anything special when it returns false (I think this is the only instance of isFullyDefined in the compiler where we ignore the return value)

case _ =>
}

val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length)

def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match {
Expand Down
1 change: 1 addition & 0 deletions test/dotc/scala-collections.whitelist
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
./scala-scala/src/library/scala/collection/immutable/Seq.scala
./scala-scala/src/library/scala/collection/mutable/IndexedSeq.scala
./scala-scala/src/library/scala/collection/mutable/ListBuffer.scala
#./scala-scala/src/library/scala/collection/mutable/BufferLike.scala // works under junit, fails under partest, but can't see more info on the cause

./scala-scala/src/library/scala/collection/mutable/ArrayBuilder.scala

Expand Down
16 changes: 7 additions & 9 deletions test/test/CompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -236,17 +236,14 @@ abstract class CompilerTest {
val processor = if (allArgs.exists(_.startsWith("#"))) Bench else Main
val storeReporter = new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
private val consoleReporter = new ConsoleReporter()
private var innerStoreReporter = new StoreReporter(consoleReporter)
private val innerStoreReporter = new StoreReporter(consoleReporter)
def doReport(d: Diagnostic)(implicit ctx: Context): Unit = {
if (innerStoreReporter == null) {
consoleReporter.report(d)
} else {
innerStoreReporter.report(d)
if (d.level == ERROR) {
innerStoreReporter.flush()
innerStoreReporter = null
}
if (d.level == ERROR) {
innerStoreReporter.flush()
consoleReporter.doReport(d)
}
else if (errorCount > 0) consoleReporter.doReport(d)
else innerStoreReporter.doReport(d)
}
}
val reporter = processor.process(allArgs, storeReporter)
Expand All @@ -260,6 +257,7 @@ abstract class CompilerTest {
assert(nerrors == xerrors,
s"""Wrong # of errors. Expected: $xerrors, found: $nerrors
|Files with expected errors: $expectedErrorFiles
|errors:
""".stripMargin)
// NEG TEST
if (xerrors > 0) {
Expand Down
3 changes: 3 additions & 0 deletions tests/pos/i1378.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object Test {
(1, x => 2): (Int, Int => Int)
}