diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 356e5ad40fdd..d4781f760015 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -1047,6 +1047,20 @@ class Inliner(val call: tpd.Tree)(using Context): reduceInlineMatchExpr(sel) } + private def shouldStripAscription(tree: Typed)(using Context): Boolean = + val exprTp = tree.expr.tpe + tree.hasAttachment(PrepareInlineable.InlineResultAscription) + && enclosingInlineds.size > 1 + && exprTp.exists + && !exprTp.widen.isRef(defn.NothingClass) + && !exprTp.widen.isRef(defn.NullClass) + && (exprTp frozen_<:< tree.tpe) + + override def typedTyped(tree: untpd.Typed, pt: Type)(using Context): Tree = + super.typedTyped(tree, pt) match + case typedTree: Typed if shouldStripAscription(typedTree) => typedTree.expr + case typedTree => typedTree + override def newLikeThis(nestingLevel: Int): Typer = new InlineTyper(initialErrorCount, nestingLevel) /** True if this inline typer has already issued errors */ diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 2dd86132fb97..a21912439051 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -134,9 +134,9 @@ object Inlines: * inline call expansions smaller. */ def liftBindings(tree: Tree, liftPos: Tree => Tree): Tree = tree match { - case Block(stats, expr) => - bindings ++= stats.map(liftPos) - liftBindings(expr, liftPos) + //case Block(stats, expr) => + // bindings ++= stats.map(liftPos) + // liftBindings(expr, liftPos) case tree @ Inlined(call, stats, expr) => bindings ++= stats.map(liftPos) val lifter = liftFromInlined(call) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 47a47f10f905..bd4b6a3530c0 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -30,6 +30,11 @@ object PrepareInlineable { private val InlineAccessorsKey = new Property.Key[InlineAccessors] + /** Indicates that an ascription was inserted by [[PrepareInlinable.wrapRHS]]. + * It is used to remove it [[Inliner.stripInlineResultAscription]]. + */ + val InlineResultAscription = new Property.StickyKey[Unit] + def initContext(ctx: Context): Context = ctx.fresh.setProperty(InlineAccessorsKey, new InlineAccessors) @@ -246,7 +251,10 @@ object PrepareInlineable { /** The type ascription `rhs: tpt`, unless `original` is `transparent`. */ def wrapRHS(original: untpd.DefDef, tpt: Tree, rhs: Tree)(using Context): Tree = - if original.mods.is(Transparent) then rhs else Typed(rhs, tpt) + if original.mods.is(Transparent) then + rhs + else + Typed(rhs, tpt).withAttachment(InlineResultAscription, ()) /** Return result of evaluating `op`, but drop `Inline` flag and `Body` annotation * of `sym` in case that leads to errors. diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 8400639706d6..20b0fd93737b 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -58,6 +58,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedTyped(tree: untpd.Typed, pt: Type)(using Context): Tree = { assertTyped(tree) + if tree.hasAttachment(Typer.NoLocalRefsAscription) then + return typedExpr(tree.expr, pt) + val tpt1 = checkSimpleKinded(typedType(tree.tpt)) val expr1 = tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && untpd.isVarPattern(id) && (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 164ff411e73b..29ad072b8e8f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -86,6 +86,9 @@ object Typer { /** Indicates that an expression is explicitly ascribed to [[Unit]] type. */ val AscribedToUnit = new Property.StickyKey[Unit] + /** Indicates that this ascription was inserted by `ensureNoLocalRefs`. */ + val NoLocalRefsAscription = new Property.StickyKey[Unit] + /** Tree adaptation lost fidelity; this attachment preserves the original tree. */ val AdaptedTree = new Property.StickyKey[tpd.Tree] @@ -1580,7 +1583,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => val target = pt.simplified val targetTpt = TypeTree(target, inferred = true) - if tree.tpe <:< target then Typed(tree, targetTpt) + if tree.tpe <:< target then Typed(tree, targetTpt).withAttachment(NoLocalRefsAscription, ()) else // This case should not normally arise. It currently does arise in test cases // pos/t4080b.scala and pos/i7067.scala. In that case, a type ascription is wrong diff --git a/tests/pos/i24412.scala b/tests/pos/i24412.scala new file mode 100644 index 000000000000..3b9ec8d5a579 --- /dev/null +++ b/tests/pos/i24412.scala @@ -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)] +} diff --git a/tests/run/i24420-inline-local-ref.scala b/tests/run/i24420-inline-local-ref.scala new file mode 100644 index 000000000000..84828de9f443 --- /dev/null +++ b/tests/run/i24420-inline-local-ref.scala @@ -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) diff --git a/tests/run/i24420-inline-val.scala b/tests/run/i24420-inline-val.scala new file mode 100644 index 000000000000..ec201d948a96 --- /dev/null +++ b/tests/run/i24420-inline-val.scala @@ -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) diff --git a/tests/run/i24420-transparent-inline-local-ref.scala b/tests/run/i24420-transparent-inline-local-ref.scala new file mode 100644 index 000000000000..7d8180680130 --- /dev/null +++ b/tests/run/i24420-transparent-inline-local-ref.scala @@ -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)