Skip to content

Commit 87e76a9

Browse files
committed
Handle generic tuples when inlining
Fixes #14182
1 parent 355d2f6 commit 87e76a9

File tree

7 files changed

+34
-3
lines changed

7 files changed

+34
-3
lines changed

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,18 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
486486
}
487487
val argtpe = arg.tpe.dealiasKeepAnnots.translateFromRepeated(toArray = false)
488488
val argIsBottom = argtpe.isBottomTypeAfterErasure
489-
val bindingType =
489+
val argwtpe =
490490
if argIsBottom then formal
491-
else if isByName then ExprType(argtpe.widen)
491+
else if defn.isTupleNType(formal.widenExpr) && !defn.isTupleNType(argtpe.widen) then
492+
// A generic tuple with know size N is considered as a subtype of TupleN but it does not
493+
// contain the members of TupleN. All these tuples will be erased to TupleN and will have
494+
// those members at runtime. Therefore when inlining it is important to keep the fact that
495+
// this is TupleN and that the members can be accessed when the tree is re-typechecked.
496+
// Also see `newArg` bellow
497+
// See i14182
498+
argtpe.widen & formal.widenExpr
492499
else argtpe.widen
500+
val bindingType = if isByName then ExprType(argwtpe) else argwtpe
493501
var bindingFlags: FlagSet = InlineProxy
494502
if formal.widenExpr.hasAnnotation(defn.InlineParamAnnot) then
495503
bindingFlags |= Inline
@@ -500,8 +508,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
500508
val boundSym = newSym(InlineBinderName.fresh(name.asTermName), bindingFlags, bindingType).asTerm
501509
val binding = {
502510
var newArg = arg.changeOwner(ctx.owner, boundSym)
503-
if bindingFlags.is(Inline) && argIsBottom then
511+
if argIsBottom && bindingFlags.is(Inline) then
504512
newArg = Typed(newArg, TypeTree(formal)) // type ascribe RHS to avoid type errors in expansion. See i8612.scala
513+
else if defn.isTupleNType(formal.widenExpr) && !defn.isTupleNType(argtpe.widen) then
514+
// Also see `argtpe1` above
515+
newArg = Typed(newArg, TypeTree(formal.widenExpr))
516+
505517
if isByName then DefDef(boundSym, newArg)
506518
else ValDef(boundSym, newArg)
507519
}.withSpan(boundSym.span)

tests/pos-macros/i14182/Macro_1.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import scala.quoted._
2+
def fooImpl(xs: Expr[(Int, Int)])(using Quotes): Expr[Unit] =
3+
'{ val a: Int = $xs._1; }

tests/pos-macros/i14182/Test_2.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
inline def foo(inline xs: (Int, Int)): Unit = ${ fooImpl('xs) }
2+
def fail = foo(1 *: 2 *: EmptyTuple)
3+
def ok = foo((1, 2))

tests/pos/i14182.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
inline def foo(inline xs: (Int, Int)): Unit = { val a: Int = xs._1; }
2+
def fail = foo(1 *: 2 *: EmptyTuple)
3+
def ok = foo((1, 2))

tests/pos/i14182a.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
inline def foo(xs: (Int, Int)): Unit = { val a: Int = xs._1; }
2+
def fail = foo(1 *: 2 *: EmptyTuple)
3+
def ok = foo((1, 2))

tests/pos/i14182b.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
inline def foo(inline xs: (Int, Int)): Unit = { val a: Int = xs._1; }
2+
def bar =
3+
val tup: 1 *: 2 *: EmptyTuple = ???
4+
foo(tup)

tests/pos/i14182c.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
inline def foo(xs: => (Int, Int)): Unit = { val a: Int = xs._1; }
2+
def bar =
3+
foo(1 *: 2 *: EmptyTuple)

0 commit comments

Comments
 (0)