Skip to content

Commit a3ad64e

Browse files
committed
Type ascribe arguments of bottom type to inline functions
Fixes scala#11225 Fixes scala#11864
1 parent 6c82605 commit a3ad64e

File tree

4 files changed

+94
-33
lines changed

4 files changed

+94
-33
lines changed

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

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -452,24 +452,28 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
452452
*/
453453
private def paramBindingDef(name: Name, paramtp: Type, arg0: Tree,
454454
bindingsBuf: mutable.ListBuffer[ValOrDefDef])(using Context): ValOrDefDef = {
455+
val isByName = paramtp.dealias.isInstanceOf[ExprType]
455456
val arg = arg0 match {
456457
case Typed(arg1, tpt) if tpt.tpe.isRepeatedParam && arg1.tpe.derivesFrom(defn.ArrayClass) =>
457458
wrapArray(arg1, arg0.tpe.elemType)
458459
case _ => arg0
459460
}
460461
val argtpe = arg.tpe.dealiasKeepAnnots.translateFromRepeated(toArray = false)
461-
val isByName = paramtp.dealias.isInstanceOf[ExprType]
462-
var inlineFlags: FlagSet = InlineProxy
463-
if (paramtp.widenExpr.hasAnnotation(defn.InlineParamAnnot)) inlineFlags |= Inline
464-
if (isByName) inlineFlags |= Method
465-
val (bindingFlags, bindingType) =
466-
if (isByName) (inlineFlags, ExprType(argtpe.widen))
467-
else (inlineFlags, argtpe.widen)
462+
val argIsBottom = argtpe.isBottomTypeAfterErasure
463+
val bindingType =
464+
if argIsBottom then paramtp.dealias
465+
else if isByName then ExprType(argtpe.widen)
466+
else argtpe
467+
var bindingFlags: FlagSet = InlineProxy
468+
if paramtp.widenExpr.hasAnnotation(defn.InlineParamAnnot) && !argIsBottom then
469+
bindingFlags |= Inline // Don't inline arguments of type Nothing or Null; they need to be type ascribed
470+
// to avoid type errors in the inlined code. See i8612.scala
471+
if isByName then
472+
bindingFlags |= Method
468473
val boundSym = newSym(InlineBinderName.fresh(name.asTermName), bindingFlags, bindingType).asTerm
469474
val binding = {
470475
val newArg = arg.changeOwner(ctx.owner, boundSym)
471-
if (isByName) DefDef(boundSym, newArg)
472-
else ValDef(boundSym, newArg)
476+
if isByName then DefDef(boundSym, newArg) else ValDef(boundSym, newArg)
473477
}.withSpan(boundSym.span)
474478
bindingsBuf += binding
475479
binding
@@ -479,30 +483,35 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
479483
* corresponding arguments. `bindingbuf` will be further extended later by
480484
* proxies to this-references. Issue an error if some arguments are missing.
481485
*/
482-
private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Boolean = tp match
483-
case tp: PolyType =>
484-
tp.paramNames.lazyZip(targs).foreach { (name, arg) =>
485-
paramSpan(name) = arg.span
486-
paramBinding(name) = arg.tpe.stripTypeVar
487-
}
488-
computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss)
489-
case tp: MethodType =>
490-
if argss.isEmpty then
491-
report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos)
492-
false
493-
else
494-
tp.paramNames.lazyZip(tp.paramInfos).lazyZip(argss.head).foreach { (name, paramtp, arg) =>
486+
private def computeParamBindings(
487+
tp: Type, targs: List[Tree], argss: List[List[Tree]], paramSubst: Type => Type): Boolean =
488+
tp match
489+
case tp: PolyType =>
490+
tp.paramNames.lazyZip(targs).foreach { (name, arg) =>
495491
paramSpan(name) = arg.span
496-
paramBinding(name) = arg.tpe.dealias match {
497-
case _: SingletonType if isIdempotentPath(arg) => arg.tpe
498-
case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol.termRef
499-
}
492+
paramBinding(name) = arg.tpe.stripTypeVar
500493
}
501-
computeParamBindings(tp.resultType, targs, argss.tail)
502-
case _ =>
503-
assert(targs.isEmpty)
504-
assert(argss.isEmpty)
505-
true
494+
computeParamBindings(
495+
tp.resultType, targs.drop(tp.paramNames.length), argss,
496+
paramSubst.andThen(_.substParams(tp, targs.map(_.tpe.stripTypeVar))))
497+
case tp: MethodType =>
498+
if argss.isEmpty then
499+
report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos)
500+
false
501+
else
502+
tp.paramNames.lazyZip(tp.paramInfos).lazyZip(argss.head).foreach { (name, paramtp, arg) =>
503+
paramSpan(name) = arg.span
504+
paramBinding(name) = arg.tpe.dealias match
505+
case _: SingletonType if isIdempotentPath(arg) =>
506+
arg.tpe
507+
case _ =>
508+
paramBindingDef(name, paramSubst(paramtp), arg, bindingsBuf).symbol.termRef
509+
}
510+
computeParamBindings(tp.resultType, targs, argss.tail, paramSubst)
511+
case _ =>
512+
assert(targs.isEmpty)
513+
assert(argss.isEmpty)
514+
true
506515

507516
// Compute val-definitions for all this-proxies and append them to `bindingsBuf`
508517
private def computeThisBindings() = {
@@ -687,7 +696,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
687696
}
688697

689698
// Compute bindings for all parameters, appending them to bindingsBuf
690-
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss) then
699+
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss, identity) then
691700
return call
692701

693702
// make sure prefix is executed if it is impure

tests/neg/i11225.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ class Test:
2525
transparent inline def uni = uninitialized
2626

2727
inline def g(inline x: Int): Unit = ()
28-
def f2 = g(uninitialized) // this one is ok since `uninitialized` is inlined away
28+
def f2 = g(uninitialized) // error
2929

3030
var x7: Int = uni // error

tests/pos/i11864.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import language.experimental.erasedDefinitions
2+
3+
type Ev = { type Out }
4+
type Sub[X] = Ev { type Out = X }
5+
6+
object test1:
7+
transparent inline def foo(ev: Ev): Option[ev.Out] = ???
8+
transparent inline def bar[X](ev: Sub[X]): Option[ev.Out] = ???
9+
def test =
10+
foo(???)
11+
val y = bar[Int](???)
12+
y: Option[Int]
13+
14+
object test2:
15+
inline def foo(ev: Ev): Option[ev.Out] = ???
16+
inline def bar[X](ev: Sub[X]): Option[ev.Out] = ???
17+
def test =
18+
foo(???)
19+
val y = bar[Int](???)
20+
y: Option[Int]
21+
22+
final class CallbackTo[+A] {
23+
inline def map[B](f: A => B)(using erased ev: CallbackTo.MapGuard[B]): CallbackTo[ev.Out] = ???
24+
}
25+
26+
object CallbackTo {
27+
28+
type MapGuard[A] = { type Out = A }
29+
erased given MapGuard[A]: MapGuard[A] = ???
30+
31+
def traverse[A, B](ta: List[A]): CallbackTo[List[B]] =
32+
val x: CallbackTo[List[A] => List[B]] = ???
33+
x.map(_(ta))
34+
}
35+

tests/pos/i8612.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
object Foo1:
2+
def assert1(x: Boolean) = if !x then ???
3+
inline def assert2(x: Boolean) = if !x then ???
4+
inline def assert3(inline x: Boolean) = if !x then ???
5+
6+
assert1(???)
7+
assert2(???)
8+
assert3(???)
9+
10+
object Foo2:
11+
def assert1(x: Boolean) = if !x then ???
12+
transparent inline def assert2(x: Boolean) = if !x then ???
13+
transparent inline def assert3(inline x: Boolean) = if !x then ???
14+
15+
assert1(???)
16+
assert2(???)
17+
assert3(???)

0 commit comments

Comments
 (0)