Skip to content

Type ascribe arguments of bottom type to inline functions #11917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 28, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 44 additions & 32 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -452,25 +452,32 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
*/
private def paramBindingDef(name: Name, paramtp: Type, arg0: Tree,
bindingsBuf: mutable.ListBuffer[ValOrDefDef])(using Context): ValOrDefDef = {
val isByName = paramtp.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 paramtp
else if isByName then ExprType(argtpe.widen)
else argtpe.widen
var bindingFlags: FlagSet = InlineProxy
if paramtp.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(paramtp)) // 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
}
Expand All @@ -479,30 +486,35 @@ 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]], paramSubst: Type => 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,
paramSubst.andThen(_.substParams(tp, targs.map(_.tpe.stripTypeVar))))
Copy link
Contributor

@liufengyun liufengyun Mar 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the usage of tp will disregard the prefix -- maybe methPart.tpe.widen is more accurate? Meanwhile, the current fix does not handle dependent types. Will it be simpler to extract the types from the fun part of Apply trees instead of recomputing them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation. Yes, that should be fixed.

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) =>
paramSpan(name) = arg.span
paramBinding(name) = arg.tpe.dealias match
case _: SingletonType if isIdempotentPath(arg) =>
arg.tpe
case _ =>
paramBindingDef(name, paramSubst(paramtp), arg, bindingsBuf).symbol.termRef
}
computeParamBindings(tp.resultType, targs, argss.tail, paramSubst)
case _ =>
assert(targs.isEmpty)
assert(argss.isEmpty)
true

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

// Compute bindings for all parameters, appending them to bindingsBuf
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss) then
if !computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss, identity) then
return call

// make sure prefix is executed if it is impure
Expand Down
35 changes: 35 additions & 0 deletions tests/pos/i11864.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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]

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))
}

17 changes: 17 additions & 0 deletions tests/pos/i8612.scala
Original file line number Diff line number Diff line change
@@ -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(???)
4 changes: 2 additions & 2 deletions tests/run-macros/tasty-extractors-2.check
Original file line number Diff line number Diff line change
Expand Up @@ -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(), "<root>")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "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(), "<root>")), "scala"), "Any"), Apply(Select(New(Inferred()), "<init>"), Nil))

Inlined(None, Nil, Literal(IntConstant(1)))
ConstantType(IntConstant(1))
Expand Down