Skip to content

Commit 9debedd

Browse files
committed
Add fallback E -> new E if typing an applied function fails
1 parent 253307a commit 9debedd

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
@@ -214,7 +214,7 @@ object Applications {
214214
*/
215215
class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile)
216216
extends IntegratedTypeArgs(app)
217-
217+
218218
/** 1. If we are in an inline method but not in a nested quote, mark the inline method
219219
* as a macro.
220220
*
@@ -803,6 +803,29 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
803803
if (ctx.owner.isClassConstructor && untpd.isSelfConstrCall(app)) ctx.thisCallArgContext
804804
else ctx
805805

806+
/** Typecheck the function part of an application.
807+
* Fallback if this fails: try to convert `E` to `new E`.
808+
*/
809+
def typedFunPart(fn: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
810+
tryEither { implicit ctx =>
811+
typedExpr(fn, pt)
812+
} { (result, tstate) =>
813+
def fallBack = {
814+
tstate.commit()
815+
result
816+
}
817+
fn match {
818+
case Ident(name) =>
819+
tryNewWithType(cpy.Ident(fn)(name.toTypeName), pt, fallBack)
820+
case Select(qual, name) =>
821+
tryNewWithType(cpy.Select(fn)(qual, name.toTypeName), pt, fallBack)
822+
// TODO: try to keep as much as possible from typed `qual` in order to avoid
823+
// combinatorial explosion
824+
case _ =>
825+
fallBack
826+
}
827+
}
828+
806829
/** Typecheck application. Result could be an `Apply` node,
807830
* or, if application is an operator assignment, also an `Assign` or
808831
* Block node.
@@ -812,7 +835,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
812835
def realApply(implicit ctx: Context): Tree = track("realApply") {
813836
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isContextual)(argCtx(tree))
814837
record("typedApply")
815-
val fun1 = typedExpr(tree.fun, originalProto)
838+
val fun1 = typedFunPart(tree.fun, originalProto)
816839

817840
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
818841
// a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application,
@@ -947,7 +970,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
947970
val isNamed = hasNamedArg(tree.args)
948971
val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
949972
record("typedTypeApply")
950-
handleMeta(typedExpr(tree.fun, PolyProto(typedArgs, pt)) match {
973+
handleMeta(typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
951974
case IntegratedTypeArgs(app) =>
952975
app
953976
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
@@ -2232,6 +2232,24 @@ class Typer extends Namer
22322232
case _ => false
22332233
}
22342234

2235+
/** Try to rename `tpt` to a type `T` and typecheck `new T` with given expected type `pt`.
2236+
*/
2237+
def tryNewWithType(tpt: untpd.Tree, pt: Type, fallBack: => Tree)(implicit ctx: Context): Tree =
2238+
tryEither { implicit ctx =>
2239+
val tycon = typed(tpt)
2240+
if (ctx.reporter.hasErrors)
2241+
EmptyTree // signal that we should return the error in fallBack
2242+
else
2243+
typed(untpd.Select(untpd.New(untpd.TypedSplice(tycon)), nme.CONSTRUCTOR), pt)
2244+
} { (nu, nuState) =>
2245+
if (nu.isEmpty) fallBack
2246+
else {
2247+
// we found a type constructor, signal the error in its application instead of the original one
2248+
nuState.commit()
2249+
nu
2250+
}
2251+
}
2252+
22352253
/** Potentially add apply node or implicit conversions. Before trying either,
22362254
* if the function is applied to an empty parameter list (), we try
22372255
*
@@ -2273,28 +2291,11 @@ class Typer extends Namer
22732291
}
22742292

22752293
def tryNew(fallBack: => Tree): Tree = {
2276-
2277-
def tryNewWithType(tpt: untpd.Tree): Tree =
2278-
tryEither { implicit ctx =>
2279-
val tycon = typed(tpt.withSpan(tree.span))
2280-
if (ctx.reporter.hasErrors)
2281-
EmptyTree // signal that we should return the error in fallBack
2282-
else
2283-
typed(untpd.Select(untpd.New(untpd.TypedSplice(tycon)), nme.CONSTRUCTOR), pt)
2284-
} { (nu, nuState) =>
2285-
if (nu.isEmpty) fallBack
2286-
else {
2287-
// we found a type constructor, signal the error in its application instead of the original one
2288-
nuState.commit()
2289-
nu
2290-
}
2291-
}
2292-
22932294
tree match {
22942295
case Ident(name) =>
2295-
tryNewWithType(untpd.Ident(name.toTypeName))
2296+
tryNewWithType(cpy.Ident(tree)(name.toTypeName), pt, fallBack)
22962297
case Select(qual, name) =>
2297-
tryNewWithType(untpd.Select(untpd.TypedSplice(qual), name.toTypeName))
2298+
tryNewWithType(cpy.Select(tree)(untpd.TypedSplice(qual), name.toTypeName), pt, fallBack)
22982299
case _ =>
22992300
fallBack
23002301
}

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)