Skip to content

Commit 75a4589

Browse files
authored
Merge pull request #11917 from dotty-staging/fix-11864
Type ascribe arguments of bottom type to inline functions
2 parents eb7b632 + a562c9b commit 75a4589

File tree

4 files changed

+120
-36
lines changed

4 files changed

+120
-36
lines changed

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

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -446,31 +446,38 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
446446
* inline annotations from their parameters. The generated `def` is appended
447447
* to `bindingsBuf`.
448448
* @param name the name of the parameter
449-
* @param paramtp the type of the parameter
449+
* @param formal the type of the parameter
450450
* @param arg the argument corresponding to the parameter
451451
* @param bindingsBuf the buffer to which the definition should be appended
452452
*/
453-
private def paramBindingDef(name: Name, paramtp: Type, arg0: Tree,
453+
private def paramBindingDef(name: Name, formal: Type, arg0: Tree,
454454
bindingsBuf: mutable.ListBuffer[ValOrDefDef])(using Context): ValOrDefDef = {
455+
val isByName = formal.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 formal
465+
else if isByName then ExprType(argtpe.widen)
466+
else argtpe.widen
467+
var bindingFlags: FlagSet = InlineProxy
468+
if formal.widenExpr.hasAnnotation(defn.InlineParamAnnot) then
469+
bindingFlags |= Inline
470+
if isByName then
471+
bindingFlags |= Method
468472
val boundSym = newSym(InlineBinderName.fresh(name.asTermName), bindingFlags, bindingType).asTerm
469473
val binding = {
470-
val newArg = arg.changeOwner(ctx.owner, boundSym)
471-
if (isByName) DefDef(boundSym, newArg)
474+
var newArg = arg.changeOwner(ctx.owner, boundSym)
475+
if bindingFlags.is(Inline) && argIsBottom then
476+
newArg = Typed(newArg, TypeTree(formal)) // type ascribe RHS to avoid type errors in expansion. See i8612.scala
477+
if isByName then DefDef(boundSym, newArg)
472478
else ValDef(boundSym, newArg)
473479
}.withSpan(boundSym.span)
480+
inlining.println(i"parameter binding: $binding, $argIsBottom")
474481
bindingsBuf += binding
475482
binding
476483
}
@@ -479,30 +486,33 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
479486
* corresponding arguments. `bindingbuf` will be further extended later by
480487
* proxies to this-references. Issue an error if some arguments are missing.
481488
*/
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) =>
489+
private def computeParamBindings(
490+
tp: Type, targs: List[Tree], argss: List[List[Tree]], formalss: List[List[Type]]): Boolean =
491+
tp match
492+
case tp: PolyType =>
493+
tp.paramNames.lazyZip(targs).foreach { (name, arg) =>
495494
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-
}
495+
paramBinding(name) = arg.tpe.stripTypeVar
500496
}
501-
computeParamBindings(tp.resultType, targs, argss.tail)
502-
case _ =>
503-
assert(targs.isEmpty)
504-
assert(argss.isEmpty)
505-
true
497+
computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss, formalss)
498+
case tp: MethodType =>
499+
if argss.isEmpty then
500+
report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos)
501+
false
502+
else
503+
tp.paramNames.lazyZip(formalss.head).lazyZip(argss.head).foreach { (name, formal, arg) =>
504+
paramSpan(name) = arg.span
505+
paramBinding(name) = arg.tpe.dealias match
506+
case _: SingletonType if isIdempotentPath(arg) =>
507+
arg.tpe
508+
case _ =>
509+
paramBindingDef(name, formal, arg, bindingsBuf).symbol.termRef
510+
}
511+
computeParamBindings(tp.resultType, targs, argss.tail, formalss.tail)
512+
case _ =>
513+
assert(targs.isEmpty)
514+
assert(argss.isEmpty)
515+
true
506516

507517
// Compute val-definitions for all this-proxies and append them to `bindingsBuf`
508518
private def computeThisBindings() = {
@@ -686,8 +696,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
686696
)
687697
}
688698

699+
def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match
700+
case Apply(fn, args) =>
701+
fn.tpe.widen.match
702+
case mt: MethodType => paramTypess(fn, mt.instantiateParamInfos(args.tpes) :: acc)
703+
case _ => Nil
704+
case TypeApply(fn, _) => paramTypess(fn, acc)
705+
case _ => acc
706+
689707
// Compute bindings for all parameters, appending them to bindingsBuf
690-
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss) then
708+
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss, paramTypess(call, Nil)) then
691709
return call
692710

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

tests/pos/i11864.scala

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
object test3:
23+
inline def bar(ev: Ev)(x: ev.Out): Option[ev.Out] = Some(x)
24+
val a: Ev { type Out = Int } = ???
25+
def test =
26+
val y = bar(a)(???)
27+
y: Option[Int]
28+
29+
object test4:
30+
inline def bar(ev: Ev, x: ev.Out): Option[ev.Out] = Some(x)
31+
val a: Ev { type Out = Int } = ???
32+
def test =
33+
val y = bar(a, ???)
34+
y: Option[Int]
35+
36+
final class CallbackTo[+A] {
37+
inline def map[B](f: A => B)(using erased ev: CallbackTo.MapGuard[B]): CallbackTo[ev.Out] = ???
38+
}
39+
40+
object CallbackTo {
41+
42+
type MapGuard[A] = { type Out = A }
43+
erased given MapGuard[A]: MapGuard[A] = ???
44+
45+
def traverse[A, B](ta: List[A]): CallbackTo[List[B]] =
46+
val x: CallbackTo[List[A] => List[B]] = ???
47+
x.map(_(ta))
48+
}
49+

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(???)

tests/run-macros/tasty-extractors-2.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
44
Inlined(None, Nil, Block(List(DefDef("$anonfun", List(TermParamClause(List(ValDef("x", TypeIdent("Int"), None)))), Inferred(), Some(Ident("x")))), Closure(Ident("$anonfun"), None)))
55
AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Function1"), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")))
66

7-
Inlined(None, Nil, Ident("???"))
8-
TermRef(TermRef(ThisType(TypeRef(NoPrefix(), "scala")), "Predef"), "???")
7+
Inlined(None, Nil, Typed(Ident("???"), Inferred()))
8+
AnnotatedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Any"), Apply(Select(New(Inferred()), "<init>"), Nil))
99

1010
Inlined(None, Nil, Literal(IntConstant(1)))
1111
ConstantType(IntConstant(1))

0 commit comments

Comments
 (0)