Skip to content

Commit 72d5835

Browse files
committed
Fix scala#2412: Local error causes spurious errors to be reported
`FunProto#typedArgs` was using the implicit Context from the `FunProto` constructor, and thus errors weren't stored in the proper context. This is fixed by passing an implicit Context argument to `typedArgs` (this was already done for `typedArg`), this means that we no longer need to store a Context in the class, on the other hand there are some new difficulties: - `FunProto#typedArgs` can only be safely called from a point where the implicit Context contains the Context where the `FunProto` instance was created in its owner chain. This is usually the case but required some changes to Implicits (done in the previous commit) - The Context used must be created with Context#thisCallArgContext when typing the arguments of a this(...) constructor call. This requires passing storing this information in FunProto. This wouldn't be necessary if we could simply type the whole this(...) Apply node with Context#thisCallArgContext, unfortunately this does not work since this Context does not have the constructors of the class in scope, and I did not figure out a way to construct a Context that would have them in scope without having the other methods of the class in scope.
1 parent 2597022 commit 72d5835

File tree

8 files changed

+40
-24
lines changed

8 files changed

+40
-24
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ object Contexts {
434434
override def toString = {
435435
def iinfo(implicit ctx: Context) = if (ctx.importInfo == null) "" else i"${ctx.importInfo.selectors}%, %"
436436
"Context(\n" +
437-
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}, import = ${iinfo(ctx)}") mkString "\n")
437+
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${if (ctx.scope != null) ctx.scope.toList(ctx) else null}, import = ${iinfo(ctx)}") mkString "\n")
438438
}
439439
}
440440

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
187187
": " ~ toText(tp.memberProto) ~ " }"
188188
case tp: ViewProto =>
189189
return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
190-
case tp @ FunProto(args, resultType, _) =>
190+
case tp @ FunProto(args, resultType, _, _) =>
191191
val argsText = args match {
192192
case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp)
193193
case _ => toTextGlobal(args, ", ")

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ object Erasure {
465465
val Apply(fun, args) = tree
466466
if (fun.symbol == defn.cbnArg)
467467
typedUnadapted(args.head, pt)
468-
else typedExpr(fun, FunProto(args, pt, this)) match {
468+
else typedExpr(fun, FunProto(args, pt, this, untpd.isSelfConstrCall(tree))) match {
469469
case fun1: Apply => // arguments passed in prototype were already passed
470470
fun1
471471
case fun1 =>

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
650650
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
651651

652652
def realApply(implicit ctx: Context): Tree = track("realApply") {
653-
val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree))
653+
val originalProto = new FunProto(tree.args, IgnoredProto(pt), this, untpd.isSelfConstrCall(tree))
654654
val fun1 = typedExpr(tree.fun, originalProto)
655655

656656
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
@@ -668,7 +668,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
668668
// help sharpen the inferred parameter types for the argument function literal(s).
669669
// This tweak is needed to make i1378 compile.
670670
if (tree.args.exists(untpd.isFunctionWithUnknownParamType(_)))
671-
if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt)))
671+
if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resType = pt)))
672672
typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt")
673673

674674
/** Type application where arguments come from prototype, and no implicits are inserted */
@@ -679,7 +679,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
679679
if (proto.allArgTypesAreCurrent())
680680
new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
681681
else
682-
new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree))
682+
new ApplyToUntyped(tree, fun1, funRef, proto, pt)
683683
convertNewGenericArray(ConstFold(app.result))
684684
case _ =>
685685
handleUnexpectedFunType(tree, fun1)
@@ -1292,7 +1292,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12921292
alts filter (isApplicable(_, argTypes, resultType))
12931293

12941294
val candidates = pt match {
1295-
case pt @ FunProto(args, resultType, _) =>
1295+
case pt @ FunProto(args, resultType, _, _) =>
12961296
val numArgs = args.length
12971297
val normArgs = args.mapConserve {
12981298
case Block(Nil, expr) => expr
@@ -1345,7 +1345,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13451345
if (isDetermined(alts2)) alts2
13461346
else {
13471347
pretypeArgs(alts2, pt)
1348-
narrowByTrees(alts2, pt.typedArgs, resultType)
1348+
narrowByTrees(alts2, pt.typedArgs(ctx.addMode(Mode.ImplicitsEnabled)), resultType)
13491349
}
13501350
}
13511351

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ trait Implicits { self: Typer =>
752752

753753
lazy val funProto = fullProto match {
754754
case proto: ViewProto =>
755-
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
755+
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self, isSelfConstrCall = false)
756756
case proto => proto
757757
}
758758

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,10 @@ object ProtoTypes {
172172
/** A prototype for expressions that appear in function position
173173
*
174174
* [](args): resultType
175+
*
176+
* @param isSelfConstrCall True for prototypes of `this(...)` constructor calls
175177
*/
176-
case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context)
178+
case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer, isSelfConstrCall: Boolean)
177179
extends UncachedGroundType with ApplyingProto {
178180
private var myTypedArgs: List[Tree] = Nil
179181

@@ -185,15 +187,19 @@ object ProtoTypes {
185187
/** A map recording the typer states in which arguments stored in myTypedArg were typed */
186188
private var evalState: SimpleMap[untpd.Tree, TyperState] = SimpleMap.Empty
187189

188-
def isMatchedBy(tp: Type)(implicit ctx: Context) =
190+
/** Evalute `op` with the proper context to type the arguments. */
191+
protected def withArgCtx[T](op: Context => T)(implicit ctx: Context) =
192+
if (isSelfConstrCall) op(ctx.thisCallArgContext) else op(ctx)
193+
194+
def isMatchedBy(tp: Type)(implicit ctx: Context) = withArgCtx { implicit ctx =>
189195
typer.isApplicable(tp, Nil, typedArgs, resultType)
196+
}
190197

191-
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) =
192-
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
193-
else new FunProto(args, resultType, typer)
198+
def derivedFunProto(args: List[untpd.Tree] = this.args, resType: Type, typer: Typer = this.typer) =
199+
if ((args eq this.args) && (resType eq this.resType) && (typer eq this.typer)) this
200+
else new FunProto(args, resType, typer, isSelfConstrCall)
194201

195202
override def notApplied = WildcardType
196-
197203
/** Forget the types of any arguments that have been typed producing a constraint in a
198204
* typer state that is not yet committed into the one of the current context `ctx`.
199205
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
@@ -227,7 +233,7 @@ object ProtoTypes {
227233
/** The typed arguments. This takes any arguments already typed using
228234
* `typedArg` into account.
229235
*/
230-
def typedArgs: List[Tree] = {
236+
def typedArgs(implicit ctx: Context): List[Tree] = withArgCtx { implicit ctx =>
231237
if (myTypedArgs.size != args.length)
232238
myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_)))
233239
myTypedArgs
@@ -236,7 +242,7 @@ object ProtoTypes {
236242
/** Type single argument and remember the unadapted result in `myTypedArg`.
237243
* used to avoid repeated typings of trees when backtracking.
238244
*/
239-
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
245+
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = withArgCtx { implicit ctx =>
240246
val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal))
241247
typer.adapt(targ, formal, arg)
242248
}
@@ -254,7 +260,7 @@ object ProtoTypes {
254260
case pt: FunProto =>
255261
pt
256262
case _ =>
257-
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
263+
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resType, typer, isSelfConstrCall)
258264
tupled
259265
}
260266

@@ -277,7 +283,7 @@ object ProtoTypes {
277283

278284
def isDropped: Boolean = toDrop
279285

280-
override def toString = s"FunProto(${args mkString ","} => $resultType)"
286+
override def toString = s"FunProto(${args mkString ","} => $resType)"
281287

282288
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
283289
derivedFunProto(args, tm(resultType), typer)
@@ -293,8 +299,10 @@ object ProtoTypes {
293299
*
294300
* [](args): resultType, where args are known to be typed
295301
*/
296-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) {
297-
override def typedArgs = args
302+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)
303+
// args are already typed so no need to set isSelfConstrCall
304+
extends FunProto(args, resultType, typer, isSelfConstrCall = false) {
305+
override def typedArgs(implicit ctx: Context) = args
298306
}
299307

300308
/** A prototype for implicitly inferred views:
@@ -331,7 +339,7 @@ object ProtoTypes {
331339
}
332340

333341
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
334-
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)
342+
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer, isSelfConstrCall = false)
335343

336344
/** A prototype for expressions [] that are type-parameterized:
337345
*

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
771771
expr1.tpe
772772
case _ =>
773773
val protoArgs = args map (_ withType WildcardType)
774-
val callProto = FunProto(protoArgs, WildcardType, this)
774+
val callProto = FunProto(protoArgs, WildcardType, this, isSelfConstrCall = false)
775775
val expr1 = typedExpr(expr, callProto)
776776
fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
777777
expr1.tpe
@@ -1814,7 +1814,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
18141814
tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack)
18151815

18161816
pt match {
1817-
case pt @ FunProto(Nil, _, _)
1817+
case pt @ FunProto(Nil, _, _, _)
18181818
if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) =>
18191819
pt.markAsDropped()
18201820
tree

tests/neg/i2412.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
def test(foo: List[String]): Unit = {
3+
foo.filter(f => {
4+
iDontExist // error: not found
5+
true
6+
})
7+
}
8+
}

0 commit comments

Comments
 (0)