Skip to content

Commit

Permalink
Fix scala#3971: Relax checkInlineConformant for final vals
Browse files Browse the repository at this point in the history
Necessary for -Ycheck:frontend to work
  • Loading branch information
smarter committed Apr 17, 2018
1 parent a768e39 commit 72b247b
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 11 deletions.
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(_, _)
Expand All @@ -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(_, _)
Expand Down Expand Up @@ -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.
Expand Down
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import StdNames._
import NameOps._
import Symbols._
import Trees._
import TreeInfo._
import ProtoTypes._
import Constants._
import Scopes._
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = {
Expand Down Expand Up @@ -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 &&
Expand Down
3 changes: 3 additions & 0 deletions tests/pos/i3971.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object BadInlineConstCheck {
final val MaxSize = Int.MaxValue + 0
}

0 comments on commit 72b247b

Please sign in to comment.