Skip to content

Commit

Permalink
Redefine semantics of inline parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Jan 23, 2020
1 parent 2ecdf91 commit c33f442
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 113 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Expand Up @@ -533,7 +533,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
// but potentially re-introduced by ResolveSuper, when we add
// forwarders to mixin methods.
// See doc comment for ElimByName for speculation how we could improve this.
else MethodType(Nil, Nil, eraseResult(sym.info.finalResultType))
else MethodType(Nil, Nil, eraseResult(sym.info.finalResultType.underlyingIfRepeated(isJava)))
case tp: PolyType =>
eraseResult(tp.resultType) match {
case rt: MethodType => rt
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Expand Up @@ -3360,11 +3360,11 @@ object Types {
/** Produce method type from parameter symbols, with special mappings for repeated
* and inline parameters:
* - replace @repeated annotations on Seq or Array types by <repeated> types
* - add @inlineParam to inline call-by-value parameters
* - add @inlineParam to inline parameters
*/
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context): MethodType = {
def translateInline(tp: Type): Type = tp match {
case _: ExprType => tp
case ExprType(resType) => ExprType(AnnotatedType(resType, Annotation(defn.InlineParamAnnot)))
case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
}
def paramInfo(param: Symbol) = {
Expand Down
39 changes: 2 additions & 37 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Expand Up @@ -818,43 +818,8 @@ trait Checking {
tree.tpe.widenTermRefExpr match {
case tp: ConstantType if exprPurity(tree) >= purityLevel => // ok
case _ =>
tree match {
case Typed(expr, _) =>
checkInlineConformant(expr, isFinal, what)
case Inlined(_, Nil, expr) =>
checkInlineConformant(expr, isFinal, what)
case SeqLiteral(elems, _) =>
elems.foreach(elem => checkInlineConformant(elem, isFinal, what))
case Apply(fn, List(arg)) if defn.WrapArrayMethods().contains(fn.symbol) =>
checkInlineConformant(arg, isFinal, what)
case _ =>
def isCaseClassApply(sym: Symbol): Boolean =
sym.name == nme.apply && sym.is(Synthetic) && sym.owner.is(Module) && sym.owner.companionClass.is(Case)
def isCaseClassNew(sym: Symbol): Boolean =
sym.isPrimaryConstructor && sym.owner.is(Case) && sym.owner.isStatic
def isCaseObject(sym: Symbol): Boolean =
// TODO add alias to Nil in scala package
sym.is(Case) && sym.is(Module)
def isStaticEnumCase(sym: Symbol): Boolean =
sym.is(Enum) && sym.is(JavaStatic) && sym.is(Case)
val allow =
ctx.erasedTypes ||
ctx.inInlineMethod ||
(tree.symbol.isStatic && isCaseObject(tree.symbol) || isCaseClassApply(tree.symbol)) ||
isStaticEnumCase(tree.symbol) ||
isCaseClassNew(tree.symbol)

if (!allow) ctx.error(em"$what must be a known value", tree.sourcePos)
else {
def checkArgs(tree: Tree): Unit = tree match {
case Apply(fn, args) =>
args.foreach(arg => checkInlineConformant(arg, isFinal, what))
checkArgs(fn)
case _ =>
}
checkArgs(tree)
}
}
if (!ctx.erasedTypes && !ctx.inInlineMethod)
ctx.error(em"$what must be a known value", tree.sourcePos)
}
}

Expand Down
22 changes: 11 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Expand Up @@ -223,7 +223,7 @@ object Inliner {
res ++= typerErrors.map(e => ErrorKind.Typer -> e)
res.toList
case t =>
assert(ctx.reporter.hasErrors) // at least: argument to inline parameter must be a known value
ctx.error("expected argument to be a statically known String", codeArg.sourcePos)
Nil
}

Expand Down Expand Up @@ -333,9 +333,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
val argtpe = arg.tpe.dealiasKeepAnnots
val isByName = paramtp.dealias.isInstanceOf[ExprType]
var inlineFlags: FlagSet = InlineProxy
if (paramtp.hasAnnotation(defn.InlineParamAnnot)) inlineFlags |= Inline
if (paramtp.widenExpr.hasAnnotation(defn.InlineParamAnnot)) inlineFlags |= Inline
if (isByName) inlineFlags |= Method
val (bindingFlags, bindingType) =
if (isByName) (InlineByNameProxy.toTermFlags, ExprType(argtpe.widen))
if (isByName) (inlineFlags, ExprType(argtpe.widen))
else (inlineFlags, argtpe.widen)
val boundSym = newSym(name, bindingFlags, bindingType).asTerm
val binding = {
Expand Down Expand Up @@ -765,10 +766,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
def search(buf: mutable.ListBuffer[ValOrDefDef]) = buf.find(_.name == tree.name)
if (paramProxies.contains(tree.typeOpt))
search(bindingsBuf) match {
case Some(vdef: ValDef) if vdef.symbol.is(Inline) =>
Some(integrate(vdef.rhs, vdef.symbol))
case Some(ddef: DefDef) =>
Some(integrate(ddef.rhs, ddef.symbol))
case Some(bind: ValOrDefDef) if bind.symbol.is(Inline) =>
Some(integrate(bind.rhs, bind.symbol))
case _ => None
}
else None
Expand Down Expand Up @@ -1198,7 +1197,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
val bindingOfSym = newMutableSymbolMap[MemberDef]

def isInlineable(binding: MemberDef) = binding match {
case DefDef(_, Nil, Nil, _, _) => true
case ddef @ DefDef(_, Nil, Nil, _, _) => isPureExpr(ddef.rhs)
case vdef @ ValDef(_, _, _) => isPureExpr(vdef.rhs)
case _ => false
}
Expand Down Expand Up @@ -1232,11 +1231,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
for (binding <- bindings) countRefs.traverse(binding)

def retain(boundSym: Symbol) = {
refCount.get(boundSym) match {
!boundSym.is(Inline) &&
(refCount.get(boundSym) match {
case Some(x) => x > 1 || x == 1 && !boundSym.is(Method)
case none => true
}
} && !(boundSym.isAllOf(InlineMethod) && boundSym.isOneOf(GivenOrImplicit))
})
} && !(boundSym.isAllOf(InlineMethod) && boundSym.isOneOf(GivenOrImplicit)) // TODO clenup unnecessary conditions?

val inlineBindings = new TreeMap {
override def transform(t: Tree)(implicit ctx: Context) = t match {
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Expand Up @@ -2896,8 +2896,6 @@ class Typer extends Namer
tree
}
else if (tree.tpe.widenExpr <:< pt) {
if (pt.hasAnnotation(defn.InlineParamAnnot))
checkInlineConformant(tree, isFinal = false, "argument to inline parameter")
if (ctx.typeComparer.GADTused && pt.isValueType)
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
// I suspect, but am not 100% sure that this might affect inferred types,
Expand Down
60 changes: 31 additions & 29 deletions compiler/test/dotty/tools/backend/jvm/ArrayApplyOptTest.scala
Expand Up @@ -8,6 +8,8 @@ import scala.tools.asm.Opcodes._
class ArrayApplyOptTest extends DottyBytecodeTest {
import ASMConverters._

// FIXME: Re-enable IArray bytecode tests (requires updated reference compiler)
// Also change: library/src/scala/IArray.scala
@Test def testArrayEmptyGenericApply= {
test("Array[String]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/String"), Op(POP), Op(RETURN)))
test("Array[Unit]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(POP), Op(RETURN)))
Expand All @@ -22,18 +24,18 @@ class ArrayApplyOptTest extends DottyBytecodeTest {
test("Array[Char]()", newArray0Opcodes(T_CHAR))
test("Array[T]()", newArray0Opcodes(T_INT))

test("IArray[String]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/String"), TypeOp(CHECKCAST, "[Ljava/lang/String;"), Op(POP), Op(RETURN)))
test("IArray[Unit]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN)))
test("IArray[Object]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/Object"), TypeOp(CHECKCAST, "[Ljava/lang/Object;"), Op(POP), Op(RETURN)))
test("IArray[Boolean]()", newArray0Opcodes(T_BOOLEAN, TypeOp(CHECKCAST, "[Z") :: Nil))
test("IArray[Byte]()", newArray0Opcodes(T_BYTE, TypeOp(CHECKCAST, "[B") :: Nil))
test("IArray[Short]()", newArray0Opcodes(T_SHORT, TypeOp(CHECKCAST, "[S") :: Nil))
test("IArray[Int]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil))
test("IArray[Long]()", newArray0Opcodes(T_LONG, TypeOp(CHECKCAST, "[J") :: Nil))
test("IArray[Float]()", newArray0Opcodes(T_FLOAT, TypeOp(CHECKCAST, "[F") :: Nil))
test("IArray[Double]()", newArray0Opcodes(T_DOUBLE, TypeOp(CHECKCAST, "[D") :: Nil))
test("IArray[Char]()", newArray0Opcodes(T_CHAR, TypeOp(CHECKCAST, "[C") :: Nil))
test("IArray[T]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil))
// test("IArray[String]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/String"), TypeOp(CHECKCAST, "[Ljava/lang/String;"), Op(POP), Op(RETURN)))
// test("IArray[Unit]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN)))
// test("IArray[Object]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/Object"), TypeOp(CHECKCAST, "[Ljava/lang/Object;"), Op(POP), Op(RETURN)))
// test("IArray[Boolean]()", newArray0Opcodes(T_BOOLEAN, TypeOp(CHECKCAST, "[Z") :: Nil))
// test("IArray[Byte]()", newArray0Opcodes(T_BYTE, TypeOp(CHECKCAST, "[B") :: Nil))
// test("IArray[Short]()", newArray0Opcodes(T_SHORT, TypeOp(CHECKCAST, "[S") :: Nil))
// test("IArray[Int]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil))
// test("IArray[Long]()", newArray0Opcodes(T_LONG, TypeOp(CHECKCAST, "[J") :: Nil))
// test("IArray[Float]()", newArray0Opcodes(T_FLOAT, TypeOp(CHECKCAST, "[F") :: Nil))
// test("IArray[Double]()", newArray0Opcodes(T_DOUBLE, TypeOp(CHECKCAST, "[D") :: Nil))
// test("IArray[Char]()", newArray0Opcodes(T_CHAR, TypeOp(CHECKCAST, "[C") :: Nil))
// test("IArray[T]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil))
}

@Test def testArrayGenericApply= {
Expand All @@ -44,90 +46,90 @@ class ArrayApplyOptTest extends DottyBytecodeTest {

def opCodes2(tpe: String) =
List(Op(ICONST_2), TypeOp(ANEWARRAY, tpe), Op(DUP), Op(ICONST_0), Ldc(LDC, "a"), Op(AASTORE), Op(DUP), Op(ICONST_1), Ldc(LDC, "b"), Op(AASTORE), TypeOp(CHECKCAST, s"[L$tpe;"), Op(POP), Op(RETURN))
test("""IArray("a", "b")""", opCodes2("java/lang/String"))
test("""IArray[Object]("a", "b")""", opCodes2("java/lang/Object"))
// test("""IArray("a", "b")""", opCodes2("java/lang/String"))
// test("""IArray[Object]("a", "b")""", opCodes2("java/lang/Object"))
}

@Test def testArrayApplyBoolean = {
val init = List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(BASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_0), Op(BASTORE))
test("Array(true, false)", newArray2Opcodes(T_BOOLEAN, init))
test("IArray(true, false)", newArray2Opcodes(T_BOOLEAN, init :+ TypeOp(CHECKCAST, "[Z")))
// test("IArray(true, false)", newArray2Opcodes(T_BOOLEAN, init :+ TypeOp(CHECKCAST, "[Z")))
}

@Test def testArrayApplyByte = {
val init = List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(BASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(BASTORE))
test("Array[Byte](1, 2)", newArray2Opcodes(T_BYTE, init))
test("IArray[Byte](1, 2)", newArray2Opcodes(T_BYTE, init :+ TypeOp(CHECKCAST, "[B")))
// test("IArray[Byte](1, 2)", newArray2Opcodes(T_BYTE, init :+ TypeOp(CHECKCAST, "[B")))
}

@Test def testArrayApplyShort = {
val init = List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(SASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(SASTORE))
test("Array[Short](1, 2)", newArray2Opcodes(T_SHORT, init))
test("IArray[Short](1, 2)", newArray2Opcodes(T_SHORT, init :+ TypeOp(CHECKCAST, "[S")))
// test("IArray[Short](1, 2)", newArray2Opcodes(T_SHORT, init :+ TypeOp(CHECKCAST, "[S")))
}

@Test def testArrayApplyInt = {
val init = List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(IASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(IASTORE))
test("Array(1, 2)", newArray2Opcodes(T_INT, init))
test("IArray(1, 2)", newArray2Opcodes(T_INT, init :+ TypeOp(CHECKCAST, "[I")))
// test("IArray(1, 2)", newArray2Opcodes(T_INT, init :+ TypeOp(CHECKCAST, "[I")))

val init2 = List(Op(DUP), Op(ICONST_0), Field(GETSTATIC, "Foo$", "MODULE$", "LFoo$;"), Invoke(INVOKEVIRTUAL, "Foo$", "t", "()I", false), Op(IASTORE), Op(DUP), Op(ICONST_1), Field(GETSTATIC, "Foo$", "MODULE$", "LFoo$;"), Invoke(INVOKEVIRTUAL, "Foo$", "t", "()I", false), Op(IASTORE))
test("""Array[T](t, t)""", newArray2Opcodes(T_INT, init2))
test("""IArray[T](t, t)""", newArray2Opcodes(T_INT, init2 :+ TypeOp(CHECKCAST, "[I")))
// test("""IArray[T](t, t)""", newArray2Opcodes(T_INT, init2 :+ TypeOp(CHECKCAST, "[I")))
}

@Test def testArrayApplyLong = {
val init = List(Op(DUP), Op(ICONST_0), Ldc(LDC, 2), Op(LASTORE), Op(DUP), Op(ICONST_1), Ldc(LDC, 3), Op(LASTORE))
test("Array(2L, 3L)", newArray2Opcodes(T_LONG, init))
test("IArray(2L, 3L)", newArray2Opcodes(T_LONG, init :+ TypeOp(CHECKCAST, "[J")))
// test("IArray(2L, 3L)", newArray2Opcodes(T_LONG, init :+ TypeOp(CHECKCAST, "[J")))
}

@Test def testArrayApplyFloat = {
val init = List(Op(DUP), Op(ICONST_0), Ldc(LDC, 2.1f), Op(FASTORE), Op(DUP), Op(ICONST_1), Ldc(LDC, 3.1f), Op(FASTORE))
test("Array(2.1f, 3.1f)", newArray2Opcodes(T_FLOAT, init))
test("IArray(2.1f, 3.1f)", newArray2Opcodes(T_FLOAT, init :+ TypeOp(CHECKCAST, "[F")))
// test("IArray(2.1f, 3.1f)", newArray2Opcodes(T_FLOAT, init :+ TypeOp(CHECKCAST, "[F")))
}

@Test def testArrayApplyDouble = {
val init = List(Op(DUP), Op(ICONST_0), Ldc(LDC, 2.2d), Op(DASTORE), Op(DUP), Op(ICONST_1), Ldc(LDC, 3.2d), Op(DASTORE))
test("Array(2.2d, 3.2d)", newArray2Opcodes(T_DOUBLE, init))
test("IArray(2.2d, 3.2d)", newArray2Opcodes(T_DOUBLE, init :+ TypeOp(CHECKCAST, "[D")))
// test("IArray(2.2d, 3.2d)", newArray2Opcodes(T_DOUBLE, init :+ TypeOp(CHECKCAST, "[D")))
}

@Test def testArrayApplyChar = {
val init = List(Op(DUP), Op(ICONST_0), IntOp(BIPUSH, 120), Op(CASTORE), Op(DUP), Op(ICONST_1), IntOp(BIPUSH, 121), Op(CASTORE))
test("Array('x', 'y')", newArray2Opcodes(T_CHAR, init))
test("IArray('x', 'y')", newArray2Opcodes(T_CHAR, init :+ TypeOp(CHECKCAST, "[C")))
// test("IArray('x', 'y')", newArray2Opcodes(T_CHAR, init :+ TypeOp(CHECKCAST, "[C")))
}

@Test def testArrayApplyUnit = {
test("Array[Unit]((), ())", List(Op(ICONST_2), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(DUP),
Op(ICONST_0), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(DUP),
Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(POP), Op(RETURN)))
test("IArray[Unit]((), ())", List(Op(ICONST_2), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(DUP),
Op(ICONST_0), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(DUP),
Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN)))
// test("IArray[Unit]((), ())", List(Op(ICONST_2), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(DUP),
// Op(ICONST_0), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(DUP),
// Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN)))
}

@Test def testArrayInlined = test(
"""{
| inline def array(xs: =>Int*): Array[Int] = Array(xs: _*)
| inline def array(inline xs: Int*): Array[Int] = Array(xs: _*)
| array(1, 2)
|}""".stripMargin,
newArray2Opcodes(T_INT, List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(IASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(IASTORE), TypeOp(CHECKCAST, "[I")))
)

@Test def testArrayInlined2 = test(
"""{
| inline def array(x: =>Int, xs: =>Int*): Array[Int] = Array(x, xs: _*)
| inline def array(inline x: Int, inline xs: Int*): Array[Int] = Array(x, xs: _*)
| array(1, 2)
|}""".stripMargin,
newArray2Opcodes(T_INT, List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(IASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(IASTORE)))
)

@Test def testArrayInlined3 = test(
"""{
| inline def array[T](xs: =>T*)(given ct: =>scala.reflect.ClassTag[T]): Array[T] = Array(xs: _*)
| inline def array[T](inline xs: T*)(given inline ct: scala.reflect.ClassTag[T]): Array[T] = Array(xs: _*)
| array(1, 2)
|}""".stripMargin,
newArray2Opcodes(T_INT, List(Op(DUP), Op(ICONST_0), Op(ICONST_1), Op(IASTORE), Op(DUP), Op(ICONST_1), Op(ICONST_2), Op(IASTORE), TypeOp(CHECKCAST, "[I")))
Expand Down
14 changes: 7 additions & 7 deletions compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala
Expand Up @@ -44,7 +44,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947 = {
val source = """class Foo {
| inline def track[T](f: => T) <: T = {
| inline def track[T](inline f: T) <: T = {
| foo("tracking") // line 3
| f // line 4
| }
Expand Down Expand Up @@ -103,11 +103,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947b = {
val source = """class Foo {
| inline def track2[T](f: => T) <: T = {
| inline def track2[T](inline f: T) <: T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](f: => T) <: T = {
| inline def track[T](inline f: T) <: T = {
| foo("tracking") // line 7
| track2 { // line 8
| f // line 9
Expand Down Expand Up @@ -163,11 +163,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947c = {
val source = """class Foo {
| inline def track2[T](f: => T) <: T = {
| inline def track2[T](inline f: T) <: T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](f: => T) <: T = {
| inline def track[T](inline f: T) <: T = {
| track2 { // line 7
| foo("fgh") // line 8
| f // line 9
Expand Down Expand Up @@ -223,11 +223,11 @@ class InlineBytecodeTests extends DottyBytecodeTest {

@Test def i4947d = {
val source = """class Foo {
| inline def track2[T](f: => T) <: T = {
| inline def track2[T](inline f: T) <: T = {
| foo("tracking2") // line 3
| f // line 4
| }
| inline def track[T](f: => T) <: T = {
| inline def track[T](inline f: T) <: T = {
| track2 { // line 7
| track2 { // line 8
| f // line 9
Expand Down
2 changes: 1 addition & 1 deletion library/src/dotty/DottyPredef.scala
Expand Up @@ -8,7 +8,7 @@ object DottyPredef {
assertFail(message)
}

inline final def assert(assertion: => Boolean) <: Unit = {
inline final def assert(inline assertion: => Boolean) <: Unit = {
if (!assertion)
assertFail()
}
Expand Down

0 comments on commit c33f442

Please sign in to comment.