Skip to content

Commit 04c0f66

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 26af65f commit 04c0f66

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
@@ -438,7 +438,7 @@ object Contexts {
438438
override def toString = {
439439
def iinfo(implicit ctx: Context) = if (ctx.importInfo == null) "" else i"${ctx.importInfo.selectors}%, %"
440440
"Context(\n" +
441-
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}, import = ${iinfo(ctx)}") mkString "\n")
441+
(outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${if (ctx.scope != null) ctx.scope.toList(ctx) else null}, import = ${iinfo(ctx)}") mkString "\n")
442442
}
443443
}
444444

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
178178
": " ~ toText(tp.memberProto) ~ " }"
179179
case tp: ViewProto =>
180180
return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
181-
case tp @ FunProto(args, resultType, _) =>
181+
case tp @ FunProto(args, resultType, _, _) =>
182182
val argsText = args match {
183183
case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp)
184184
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
@@ -478,7 +478,7 @@ object Erasure {
478478
val Apply(fun, args) = tree
479479
if (fun.symbol == defn.cbnArg)
480480
typedUnadapted(args.head, pt)
481-
else typedExpr(fun, FunProto(args, pt, this)) match {
481+
else typedExpr(fun, FunProto(args, pt, this, untpd.isSelfConstrCall(tree))) match {
482482
case fun1: Apply => // arguments passed in prototype were already passed
483483
fun1
484484
case fun1 =>

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

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

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

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

696696
/** Type application where arguments come from prototype, and no implicits are inserted */
@@ -701,7 +701,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
701701
if (proto.allArgTypesAreCurrent())
702702
new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
703703
else
704-
new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree))
704+
new ApplyToUntyped(tree, fun1, funRef, proto, pt)
705705
convertNewGenericArray(ConstFold(app.result))
706706
case _ =>
707707
handleUnexpectedFunType(tree, fun1)
@@ -1317,7 +1317,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13171317
alts filter (isApplicable(_, argTypes, resultType))
13181318

13191319
val candidates = pt match {
1320-
case pt @ FunProto(args, resultType, _) =>
1320+
case pt @ FunProto(args, resultType, _, _) =>
13211321
val numArgs = args.length
13221322
val normArgs = args.mapConserve {
13231323
case Block(Nil, expr) => expr
@@ -1370,7 +1370,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13701370
if (isDetermined(alts2)) alts2
13711371
else {
13721372
pretypeArgs(alts2, pt)
1373-
narrowByTrees(alts2, pt.typedArgs, resultType)
1373+
narrowByTrees(alts2, pt.typedArgs(ctx.addMode(Mode.ImplicitsEnabled)), resultType)
13741374
}
13751375
}
13761376

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

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

758758
lazy val funProto = fullProto match {
759759
case proto: ViewProto =>
760-
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
760+
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self, isSelfConstrCall = false)
761761
case proto => proto
762762
}
763763

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

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

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

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

190-
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) =
191-
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
192-
else new FunProto(args, resultType, typer)
206+
def derivedFunProto(args: List[untpd.Tree] = this.args, resType: Type, typer: Typer = this.typer) =
207+
if ((args eq this.args) && (resType eq this.resType) && (typer eq this.typer)) this
208+
else new FunProto(args, resType, typer, isSelfConstrCall)
193209

194210
override def notApplied = WildcardType
195-
196211
/** Forget the types of any arguments that have been typed producing a constraint in a
197212
* typer state that is not yet committed into the one of the current context `ctx`.
198213
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
@@ -226,7 +241,7 @@ object ProtoTypes {
226241
/** The typed arguments. This takes any arguments already typed using
227242
* `typedArg` into account.
228243
*/
229-
def typedArgs: List[Tree] = {
244+
def typedArgs(implicit ctx: Context): List[Tree] = withArgCtx { implicit ctx =>
230245
if (myTypedArgs.size != args.length)
231246
myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_)))
232247
myTypedArgs
@@ -235,7 +250,7 @@ object ProtoTypes {
235250
/** Type single argument and remember the unadapted result in `myTypedArg`.
236251
* used to avoid repeated typings of trees when backtracking.
237252
*/
238-
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
253+
def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = withArgCtx { implicit ctx =>
239254
val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal))
240255
typer.adapt(targ, formal)
241256
}
@@ -253,7 +268,7 @@ object ProtoTypes {
253268
case pt: FunProto =>
254269
pt
255270
case _ =>
256-
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
271+
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resType, typer, isSelfConstrCall)
257272
tupled
258273
}
259274

@@ -276,7 +291,7 @@ object ProtoTypes {
276291

277292
def isDropped: Boolean = toDrop
278293

279-
override def toString = s"FunProto(${args mkString ","} => $resultType)"
294+
override def toString = s"FunProto(${args mkString ","} => $resType)"
280295

281296
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
282297
derivedFunProto(args, tm(resultType), typer)
@@ -292,8 +307,10 @@ object ProtoTypes {
292307
*
293308
* [](args): resultType, where args are known to be typed
294309
*/
295-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) {
296-
override def typedArgs = args
310+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)
311+
// args are already typed so no need to set isSelfConstrCall
312+
extends FunProto(args, resultType, typer, isSelfConstrCall = false) {
313+
override def typedArgs(implicit ctx: Context) = args
297314
}
298315

299316
/** A prototype for implicitly inferred views:
@@ -330,7 +347,7 @@ object ProtoTypes {
330347
}
331348

332349
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
333-
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)
350+
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer, isSelfConstrCall = false)
334351

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

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

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

18241824
pt match {
1825-
case pt @ FunProto(Nil, _, _)
1825+
case pt @ FunProto(Nil, _, _, _)
18261826
if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) =>
18271827
pt.markAsDropped()
18281828
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)