Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,9 @@ class Inliner(val call: tpd.Tree)(using Context):
sym.info = rhs.tpe
untpd.cpy.ValDef(vdef)(vdef.name, untpd.TypeTree(rhs.tpe), untpd.TypedSplice(rhs))
else vdef
super.typedValDef(vdef1, sym)
val typedVdef = super.typedValDef(vdef1, sym)
transform.InlineVals.checkInlineConformant(typedVdef.asInstanceOf[ValDef])
typedVdef

override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree =
val locked = ctx.typerState.ownedVars
Expand Down Expand Up @@ -1056,9 +1058,32 @@ class Inliner(val call: tpd.Tree)(using Context):
val meth = tree.symbol
if meth.isAllOf(DeferredInline) then
errorTree(tree, em"Deferred inline ${meth.showLocated} cannot be invoked")
else if Inlines.needsInlining(tree) then Inlines.inlineCall(simplify(tree, pt, locked))
else if Inlines.needsInlining(tree) then
safeStripTyped(Inlines.inlineCall(simplify(tree, pt, locked)))
else tree

/** Strips ascriptions, recursing into `Inlined` and `Block` nodes.
*
* Only strips `Typed` nodes that are safe to remove, i.e. where the
* expression type is a subtype of the expected type, and not `Nothing` or
* `Null`.
*/
private def safeStripTyped(tree: Tree)(using Context): Tree =
def canRemoveTyped(expr: Tree, pt: Type): Boolean =
(expr.tpe frozen_<:< pt)
&& !expr.tpe.isNothingType
&& !expr.tpe.isNullType

unsplice(tree) match
case Typed(expr, _) if canRemoveTyped(expr, tree.tpe) =>
safeStripTyped(expr)
case tree @ Inlined(call, bindings, expr) =>
cpy.Inlined(tree)(call, bindings, safeStripTyped(expr))
case Block(stats, expr) =>
cpy.Block(tree)(stats, safeStripTyped(expr))
case _ =>
tree

override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree =
super.typedUnadapted(tree, pt, locked) match
case member: MemberDef => member.setDefTree
Expand Down
16 changes: 8 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/InlineVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import core.*
import Contexts.*, Decorators.*, Symbols.*, Flags.*, Types.*
import MegaPhase.MiniPhase
import inlines.Inlines
import ast.tpd
import ast.tpd.*


/** Check that `tree.rhs` can be right hand-side of an `inline` value definition. */
class InlineVals extends MiniPhase:
import ast.tpd.*
import InlineVals.checkInlineConformant

override def phaseName: String = InlineVals.name

Expand All @@ -27,8 +27,12 @@ class InlineVals extends MiniPhase:
checkInlineConformant(tree)
tree

object InlineVals:
val name: String = "inlineVals"
val description: String = "check right hand-sides of an `inline val`s"

/** Check that `tree.rhs` can be right hand-side of an `inline` value definition. */
private def checkInlineConformant(tree: ValDef)(using Context): Unit = {
def checkInlineConformant(tree: ValDef)(using Context): Unit = {
if tree.symbol.is(Inline, butNot = DeferredOrTermParamOrAccessor)
&& !Inlines.inInlineMethod
then
Expand All @@ -38,7 +42,7 @@ class InlineVals extends MiniPhase:
case tp: ConstantType =>
if !isPureExpr(rhs) then
def details =
if enclosingInlineds.nonEmpty || rhs.isInstanceOf[tpd.Inlined]
if enclosingInlineds.nonEmpty || rhs.isInstanceOf[Inlined]
then i" but was: $rhs"
else ""
report.error(em"inline value must be pure$details", rhs.srcPos)
Expand All @@ -55,7 +59,3 @@ class InlineVals extends MiniPhase:
else
report.error(em"inline value must contain a literal constant value.\n\nTo inline more complex types consider using `inline def`", rhs)
}

object InlineVals:
val name: String = "inlineVals"
val description: String = "check right hand-sides of an `inline val`s"
22 changes: 22 additions & 0 deletions tests/neg/i24412-non-constant-inline-val.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Error: tests/neg/i24412-non-constant-inline-val.scala:9:3 -----------------------------------------------------------
9 | g() // error // error
| ^^^
| inline value must have a literal constant type
|---------------------------------------------------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i24412-non-constant-inline-val.scala:5
5 | inline val x = f()
| ^^^
---------------------------------------------------------------------------------------------------------------------
-- Error: tests/neg/i24412-non-constant-inline-val.scala:9:3 -----------------------------------------------------------
9 | g() // error // error
| ^^^
| Cannot reduce `inline if` because its condition is not a constant value: x > 0
|---------------------------------------------------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from i24412-non-constant-inline-val.scala:6
6 | inline if x > 0 then 1 else 0
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
---------------------------------------------------------------------------------------------------------------------
9 changes: 9 additions & 0 deletions tests/neg/i24412-non-constant-inline-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
inline def f(): Int =
Math.random().toInt

inline def g(): Int =
inline val x = f()
inline if x > 0 then 1 else 0

@main def Test: Unit =
g() // error // error
13 changes: 13 additions & 0 deletions tests/pos/i24412.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
object test {
import scala.compiletime.erasedValue

inline def contains[T <: Tuple, E]: Boolean = inline erasedValue[T] match {
case _: EmptyTuple => false
case _: (_ *: tail) => contains[tail, E]
}
inline def check[T <: Tuple]: Unit = {
inline if contains[T, Long] && false then ???
}

check[(String, Double)]
}
12 changes: 12 additions & 0 deletions tests/run/i24420-inline-local-ref.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
inline def f(): Long =
1L

inline def g(): Long =
inline val x = f()
x

inline def h(): Long =
inline if g() > 0L then 1L else 0L

@main def Test: Unit =
assert(h() == 1L)
25 changes: 25 additions & 0 deletions tests/run/i24420-inline-val.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
inline def f1(): Long =
1L

inline def f2(): Long =
inline val x = f1() + 1L
x

inline def f3(): Long =
inline val x = f1()
x

inline def g1(): Boolean =
true

inline def g2(): Long =
inline if g1() then 1L else 2L

inline def g3(): Long =
inline if f1() > 0L then 1L else 2L

@main def Test: Unit =
assert(f2() == 2L)
assert(f3() == 1L)
assert(g2() == 1L)
assert(g3() == 1L)
12 changes: 12 additions & 0 deletions tests/run/i24420-transparent-inline-local-ref.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
transparent inline def f(): Long =
1L

transparent inline def g(): Long =
inline val x = f()
x

transparent inline def h(): Long =
inline if g() > 0L then 1L else 0L

@main def Test: Unit =
assert(h() == 1L)
Loading