diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 7be5b53515a5..2e1b674b895e 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -85,6 +85,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => tree } + /** If this is a block, its expression part */ + def stripBlock(tree: Tree): Tree = unsplice(tree) match { + case Block(_, expr) => stripBlock(expr) + case _ => tree + } + /** The number of arguments in an application */ def numArgs(tree: Tree): Int = unsplice(tree) match { case Apply(fn, args) => numArgs(fn) + args.length diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 1facbdfc6690..203ab7624835 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -981,7 +981,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = { val typer = ctx.typer - val proto = new FunProtoTyped(args, expectedType, typer) + val proto = new FunProtoTyped(args, expectedType)(typer) val denot = receiver.tpe.member(method) assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}") val selected = diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 948c0bfc1606..e9d2c393ba1c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1593,6 +1593,11 @@ object Types { def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T def map(tm: TypeMap)(implicit ctx: Context): ProtoType + + /** If this prototype captures a context, the same prototype except that the result + * captures the given context `ctx`. + */ + def withContext(ctx: Context): ProtoType = this } /** Implementations of this trait cache the results of `narrow`. */ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 79227028e1e1..31eccffe7348 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -855,7 +855,7 @@ class TreeUnpickler(reader: TastyReader, val vparams = readIndexedParams[ValDef](PARAM) val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { nextUnsharedTag match { - case APPLY | TYPEAPPLY => readTerm()(parentCtx) + case APPLY | TYPEAPPLY | BLOCK => readTerm()(parentCtx) case _ => readTpt()(parentCtx) } } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9990d1d9ea8f..7335e7458ada 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -208,7 +208,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ": " ~ toText(tp.memberProto) ~ " }" case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) - case tp @ FunProto(args, resultType, _) => + case tp @ FunProto(args, resultType) => val argsText = args match { case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp) case _ => toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 88ac3e9900bc..355994aeee77 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -500,7 +500,7 @@ object Erasure { val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else typedExpr(fun, FunProto(args, pt, this)) match { + else typedExpr(fun, FunProto(args, pt)(this)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 case fun1 => diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index c17ccb835795..672b6d0d625b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -168,15 +168,19 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => * to be used as initializers of trait parameters if the target of the call * is a trait. */ - def transformConstructor(tree: Tree): (Tree, List[Tree]) = { - val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree - val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) - (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) + def transformConstructor(tree: Tree): (Tree, List[Tree]) = tree match { + case Block(stats, expr) => + val (scall, inits) = transformConstructor(expr) + (cpy.Block(tree)(stats, scall), inits) + case _ => + val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree + val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) + (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) } val superCallsAndArgs = ( - for (p <- impl.parents if p.symbol.isConstructor) - yield p.symbol.owner -> transformConstructor(p) + for (p <- impl.parents; constr = stripBlock(p).symbol if constr.isConstructor) + yield constr.owner -> transformConstructor(p) ).toMap val superCalls = superCallsAndArgs.mapValues(_._1) val initArgs = superCallsAndArgs.mapValues(_._2) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index c74c0a1d05c8..d1c62c8cebc9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -690,7 +690,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { def realApply(implicit ctx: Context): Tree = track("realApply") { - val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree)) + val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this)(argCtx(tree)) val fun1 = typedExpr(tree.fun, originalProto) // 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 => alts filter (isApplicable(_, argTypes, resultType)) val candidates = pt match { - case pt @ FunProto(args, resultType, _) => + case pt @ FunProto(args, resultType) => val numArgs = args.length val normArgs = args.mapConserve { case Block(Nil, expr) => expr diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index a1cd253a26fd..3598db12ad1a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -897,7 +897,7 @@ trait Implicits { self: Typer => lazy val funProto = fullProto match { case proto: ViewProto => - FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self) + FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType)(self) case proto => proto } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index e592d3b2517d..199da3747b7c 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -210,28 +210,38 @@ object ProtoTypes { trait ApplyingProto extends ProtoType + class FunProtoState { + + /** The list of typed arguments, if all arguments are typed */ + var typedArgs: List[Tree] = Nil + + /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ + var typedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty + + /** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */ + var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty + + /** The tupled version of this prototype, if it has been computed */ + var tupled: Type = NoType + + /** If true, the application of this prototype was canceled. */ + var toDrop: Boolean = false + } + /** A prototype for expressions that appear in function position * * [](args): resultType */ - case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context) + case class FunProto(args: List[untpd.Tree], resType: Type)(typer: Typer, state: FunProtoState = new FunProtoState)(implicit ctx: Context) extends UncachedGroundType with ApplyingProto { - private[this] var myTypedArgs: List[Tree] = Nil - override def resultType(implicit ctx: Context) = resType - /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */ - private[this] var myTypedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty - - /** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */ - private[this] var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty - def isMatchedBy(tp: Type)(implicit ctx: Context) = typer.isApplicable(tp, Nil, unforcedTypedArgs, resultType) def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) = if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this - else new FunProto(args, resultType, typer) + else new FunProto(args, resultType)(typer) override def notApplied = WildcardType @@ -244,19 +254,19 @@ object ProtoTypes { * @return True if all arguments have types (in particular, no types were forgotten). */ def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = { - evalState foreachBinding { (arg, tstateConstr) => + state.evalState foreachBinding { (arg, tstateConstr) => if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) || tstateConstr._2.isRetracted) { - typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}") - myTypedArg = myTypedArg.remove(arg) - evalState = evalState.remove(arg) + typr.println(i"need to invalidate $arg / ${state.typedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}") + state.typedArg = state.typedArg.remove(arg) + state.evalState = state.evalState.remove(arg) } } - myTypedArg.size == args.length + state.typedArg.size == args.length } private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(implicit ctx: Context): Tree = { - var targ = myTypedArg(arg) + var targ = state.typedArg(arg) if (targ == null) { if (!force && untpd.functionWithUnknownParamType(arg).isDefined) // If force = false, assume ? rather than reporting an error. @@ -270,8 +280,8 @@ object ProtoTypes { // typerstate if there are no errors. If we also omitted the next two lines // when warning were emitted, `pos/t1756.scala` would fail when run with -feature. // It would produce an orphan type parameter for CI when pickling. - myTypedArg = myTypedArg.updated(arg, targ) - evalState = evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint)) + state.typedArg = state.typedArg.updated(arg, targ) + state.evalState = state.evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint)) } } } @@ -285,9 +295,9 @@ object ProtoTypes { * "missing parameter type" error */ private def typedArgs(force: Boolean): List[Tree] = { - if (myTypedArgs.size != args.length) - myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_), force)) - myTypedArgs + if (state.typedArgs.size != args.length) + state.typedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_), force)) + state.typedArgs } def typedArgs: List[Tree] = typedArgs(force = true) @@ -306,24 +316,19 @@ object ProtoTypes { * @pre `arg` has been typed before */ def typeOfArg(arg: untpd.Tree)(implicit ctx: Context): Type = - myTypedArg(arg).tpe - - private[this] var myTupled: Type = NoType + state.typedArg(arg).tpe /** The same proto-type but with all arguments combined in a single tuple */ - def tupled: FunProto = myTupled match { + def tupled: FunProto = state.tupled match { case pt: FunProto => pt case _ => - myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer) + state.tupled = new FunProto(untpd.Tuple(args) :: Nil, resultType)(typer) tupled } /** Somebody called the `tupled` method of this prototype */ - def isTupled: Boolean = myTupled.isInstanceOf[FunProto] - - /** If true, the application of this prototype was canceled. */ - private[this] var toDrop: Boolean = false + def isTupled: Boolean = state.tupled.isInstanceOf[FunProto] /** Cancel the application of this prototype. This can happen for a nullary * application `f()` if `f` refers to a symbol that exists both in parameterless @@ -333,10 +338,10 @@ object ProtoTypes { */ def markAsDropped() = { assert(args.isEmpty) - toDrop = true + state.toDrop = true } - def isDropped: Boolean = toDrop + def isDropped: Boolean = state.toDrop override def toString = s"FunProto(${args mkString ","} => $resultType)" @@ -347,15 +352,19 @@ object ProtoTypes { ta(ta.foldOver(x, typedArgs.tpes), resultType) override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer) - } + override def withContext(newCtx: Context) = + if (newCtx `eq` ctx) this + else new FunProto(args, resType)(typer, state)(newCtx) + } /** A prototype for expressions that appear in function position * * [](args): resultType, where args are known to be typed */ - class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) { + class FunProtoTyped(args: List[tpd.Tree], resultType: Type)(typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType)(typer)(ctx) { override def typedArgs = args + override def withContext(ctx: Context) = this } /** A prototype for implicitly inferred views: @@ -392,7 +401,7 @@ object ProtoTypes { } class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer) + untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType)(typer) /** A prototype for expressions [] that are type-parameterized: * diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b367146e3ffc..ae48268483b9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -870,7 +870,7 @@ class Typer extends Namer expr1.tpe case _ => val protoArgs = args map (_ withType WildcardType) - val callProto = FunProto(protoArgs, WildcardType, this) + val callProto = FunProto(protoArgs, WildcardType)(this) val expr1 = typedExpr(expr, callProto) fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) expr1.tpe @@ -2043,17 +2043,18 @@ class Typer extends Namer } def tryApply(implicit ctx: Context) = { - val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) + val pt1 = pt.withContext(ctx) + val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt1) sel.pushAttachment(InsertedApply, ()) if (sel.tpe.isError) sel - else try adapt(simplify(sel, pt, locked), pt, locked) finally sel.removeAttachment(InsertedApply) + else try adapt(simplify(sel, pt1, locked), pt1, locked) finally sel.removeAttachment(InsertedApply) } def tryImplicit(fallBack: => Tree) = - tryInsertImplicitOnQualifier(tree, pt, locked).getOrElse(fallBack) + tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked).getOrElse(fallBack) pt match { - case pt @ FunProto(Nil, _, _) + case pt @ FunProto(Nil, _) if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) && tree.getAttachment(DroppedEmptyArgs).isEmpty => tree.putAttachment(DroppedEmptyArgs, ()) diff --git a/compiler/test/dotc/pos-from-tasty.blacklist b/compiler/test/dotc/pos-from-tasty.blacklist index 80e74dfe27c1..a78823e99acd 100644 --- a/compiler/test/dotc/pos-from-tasty.blacklist +++ b/compiler/test/dotc/pos-from-tasty.blacklist @@ -12,3 +12,6 @@ annot-bootstrap.scala # ScalaRunTime cannot be unpickled because it is already loaded repeatedArgs213.scala + +# Error printing parent constructors that are blocks +default-super.scala diff --git a/tests/pos/default-super.scala b/tests/pos/default-super.scala new file mode 100644 index 000000000000..572a33645079 --- /dev/null +++ b/tests/pos/default-super.scala @@ -0,0 +1,8 @@ +class A(x: Int)(y: String = "hi") + +object Test { + def f() = 22 + + class B extends A(f())() + +} \ No newline at end of file diff --git a/tests/pos/i4466a.scala b/tests/pos/i4466a.scala new file mode 100644 index 000000000000..0e6caceaedb4 --- /dev/null +++ b/tests/pos/i4466a.scala @@ -0,0 +1,11 @@ +class Foo { + def apply(bar: Bar[Int]): Unit = () + def apply(i: Int): Unit = () +} + +class Bar[X](x: X) + +class Main { + val foo = new Foo + foo(new Bar(0)) +} \ No newline at end of file diff --git a/tests/pos/i4466b.scala b/tests/pos/i4466b.scala new file mode 100644 index 000000000000..659d3e036625 --- /dev/null +++ b/tests/pos/i4466b.scala @@ -0,0 +1,9 @@ +object Foo { + case class Bar(map: Map[String, String]) + + object Bar { + def apply(str: String): Bar = ??? + } + + Bar(Map("A" -> "B")) +}