From 3be4e8126da4bf939f4486c78a95550fedca812c Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 24 Mar 2018 00:01:41 +0100 Subject: [PATCH 1/8] Add pretty-printer for PolyProto --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0e6d98f2e3e4..6500693da068 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -218,6 +218,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return "FunProto(" ~ argsText ~ "):" ~ toText(resultType) case tp: IgnoredProto => return "?" + case tp @ PolyProto(targs, resType) => + return "PolyProto(" ~ toTextGlobal(targs, ", ") ~ "): " ~ toText(resType) case _ => } super.toText(tp) From a768e391560dc55a56c4a1f37d6b5d610ea7bf75 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 24 Mar 2018 00:01:45 +0100 Subject: [PATCH 2/8] TreeChecker#adapt: do not check PolyProto This should be safe like skipping FunProto since we'll check the type application result anyway. This is needed to get -Ycheck:frontend to work when named type parameters are used like in t1513a.scala --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index e6886b99e431..55d3727b1bd7 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -443,7 +443,8 @@ class TreeChecker extends Phase with SymTransformer { if (ctx.mode.isExpr && !tree.isEmpty && !isPrimaryConstructorReturn && - !pt.isInstanceOf[FunProto]) + !pt.isInstanceOf[FunProto] && + !pt.isInstanceOf[PolyProto]) assert(tree.tpe <:< pt, { val mismatch = err.typeMismatchMsg(tree.tpe, pt) i"""|${mismatch.msg} From 72b247bd7aace10c520d74055c2a2711af2cd045 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 24 Mar 2018 00:37:27 +0100 Subject: [PATCH 3/8] Fix #3971: Relax checkInlineConformant for final vals Necessary for -Ycheck:frontend to work --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Checking.scala | 14 +++++++++----- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- tests/pos/i3971.scala | 3 +++ 4 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 tests/pos/i3971.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index d1e91799f31a..3b710f17e531 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -317,7 +317,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * Idempotent if running the statement a second time has no side effects * Impure otherwise */ - private def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | TypeDef(_, _) | Import(_, _) @@ -342,7 +342,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * takes a different code path than all to follow; but they are idempotent * because running the expression a second time gives the cached result. */ - private def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | This(_) | Super(_, _) @@ -397,7 +397,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable * flags set. */ - private def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = + def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = if (!tree.tpe.widen.isParameterless || tree.symbol.is(Erased)) SimplyPure else if (!tree.symbol.isStable) Impure else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 698fdaff0c15..742d5c41942e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -13,6 +13,7 @@ import StdNames._ import NameOps._ import Symbols._ import Trees._ +import TreeInfo._ import ProtoTypes._ import Constants._ import Scopes._ @@ -601,17 +602,20 @@ trait Checking { } } - /** Check that `tree` is a pure expression of constant type */ - def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit = + /** Check that `tree` can be marked `inline` */ + def checkInlineConformant(tree: Tree, isFinal: Boolean, what: => String)(implicit ctx: Context): Unit = { + // final vals can be marked inline even if they're not pure, see Typer#patchFinalVals + val purityLevel = if (isFinal) Idempotent else Pure tree.tpe match { case tp: TermRef if tp.symbol.is(InlineParam) => // ok case tp => tp.widenTermRefExpr match { - case tp: ConstantType if isPureExpr(tree) => // ok - case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok + case tp: ConstantType if exprPurity(tree) >= purityLevel => // ok + case tp if defn.isFunctionType(tp) && exprPurity(tree) >= purityLevel => // ok case _ => if (!ctx.erasedTypes) ctx.error(em"$what must be a constant expression or a function", tree.pos) } } + } /** Check that class does not declare same symbol twice */ def checkNoDoubleDeclaration(cls: Symbol)(implicit ctx: Context): Unit = { @@ -867,7 +871,7 @@ trait NoChecking extends ReChecking { override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () override def checkFeasibleParent(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp - override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = () + override def checkInlineConformant(tree: Tree, isFinal: Boolean, what: => String)(implicit ctx: Context) = () override def checkNoDoubleDeclaration(cls: Symbol)(implicit ctx: Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 431721dc44e6..fcba55cf99d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1371,7 +1371,7 @@ class Typer extends Namer } val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) if (sym.is(Inline, butNot = DeferredOrTermParamOrAccessor)) - checkInlineConformant(rhs1, em"right-hand side of inline $sym") + checkInlineConformant(rhs1, isFinal = sym.is(Final), em"right-hand side of inline $sym") patchIfLazy(vdef1) patchFinalVals(vdef1) vdef1 @@ -1392,7 +1392,7 @@ class Typer extends Namer * and instead return the value. This seemingly minor optimization has huge effect on initialization * order and the values that can be observed during superconstructor call * - * see remark about idempotency in PostTyper#normalizeTree + * see remark about idempotency in TreeInfo#constToLiteral */ private def patchFinalVals(vdef: ValDef)(implicit ctx: Context): Unit = { def isFinalInlinableVal(sym: Symbol): Boolean = { @@ -2329,7 +2329,7 @@ class Typer extends Namer } else if (tree.tpe <:< pt) { if (pt.hasAnnotation(defn.InlineParamAnnot)) - checkInlineConformant(tree, "argument to inline parameter") + checkInlineConformant(tree, isFinal = false, "argument to inline parameter") if (Inliner.hasBodyToInline(tree.symbol) && !ctx.owner.ownersIterator.exists(_.isInlineMethod) && !ctx.settings.YnoInline.value && diff --git a/tests/pos/i3971.scala b/tests/pos/i3971.scala new file mode 100644 index 000000000000..d89d9c1b36f9 --- /dev/null +++ b/tests/pos/i3971.scala @@ -0,0 +1,3 @@ +object BadInlineConstCheck { + final val MaxSize = Int.MaxValue + 0 +} From bbea62e68216b7f487cea82c7389a40443ca6fdf Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sun, 25 Mar 2018 18:59:40 +0200 Subject: [PATCH 4/8] Reenable and fix -Ycheck of FirstTransform group It was accidentally disabled because elimJavaPackages was renamed. This required a change to `isWildcardStarArg` to handle retyping repeated arguments that have been lifted in Typer, for example: foo(1, 2, 3) might be lifted to: val args: Int* = [1, 2, 3] foo(args) --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 7 ++++--- compiler/test/dotty/tools/vulpix/TestConfiguration.scala | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 3b710f17e531..bd9195f77ab3 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -170,14 +170,15 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => false } - /** Is this argument node of the form : _* ? + /** Is this argument node of the form : _*, or is it a reference to + * such an argument ? The latter case can happen when an argument is lifted. */ def isWildcardStarArg(tree: Tree)(implicit ctx: Context): Boolean = unbind(tree) match { case Typed(Ident(nme.WILDCARD_STAR), _) => true case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true - case Typed(_, tpt: TypeTree) => tpt.hasType && tpt.tpe.isRepeatedParam + case Typed(_, tpt: TypeTree) => tpt.typeOpt.isRepeatedParam case NamedArg(_, arg) => isWildcardStarArg(arg) - case _ => false + case arg => arg.typeOpt.widen.isRepeatedParam } /** If this tree has type parameters, those. Otherwise Nil. diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 845908be2e0f..a3a449647d58 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -45,7 +45,7 @@ object TestConfiguration { } // Ideally should be Ycheck:all - val yCheckOptions = Array("-Ycheck:elimJavaPackages,refchecks,splitter,arrayConstructors,erasure,capturedVars,getClass,elimStaticThis,labelDef") + val yCheckOptions = Array("-Ycheck:firstTransform,refchecks,splitter,arrayConstructors,erasure,capturedVars,getClass,elimStaticThis,labelDef") val basicDefaultOptions = checkOptions ++ noCheckOptions ++ yCheckOptions val defaultUnoptimised = TestFlags(classPath, runClassPath, basicDefaultOptions) From aa5ece346097ae0f1f74efec106e328888b96e36 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 26 Mar 2018 00:44:43 +0200 Subject: [PATCH 5/8] Make -Ytest-pickler compatible with -Ycheck:all --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 55d3727b1bd7..862793852bcf 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -97,7 +97,10 @@ class TreeChecker extends Phase with SymTransformer { def phaseName: String = "Ycheck" def run(implicit ctx: Context): Unit = { - check(ctx.allPhases, ctx) + if (ctx.settings.YtestPickler.value && ctx.phase.prev.isInstanceOf[Pickler]) + ctx.echo("Skipping Ycheck after pickling with -Ytest-pickler, the returned tree contains stale symbols") + else + check(ctx.allPhases, ctx) } private def previousPhases(phases: List[Phase])(implicit ctx: Context): List[Phase] = phases match { From 5ac290f2c9794740d9188c2a26eeff3947eb51ee Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 29 Mar 2018 19:39:06 +0200 Subject: [PATCH 6/8] TreeChecker: allow _ in patterns to be typed as selection Needed to get tests/pos/infersingle.scala to pass -Ycheck:frontend --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 862793852bcf..b272676d6f3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -308,7 +308,7 @@ class TreeChecker extends Phase with SymTransformer { override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) - assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}") + assert(tree.isType || ctx.mode.is(Mode.Pattern) && untpd.isWildcardArg(tree) || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}") assertDefined(tree) checkNotRepeated(super.typedIdent(tree, pt)) From 953dc79b5db7f7e2d29ff672eac3175a9958f38b Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 9 Apr 2018 20:01:13 +0200 Subject: [PATCH 7/8] TreeChecker#transformSym: don't crash on Necessary to get -Ycheck:pickler to pass. --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index b272676d6f3b..8f700481f111 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -80,7 +80,8 @@ class TreeChecker extends Phase with SymTransformer { if (sym.isClass && !sym.isAbsent) { val validSuperclass = sym.isPrimitiveValueClass || defn.syntheticCoreClasses.contains(sym) || - (sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists) + (sym eq defn.ObjectClass) || (sym is NoSuperClass) || (sym.asClass.superClass.exists) || + sym.isRefinementClass assert(validSuperclass, i"$sym has no superclass set") testDuplicate(sym, seenClasses, "class") From 35c13d87f570249b954fe4e072fef8935fb10db6 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 26 Mar 2018 00:44:54 +0200 Subject: [PATCH 8/8] Enable -Ycheck:all --- compiler/test/dotty/tools/vulpix/TestConfiguration.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index a3a449647d58..486d968a24bb 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -45,7 +45,7 @@ object TestConfiguration { } // Ideally should be Ycheck:all - val yCheckOptions = Array("-Ycheck:firstTransform,refchecks,splitter,arrayConstructors,erasure,capturedVars,getClass,elimStaticThis,labelDef") + val yCheckOptions = Array("-Ycheck:all") val basicDefaultOptions = checkOptions ++ noCheckOptions ++ yCheckOptions val defaultUnoptimised = TestFlags(classPath, runClassPath, basicDefaultOptions)