Skip to content

Commit 220703b

Browse files
committed
Add fallback E -> new E if typing an applied function fails
1 parent 512b662 commit 220703b

File tree

7 files changed

+59
-26
lines changed

7 files changed

+59
-26
lines changed

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import Contexts._
77
import util.{SimpleIdentityMap, SimpleIdentitySet}
88
import reporting._
99
import config.Config
10+
import config.Printers.constr
1011
import collection.mutable
1112
import java.lang.ref.WeakReference
1213
import util.Stats
14+
import Decorators._
1315

1416
import scala.annotation.internal.sharable
1517

@@ -143,6 +145,8 @@ class TyperState(private val previous: TyperState /* | Null */) {
143145
def commit()(implicit ctx: Context): Unit = {
144146
Stats.record("typerState.commit")
145147
val targetState = ctx.typerState
148+
if (constraint ne targetState.constraint)
149+
constr.println(i"committing $this to $targetState, fromConstr = $constraint, toConstr = ${targetState.constraint}")
146150
assert(isCommittable)
147151
if (targetState.constraint eq previousConstraint) targetState.constraint = constraint
148152
else targetState.mergeConstraintWith(this)

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ object Applications {
191191
*/
192192
class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile)
193193
extends IntegratedTypeArgs(app)
194-
194+
195195
/** 1. If we are in an inline method but not in a nested quote, mark the inline method
196196
* as a macro.
197197
*
@@ -780,6 +780,29 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
780780
if (ctx.owner.isClassConstructor && untpd.isSelfConstrCall(app)) ctx.thisCallArgContext
781781
else ctx
782782

783+
/** Typecheck the function part of an application.
784+
* Fallback if this fails: try to convert `E` to `new E`.
785+
*/
786+
def typedFunPart(fn: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
787+
tryEither { implicit ctx =>
788+
typedExpr(fn, pt)
789+
} { (result, tstate) =>
790+
def fallBack = {
791+
tstate.commit()
792+
result
793+
}
794+
fn match {
795+
case Ident(name) =>
796+
tryNewWithType(cpy.Ident(fn)(name.toTypeName), pt, fallBack)
797+
case Select(qual, name) =>
798+
tryNewWithType(cpy.Select(fn)(qual, name.toTypeName), pt, fallBack)
799+
// TODO: try to keep as much as possible from typed `qual` in order to avoid
800+
// combinatorial explosion
801+
case _ =>
802+
fallBack
803+
}
804+
}
805+
783806
/** Typecheck application. Result could be an `Apply` node,
784807
* or, if application is an operator assignment, also an `Assign` or
785808
* Block node.
@@ -789,7 +812,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
789812
def realApply(implicit ctx: Context): Tree = track("realApply") {
790813
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isContextual)(argCtx(tree))
791814
record("typedApply")
792-
val fun1 = typedExpr(tree.fun, originalProto)
815+
val fun1 = typedFunPart(tree.fun, originalProto)
793816

794817
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
795818
// a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application,
@@ -924,7 +947,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
924947
val isNamed = hasNamedArg(tree.args)
925948
val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
926949
record("typedTypeApply")
927-
handleMeta(typedExpr(tree.fun, PolyProto(typedArgs, pt)) match {
950+
handleMeta(typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
928951
case IntegratedTypeArgs(app) =>
929952
app
930953
case _: TypeApply if !ctx.isAfterTyper =>

compiler/src/dotty/tools/dotc/typer/ReTyper.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class ReTyper extends Typer with ReChecking {
6464
override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree =
6565
promote(tree)
6666

67+
override def typedFunPart(fn: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
68+
typedExpr(fn, pt)
69+
6770
override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = {
6871
assertTyped(tree)
6972
val body1 = typed(tree.body, pt)
@@ -93,6 +96,8 @@ class ReTyper extends Typer with ReChecking {
9396
override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType, locked: TypeVars)(fallBack: => Tree)(implicit ctx: Context): Tree =
9497
fallBack
9598

99+
override def tryNewWithType(tpt: untpd.Tree, pt: Type, fallBack: => Tree)(implicit ctx: Context): Tree = fallBack
100+
96101
override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = ()
97102

98103
override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] =

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,24 @@ class Typer extends Namer
22192219
case _ => false
22202220
}
22212221

2222+
/** Try to rename `tpt` to a type `T` and typecheck `new T` with given expected type `pt`.
2223+
*/
2224+
def tryNewWithType(tpt: untpd.Tree, pt: Type, fallBack: => Tree)(implicit ctx: Context): Tree =
2225+
tryEither { implicit ctx =>
2226+
val tycon = typed(tpt)
2227+
if (ctx.reporter.hasErrors)
2228+
EmptyTree // signal that we should return the error in fallBack
2229+
else
2230+
typed(untpd.Select(untpd.New(untpd.TypedSplice(tycon)), nme.CONSTRUCTOR), pt)
2231+
} { (nu, nuState) =>
2232+
if (nu.isEmpty) fallBack
2233+
else {
2234+
// we found a type constructor, signal the error in its application instead of the original one
2235+
nuState.commit()
2236+
nu
2237+
}
2238+
}
2239+
22222240
/** Potentially add apply node or implicit conversions. Before trying either,
22232241
* if the function is applied to an empty parameter list (), we try
22242242
*
@@ -2260,28 +2278,11 @@ class Typer extends Namer
22602278
}
22612279

22622280
def tryNew(fallBack: => Tree): Tree = {
2263-
2264-
def tryNewWithType(tpt: untpd.Tree): Tree =
2265-
tryEither { implicit ctx =>
2266-
val tycon = typed(tpt.withSpan(tree.span))
2267-
if (ctx.reporter.hasErrors)
2268-
EmptyTree // signal that we should return the error in fallBack
2269-
else
2270-
typed(untpd.Select(untpd.New(untpd.TypedSplice(tycon)), nme.CONSTRUCTOR), pt)
2271-
} { (nu, nuState) =>
2272-
if (nu.isEmpty) fallBack
2273-
else {
2274-
// we found a type constructor, signal the error in its application instead of the original one
2275-
nuState.commit()
2276-
nu
2277-
}
2278-
}
2279-
22802281
tree match {
22812282
case Ident(name) =>
2282-
tryNewWithType(untpd.Ident(name.toTypeName))
2283+
tryNewWithType(cpy.Ident(tree)(name.toTypeName), pt, fallBack)
22832284
case Select(qual, name) =>
2284-
tryNewWithType(untpd.Select(untpd.TypedSplice(qual), name.toTypeName))
2285+
tryNewWithType(cpy.Select(tree)(untpd.TypedSplice(qual), name.toTypeName), pt, fallBack)
22852286
case _ =>
22862287
fallBack
22872288
}

tests/neg/i1648.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
class Foo { Object[A] } // error // error
1+
class Foo { Object[A] } // error

tests/neg/i1907.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import java.io.File
33
object Test {
44
Some(new File("."))
55
.map(_.listFiles).getOrElse(Array.empty) // error: undetermined ClassTag
6-
.map(_.listFiles)
6+
.map(_.listFiles) // error: missing parameter type
77
}

tests/run/creator-applys.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object Test extends App {
3737
assert((x8: C[String, Int]).run == "C a 1")
3838
*/
3939
}
40-
/*
40+
4141
object Test2 {
4242
class A {
4343
def run = "A"
@@ -66,4 +66,4 @@ object Test2 {
6666

6767
val x6 = C("a", 1)
6868
assert((x6: C[String, Int]).run == "C a 1")
69-
}*/
69+
}

0 commit comments

Comments
 (0)