Skip to content

Commit 66801cc

Browse files
committed
Fix #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 a48fc80 commit 66801cc

File tree

8 files changed

+49
-24
lines changed

8 files changed

+49
-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: 30 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,28 @@ 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+
private[this] var thisCallArgCtxUsed: Boolean = false
191+
192+
/** Evalute `op` with the proper context to type the arguments. */
193+
protected def withArgCtx[T](op: Context => T)(implicit ctx: Context) = {
194+
if (isSelfConstrCall && !thisCallArgCtxUsed) {
195+
// Context#thisCallArgContext is not idempotent so we must only use it
196+
// once even with nested calls to withArgCtx
197+
thisCallArgCtxUsed = true
198+
try op(ctx.thisCallArgContext)
199+
finally thisCallArgCtxUsed = false
200+
} else op(ctx)
201+
}
202+
203+
def isMatchedBy(tp: Type)(implicit ctx: Context) = withArgCtx { implicit ctx =>
189204
typer.isApplicable(tp, Nil, typedArgs, resultType)
205+
}
190206

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)
207+
def derivedFunProto(args: List[untpd.Tree] = this.args, resType: Type, typer: Typer = this.typer) =
208+
if ((args eq this.args) && (resType eq this.resType) && (typer eq this.typer)) this
209+
else new FunProto(args, resType, typer, isSelfConstrCall)
194210

195211
override def notApplied = WildcardType
196-
197212
/** Forget the types of any arguments that have been typed producing a constraint in a
198213
* typer state that is not yet committed into the one of the current context `ctx`.
199214
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
@@ -227,7 +242,7 @@ object ProtoTypes {
227242
/** The typed arguments. This takes any arguments already typed using
228243
* `typedArg` into account.
229244
*/
230-
def typedArgs: List[Tree] = {
245+
def typedArgs(implicit ctx: Context): List[Tree] = withArgCtx { implicit ctx =>
231246
if (myTypedArgs.size != args.length)
232247
myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_)))
233248
myTypedArgs
@@ -236,7 +251,7 @@ object ProtoTypes {
236251
/** Type single argument and remember the unadapted result in `myTypedArg`.
237252
* used to avoid repeated typings of trees when backtracking.
238253
*/
239-
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
254+
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = withArgCtx { implicit ctx =>
240255
val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal))
241256
typer.adapt(targ, formal, arg)
242257
}
@@ -254,7 +269,7 @@ object ProtoTypes {
254269
case pt: FunProto =>
255270
pt
256271
case _ =>
257-
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
272+
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resType, typer, isSelfConstrCall)
258273
tupled
259274
}
260275

@@ -277,7 +292,7 @@ object ProtoTypes {
277292

278293
def isDropped: Boolean = toDrop
279294

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

282297
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
283298
derivedFunProto(args, tm(resultType), typer)
@@ -293,8 +308,10 @@ object ProtoTypes {
293308
*
294309
* [](args): resultType, where args are known to be typed
295310
*/
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
311+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)
312+
// args are already typed so no need to set isSelfConstrCall
313+
extends FunProto(args, resultType, typer, isSelfConstrCall = false) {
314+
override def typedArgs(implicit ctx: Context) = args
298315
}
299316

300317
/** A prototype for implicitly inferred views:
@@ -331,7 +348,7 @@ object ProtoTypes {
331348
}
332349

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

336353
/** A prototype for expressions [] that are type-parameterized:
337354
*

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)