diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 08ff58e918a2..ed8467f1ebb1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -446,31 +446,38 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { * inline annotations from their parameters. The generated `def` is appended * to `bindingsBuf`. * @param name the name of the parameter - * @param paramtp the type of the parameter + * @param formal the type of the parameter * @param arg the argument corresponding to the parameter * @param bindingsBuf the buffer to which the definition should be appended */ - private def paramBindingDef(name: Name, paramtp: Type, arg0: Tree, + private def paramBindingDef(name: Name, formal: Type, arg0: Tree, bindingsBuf: mutable.ListBuffer[ValOrDefDef])(using Context): ValOrDefDef = { + val isByName = formal.dealias.isInstanceOf[ExprType] val arg = arg0 match { case Typed(arg1, tpt) if tpt.tpe.isRepeatedParam && arg1.tpe.derivesFrom(defn.ArrayClass) => wrapArray(arg1, arg0.tpe.elemType) case _ => arg0 } val argtpe = arg.tpe.dealiasKeepAnnots.translateFromRepeated(toArray = false) - val isByName = paramtp.dealias.isInstanceOf[ExprType] - var inlineFlags: FlagSet = InlineProxy - if (paramtp.widenExpr.hasAnnotation(defn.InlineParamAnnot)) inlineFlags |= Inline - if (isByName) inlineFlags |= Method - val (bindingFlags, bindingType) = - if (isByName) (inlineFlags, ExprType(argtpe.widen)) - else (inlineFlags, argtpe.widen) + val argIsBottom = argtpe.isBottomTypeAfterErasure + val bindingType = + if argIsBottom then formal + else if isByName then ExprType(argtpe.widen) + else argtpe.widen + var bindingFlags: FlagSet = InlineProxy + if formal.widenExpr.hasAnnotation(defn.InlineParamAnnot) then + bindingFlags |= Inline + if isByName then + bindingFlags |= Method val boundSym = newSym(InlineBinderName.fresh(name.asTermName), bindingFlags, bindingType).asTerm val binding = { - val newArg = arg.changeOwner(ctx.owner, boundSym) - if (isByName) DefDef(boundSym, newArg) + var newArg = arg.changeOwner(ctx.owner, boundSym) + if bindingFlags.is(Inline) && argIsBottom then + newArg = Typed(newArg, TypeTree(formal)) // type ascribe RHS to avoid type errors in expansion. See i8612.scala + if isByName then DefDef(boundSym, newArg) else ValDef(boundSym, newArg) }.withSpan(boundSym.span) + inlining.println(i"parameter binding: $binding, $argIsBottom") bindingsBuf += binding binding } @@ -479,30 +486,33 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { * corresponding arguments. `bindingbuf` will be further extended later by * proxies to this-references. Issue an error if some arguments are missing. */ - private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Boolean = tp match - case tp: PolyType => - tp.paramNames.lazyZip(targs).foreach { (name, arg) => - paramSpan(name) = arg.span - paramBinding(name) = arg.tpe.stripTypeVar - } - computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss) - case tp: MethodType => - if argss.isEmpty then - report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos) - false - else - tp.paramNames.lazyZip(tp.paramInfos).lazyZip(argss.head).foreach { (name, paramtp, arg) => + private def computeParamBindings( + tp: Type, targs: List[Tree], argss: List[List[Tree]], formalss: List[List[Type]]): Boolean = + tp match + case tp: PolyType => + tp.paramNames.lazyZip(targs).foreach { (name, arg) => paramSpan(name) = arg.span - paramBinding(name) = arg.tpe.dealias match { - case _: SingletonType if isIdempotentPath(arg) => arg.tpe - case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol.termRef - } + paramBinding(name) = arg.tpe.stripTypeVar } - computeParamBindings(tp.resultType, targs, argss.tail) - case _ => - assert(targs.isEmpty) - assert(argss.isEmpty) - true + computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss, formalss) + case tp: MethodType => + if argss.isEmpty then + report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos) + false + else + tp.paramNames.lazyZip(formalss.head).lazyZip(argss.head).foreach { (name, formal, arg) => + paramSpan(name) = arg.span + paramBinding(name) = arg.tpe.dealias match + case _: SingletonType if isIdempotentPath(arg) => + arg.tpe + case _ => + paramBindingDef(name, formal, arg, bindingsBuf).symbol.termRef + } + computeParamBindings(tp.resultType, targs, argss.tail, formalss.tail) + case _ => + assert(targs.isEmpty) + assert(argss.isEmpty) + true // Compute val-definitions for all this-proxies and append them to `bindingsBuf` private def computeThisBindings() = { @@ -686,8 +696,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { ) } + def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match + case Apply(fn, args) => + fn.tpe.widen.match + case mt: MethodType => paramTypess(fn, mt.instantiateParamInfos(args.tpes) :: acc) + case _ => Nil + case TypeApply(fn, _) => paramTypess(fn, acc) + case _ => acc + // Compute bindings for all parameters, appending them to bindingsBuf - if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss) then + if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss, paramTypess(call, Nil)) then return call // make sure prefix is executed if it is impure diff --git a/tests/pos/i11864.scala b/tests/pos/i11864.scala new file mode 100644 index 000000000000..da7140e57b8d --- /dev/null +++ b/tests/pos/i11864.scala @@ -0,0 +1,49 @@ +import language.experimental.erasedDefinitions + +type Ev = { type Out } +type Sub[X] = Ev { type Out = X } + +object test1: + transparent inline def foo(ev: Ev): Option[ev.Out] = ??? + transparent inline def bar[X](ev: Sub[X]): Option[ev.Out] = ??? + def test = + foo(???) + val y = bar[Int](???) + y: Option[Int] + +object test2: + inline def foo(ev: Ev): Option[ev.Out] = ??? + inline def bar[X](ev: Sub[X]): Option[ev.Out] = ??? + def test = + foo(???) + val y = bar[Int](???) + y: Option[Int] + +object test3: + inline def bar(ev: Ev)(x: ev.Out): Option[ev.Out] = Some(x) + val a: Ev { type Out = Int } = ??? + def test = + val y = bar(a)(???) + y: Option[Int] + +object test4: + inline def bar(ev: Ev, x: ev.Out): Option[ev.Out] = Some(x) + val a: Ev { type Out = Int } = ??? + def test = + val y = bar(a, ???) + y: Option[Int] + +final class CallbackTo[+A] { + inline def map[B](f: A => B)(using erased ev: CallbackTo.MapGuard[B]): CallbackTo[ev.Out] = ??? +} + +object CallbackTo { + + type MapGuard[A] = { type Out = A } + erased given MapGuard[A]: MapGuard[A] = ??? + + def traverse[A, B](ta: List[A]): CallbackTo[List[B]] = + val x: CallbackTo[List[A] => List[B]] = ??? + x.map(_(ta)) +} + diff --git a/tests/pos/i8612.scala b/tests/pos/i8612.scala new file mode 100644 index 000000000000..294f7b58114b --- /dev/null +++ b/tests/pos/i8612.scala @@ -0,0 +1,17 @@ +object Foo1: + def assert1(x: Boolean) = if !x then ??? + inline def assert2(x: Boolean) = if !x then ??? + inline def assert3(inline x: Boolean) = if !x then ??? + + assert1(???) + assert2(???) + assert3(???) + +object Foo2: + def assert1(x: Boolean) = if !x then ??? + transparent inline def assert2(x: Boolean) = if !x then ??? + transparent inline def assert3(inline x: Boolean) = if !x then ??? + + assert1(???) + assert2(???) + assert3(???) diff --git a/tests/run-macros/tasty-extractors-2.check b/tests/run-macros/tasty-extractors-2.check index d7572ef7f5b2..1223a1fb71d9 100644 --- a/tests/run-macros/tasty-extractors-2.check +++ b/tests/run-macros/tasty-extractors-2.check @@ -4,8 +4,8 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(DefDef("$anonfun", List(TermParamClause(List(ValDef("x", TypeIdent("Int"), None)))), Inferred(), Some(Ident("x")))), Closure(Ident("$anonfun"), None))) AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Function1"), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int"))) -Inlined(None, Nil, Ident("???")) -TermRef(TermRef(ThisType(TypeRef(NoPrefix(), "scala")), "Predef"), "???") +Inlined(None, Nil, Typed(Ident("???"), Inferred())) +AnnotatedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Any"), Apply(Select(New(Inferred()), ""), Nil)) Inlined(None, Nil, Literal(IntConstant(1))) ConstantType(IntConstant(1))