Skip to content

Commit 1fef282

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 1fef282

File tree

8 files changed

+50
-24
lines changed

8 files changed

+50
-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: 31 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,29 @@ 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+
assert(thisCallArgCtxUsed || ctx.outersIterator.contains(ictx))
195+
if (isSelfConstrCall && !thisCallArgCtxUsed) {
196+
// Context#thisCallArgContext is not idempotent so we must only use it
197+
// once even with nested calls to withArgCtx
198+
thisCallArgCtxUsed = true
199+
try op(ctx.thisCallArgContext)
200+
finally thisCallArgCtxUsed = false
201+
} else op(ctx)
202+
}
203+
204+
def isMatchedBy(tp: Type)(implicit ctx: Context) = withArgCtx { implicit ctx =>
189205
typer.isApplicable(tp, Nil, typedArgs, resultType)
206+
}
190207

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

195212
override def notApplied = WildcardType
196-
197213
/** Forget the types of any arguments that have been typed producing a constraint in a
198214
* typer state that is not yet committed into the one of the current context `ctx`.
199215
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
@@ -227,7 +243,7 @@ object ProtoTypes {
227243
/** The typed arguments. This takes any arguments already typed using
228244
* `typedArg` into account.
229245
*/
230-
def typedArgs: List[Tree] = {
246+
def typedArgs(implicit ctx: Context): List[Tree] = withArgCtx { implicit ctx =>
231247
if (myTypedArgs.size != args.length)
232248
myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_)))
233249
myTypedArgs
@@ -236,7 +252,7 @@ object ProtoTypes {
236252
/** Type single argument and remember the unadapted result in `myTypedArg`.
237253
* used to avoid repeated typings of trees when backtracking.
238254
*/
239-
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
255+
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = withArgCtx { implicit ctx =>
240256
val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal))
241257
typer.adapt(targ, formal, arg)
242258
}
@@ -254,7 +270,7 @@ object ProtoTypes {
254270
case pt: FunProto =>
255271
pt
256272
case _ =>
257-
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
273+
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resType, typer, isSelfConstrCall)
258274
tupled
259275
}
260276

@@ -277,7 +293,7 @@ object ProtoTypes {
277293

278294
def isDropped: Boolean = toDrop
279295

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

282298
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
283299
derivedFunProto(args, tm(resultType), typer)
@@ -293,8 +309,10 @@ object ProtoTypes {
293309
*
294310
* [](args): resultType, where args are known to be typed
295311
*/
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
312+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)
313+
// args are already typed so no need to set isSelfConstrCall
314+
extends FunProto(args, resultType, typer, isSelfConstrCall = false) {
315+
override def typedArgs(implicit ctx: Context) = args
298316
}
299317

300318
/** A prototype for implicitly inferred views:
@@ -331,7 +349,7 @@ object ProtoTypes {
331349
}
332350

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

336354
/** A prototype for expressions [] that are type-parameterized:
337355
*

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)