Skip to content

Commit 84966d2

Browse files
authored
Merge pull request #4871 from dotty-staging/fix-#4466
Fix #4466: Update contexts of FunProto when something else is tried
2 parents c42e325 + 2e708a1 commit 84966d2

15 files changed

+109
-53
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
8585
case _ => tree
8686
}
8787

88+
/** If this is a block, its expression part */
89+
def stripBlock(tree: Tree): Tree = unsplice(tree) match {
90+
case Block(_, expr) => stripBlock(expr)
91+
case _ => tree
92+
}
93+
8894
/** The number of arguments in an application */
8995
def numArgs(tree: Tree): Int = unsplice(tree) match {
9096
case Apply(fn, args) => numArgs(fn) + args.length

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
981981

982982
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
983983
val typer = ctx.typer
984-
val proto = new FunProtoTyped(args, expectedType, typer)
984+
val proto = new FunProtoTyped(args, expectedType)(typer)
985985
val denot = receiver.tpe.member(method)
986986
assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}")
987987
val selected =

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,11 @@ object Types {
15931593
def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean
15941594
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T
15951595
def map(tm: TypeMap)(implicit ctx: Context): ProtoType
1596+
1597+
/** If this prototype captures a context, the same prototype except that the result
1598+
* captures the given context `ctx`.
1599+
*/
1600+
def withContext(ctx: Context): ProtoType = this
15961601
}
15971602

15981603
/** Implementations of this trait cache the results of `narrow`. */

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ class TreeUnpickler(reader: TastyReader,
855855
val vparams = readIndexedParams[ValDef](PARAM)
856856
val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
857857
nextUnsharedTag match {
858-
case APPLY | TYPEAPPLY => readTerm()(parentCtx)
858+
case APPLY | TYPEAPPLY | BLOCK => readTerm()(parentCtx)
859859
case _ => readTpt()(parentCtx)
860860
}
861861
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
208208
": " ~ toText(tp.memberProto) ~ " }"
209209
case tp: ViewProto =>
210210
return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
211-
case tp @ FunProto(args, resultType, _) =>
211+
case tp @ FunProto(args, resultType) =>
212212
val argsText = args match {
213213
case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp)
214214
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
@@ -500,7 +500,7 @@ object Erasure {
500500
val Apply(fun, args) = tree
501501
if (fun.symbol == defn.cbnArg)
502502
typedUnadapted(args.head, pt)
503-
else typedExpr(fun, FunProto(args, pt, this)) match {
503+
else typedExpr(fun, FunProto(args, pt)(this)) match {
504504
case fun1: Apply => // arguments passed in prototype were already passed
505505
fun1
506506
case fun1 =>

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,19 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
168168
* to be used as initializers of trait parameters if the target of the call
169169
* is a trait.
170170
*/
171-
def transformConstructor(tree: Tree): (Tree, List[Tree]) = {
172-
val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree
173-
val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil)
174-
(superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs)
171+
def transformConstructor(tree: Tree): (Tree, List[Tree]) = tree match {
172+
case Block(stats, expr) =>
173+
val (scall, inits) = transformConstructor(expr)
174+
(cpy.Block(tree)(stats, scall), inits)
175+
case _ =>
176+
val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree
177+
val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil)
178+
(superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs)
175179
}
176180

177181
val superCallsAndArgs = (
178-
for (p <- impl.parents if p.symbol.isConstructor)
179-
yield p.symbol.owner -> transformConstructor(p)
182+
for (p <- impl.parents; constr = stripBlock(p).symbol if constr.isConstructor)
183+
yield constr.owner -> transformConstructor(p)
180184
).toMap
181185
val superCalls = superCallsAndArgs.mapValues(_._1)
182186
val initArgs = superCallsAndArgs.mapValues(_._2)

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

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

692692
def realApply(implicit ctx: Context): Tree = track("realApply") {
693-
val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree))
693+
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this)(argCtx(tree))
694694
val fun1 = typedExpr(tree.fun, originalProto)
695695

696696
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
@@ -1365,7 +1365,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13651365
alts filter (isApplicable(_, argTypes, resultType))
13661366

13671367
val candidates = pt match {
1368-
case pt @ FunProto(args, resultType, _) =>
1368+
case pt @ FunProto(args, resultType) =>
13691369
val numArgs = args.length
13701370
val normArgs = args.mapConserve {
13711371
case Block(Nil, expr) => expr

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

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

898898
lazy val funProto = fullProto match {
899899
case proto: ViewProto =>
900-
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
900+
FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType)(self)
901901
case proto => proto
902902
}
903903

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

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -210,28 +210,38 @@ object ProtoTypes {
210210

211211
trait ApplyingProto extends ProtoType
212212

213+
class FunProtoState {
214+
215+
/** The list of typed arguments, if all arguments are typed */
216+
var typedArgs: List[Tree] = Nil
217+
218+
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
219+
var typedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty
220+
221+
/** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */
222+
var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty
223+
224+
/** The tupled version of this prototype, if it has been computed */
225+
var tupled: Type = NoType
226+
227+
/** If true, the application of this prototype was canceled. */
228+
var toDrop: Boolean = false
229+
}
230+
213231
/** A prototype for expressions that appear in function position
214232
*
215233
* [](args): resultType
216234
*/
217-
case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context)
235+
case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer, state: FunProtoState = new FunProtoState)(implicit ctx: Context)
218236
extends UncachedGroundType with ApplyingProto {
219-
private[this] var myTypedArgs: List[Tree] = Nil
220-
221237
override def resultType(implicit ctx: Context) = resType
222238

223-
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
224-
private[this] var myTypedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty
225-
226-
/** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */
227-
private[this] var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty
228-
229239
def isMatchedBy(tp: Type)(implicit ctx: Context) =
230240
typer.isApplicable(tp, Nil, unforcedTypedArgs, resultType)
231241

232242
def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) =
233243
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
234-
else new FunProto(args, resultType, typer)
244+
else new FunProto(args, resultType)(typer)
235245

236246
override def notApplied = WildcardType
237247

@@ -244,19 +254,19 @@ object ProtoTypes {
244254
* @return True if all arguments have types (in particular, no types were forgotten).
245255
*/
246256
def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = {
247-
evalState foreachBinding { (arg, tstateConstr) =>
257+
state.evalState foreachBinding { (arg, tstateConstr) =>
248258
if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) ||
249259
tstateConstr._2.isRetracted) {
250-
typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}")
251-
myTypedArg = myTypedArg.remove(arg)
252-
evalState = evalState.remove(arg)
260+
typr.println(i"need to invalidate $arg / ${state.typedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}")
261+
state.typedArg = state.typedArg.remove(arg)
262+
state.evalState = state.evalState.remove(arg)
253263
}
254264
}
255-
myTypedArg.size == args.length
265+
state.typedArg.size == args.length
256266
}
257267

258268
private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(implicit ctx: Context): Tree = {
259-
var targ = myTypedArg(arg)
269+
var targ = state.typedArg(arg)
260270
if (targ == null) {
261271
if (!force && untpd.functionWithUnknownParamType(arg).isDefined)
262272
// If force = false, assume ? rather than reporting an error.
@@ -270,8 +280,8 @@ object ProtoTypes {
270280
// typerstate if there are no errors. If we also omitted the next two lines
271281
// when warning were emitted, `pos/t1756.scala` would fail when run with -feature.
272282
// It would produce an orphan type parameter for CI when pickling.
273-
myTypedArg = myTypedArg.updated(arg, targ)
274-
evalState = evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint))
283+
state.typedArg = state.typedArg.updated(arg, targ)
284+
state.evalState = state.evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint))
275285
}
276286
}
277287
}
@@ -285,9 +295,9 @@ object ProtoTypes {
285295
* "missing parameter type" error
286296
*/
287297
private def typedArgs(force: Boolean): List[Tree] = {
288-
if (myTypedArgs.size != args.length)
289-
myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_), force))
290-
myTypedArgs
298+
if (state.typedArgs.size != args.length)
299+
state.typedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_), force))
300+
state.typedArgs
291301
}
292302

293303
def typedArgs: List[Tree] = typedArgs(force = true)
@@ -306,24 +316,19 @@ object ProtoTypes {
306316
* @pre `arg` has been typed before
307317
*/
308318
def typeOfArg(arg: untpd.Tree)(implicit ctx: Context): Type =
309-
myTypedArg(arg).tpe
310-
311-
private[this] var myTupled: Type = NoType
319+
state.typedArg(arg).tpe
312320

313321
/** The same proto-type but with all arguments combined in a single tuple */
314-
def tupled: FunProto = myTupled match {
322+
def tupled: FunProto = state.tupled match {
315323
case pt: FunProto =>
316324
pt
317325
case _ =>
318-
myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
326+
state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer)
319327
tupled
320328
}
321329

322330
/** Somebody called the `tupled` method of this prototype */
323-
def isTupled: Boolean = myTupled.isInstanceOf[FunProto]
324-
325-
/** If true, the application of this prototype was canceled. */
326-
private[this] var toDrop: Boolean = false
331+
def isTupled: Boolean = state.tupled.isInstanceOf[FunProto]
327332

328333
/** Cancel the application of this prototype. This can happen for a nullary
329334
* application `f()` if `f` refers to a symbol that exists both in parameterless
@@ -333,10 +338,10 @@ object ProtoTypes {
333338
*/
334339
def markAsDropped() = {
335340
assert(args.isEmpty)
336-
toDrop = true
341+
state.toDrop = true
337342
}
338343

339-
def isDropped: Boolean = toDrop
344+
def isDropped: Boolean = state.toDrop
340345

341346
override def toString = s"FunProto(${args mkString ","} => $resultType)"
342347

@@ -347,15 +352,19 @@ object ProtoTypes {
347352
ta(ta.foldOver(x, typedArgs.tpes), resultType)
348353

349354
override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer)
350-
}
351355

356+
override def withContext(newCtx: Context) =
357+
if (newCtx `eq` ctx) this
358+
else new FunProto(args, resType)(typer, state)(newCtx)
359+
}
352360

353361
/** A prototype for expressions that appear in function position
354362
*
355363
* [](args): resultType, where args are known to be typed
356364
*/
357-
class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) {
365+
class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType)(typer)(ctx) {
358366
override def typedArgs = args
367+
override def withContext(ctx: Context) = this
359368
}
360369

361370
/** A prototype for implicitly inferred views:
@@ -392,7 +401,7 @@ object ProtoTypes {
392401
}
393402

394403
class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
395-
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)
404+
untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType)(typer)
396405

397406
/** A prototype for expressions [] that are type-parameterized:
398407
*

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ class Typer extends Namer
870870
expr1.tpe
871871
case _ =>
872872
val protoArgs = args map (_ withType WildcardType)
873-
val callProto = FunProto(protoArgs, WildcardType, this)
873+
val callProto = FunProto(protoArgs, WildcardType)(this)
874874
val expr1 = typedExpr(expr, callProto)
875875
fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
876876
expr1.tpe
@@ -2043,17 +2043,18 @@ class Typer extends Namer
20432043
}
20442044

20452045
def tryApply(implicit ctx: Context) = {
2046-
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
2046+
val pt1 = pt.withContext(ctx)
2047+
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt1)
20472048
sel.pushAttachment(InsertedApply, ())
20482049
if (sel.tpe.isError) sel
2049-
else try adapt(simplify(sel, pt, locked), pt, locked) finally sel.removeAttachment(InsertedApply)
2050+
else try adapt(simplify(sel, pt1, locked), pt1, locked) finally sel.removeAttachment(InsertedApply)
20502051
}
20512052

20522053
def tryImplicit(fallBack: => Tree) =
2053-
tryInsertImplicitOnQualifier(tree, pt, locked).getOrElse(fallBack)
2054+
tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked).getOrElse(fallBack)
20542055

20552056
pt match {
2056-
case pt @ FunProto(Nil, _, _)
2057+
case pt @ FunProto(Nil, _)
20572058
if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) &&
20582059
tree.getAttachment(DroppedEmptyArgs).isEmpty =>
20592060
tree.putAttachment(DroppedEmptyArgs, ())

compiler/test/dotc/pos-from-tasty.blacklist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ annot-bootstrap.scala
1212

1313
# ScalaRunTime cannot be unpickled because it is already loaded
1414
repeatedArgs213.scala
15+
16+
# Error printing parent constructors that are blocks
17+
default-super.scala

tests/pos/default-super.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class A(x: Int)(y: String = "hi")
2+
3+
object Test {
4+
def f() = 22
5+
6+
class B extends A(f())()
7+
8+
}

tests/pos/i4466a.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Foo {
2+
def apply(bar: Bar[Int]): Unit = ()
3+
def apply(i: Int): Unit = ()
4+
}
5+
6+
class Bar[X](x: X)
7+
8+
class Main {
9+
val foo = new Foo
10+
foo(new Bar(0))
11+
}

tests/pos/i4466b.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Foo {
2+
case class Bar(map: Map[String, String])
3+
4+
object Bar {
5+
def apply(str: String): Bar = ???
6+
}
7+
8+
Bar(Map("A" -> "B"))
9+
}

0 commit comments

Comments
 (0)